From e2a4fb11d8e3d9146e0cbc711ddf1d4e43856b37 Mon Sep 17 00:00:00 2001 From: zhugengyu Date: Wed, 21 Sep 2022 08:57:41 +0800 Subject: [PATCH] update --- common/usb_mem.h | 16 + core/usbh_core.c | 38 + demo/phytium-e2000/usbhost/Kconfig | 19 + demo/phytium-e2000/usbhost/README.md | 3 + demo/phytium-e2000/usbhost/inc/usb_host.h | 50 + demo/phytium-e2000/usbhost/main.c | 43 + demo/phytium-e2000/usbhost/makefile | 29 + demo/phytium-e2000/usbhost/sdkconfig | 223 ++++ demo/phytium-e2000/usbhost/sdkconfig.h | 200 ++++ demo/phytium-e2000/usbhost/sdkconfig.old | 225 ++++ demo/phytium-e2000/usbhost/src/cmd_usb.c | 55 + demo/phytium-e2000/usbhost/src/usb_host.c | 149 +++ port/xhci/usb_config.h | 143 +++ port/xhci/usb_hc_xhci.c | 1218 +++++++++++++++++++++ port/xhci/usb_hc_xhci.h | 175 +++ port/xhci/xhci_reg.h | 528 +++++++++ 16 files changed, 3114 insertions(+) create mode 100644 demo/phytium-e2000/usbhost/Kconfig create mode 100644 demo/phytium-e2000/usbhost/README.md create mode 100644 demo/phytium-e2000/usbhost/inc/usb_host.h create mode 100644 demo/phytium-e2000/usbhost/main.c create mode 100644 demo/phytium-e2000/usbhost/makefile create mode 100644 demo/phytium-e2000/usbhost/sdkconfig create mode 100644 demo/phytium-e2000/usbhost/sdkconfig.h create mode 100644 demo/phytium-e2000/usbhost/sdkconfig.old create mode 100644 demo/phytium-e2000/usbhost/src/cmd_usb.c create mode 100644 demo/phytium-e2000/usbhost/src/usb_host.c create mode 100644 port/xhci/usb_config.h create mode 100644 port/xhci/usb_hc_xhci.c create mode 100644 port/xhci/usb_hc_xhci.h create mode 100644 port/xhci/xhci_reg.h diff --git a/common/usb_mem.h b/common/usb_mem.h index 3e9180cc..3377ba62 100644 --- a/common/usb_mem.h +++ b/common/usb_mem.h @@ -6,9 +6,25 @@ #ifndef USB_MEM_H #define USB_MEM_H +#include "usb_config.h" + +#ifdef CONFIG_USBHOST_XHCI + +void *usb_hc_malloc(size_t size); +void usb_hc_free(); +void *usb_hc_malloc_align(size_t align, size_t size); + +#define usb_malloc(size) usb_hc_malloc(size) +#define usb_free(ptr) usb_hc_free(ptr) +#define usb_align(align, size) usb_hc_malloc_align(align, size) + +#else + #define usb_malloc(size) malloc(size) #define usb_free(ptr) free(ptr) +#endif + #ifndef CONFIG_USB_ALIGN_SIZE #define CONFIG_USB_ALIGN_SIZE 4 #endif diff --git a/core/usbh_core.c b/core/usbh_core.c index ea36721e..7feaed03 100644 --- a/core/usbh_core.c +++ b/core/usbh_core.c @@ -408,6 +408,42 @@ int usbh_enumerate(struct usbh_hubport *hport) descsize = 8; } +#ifdef CONFIG_USBHOST_XHCI + + extern int usbh_get_xhci_devaddr(usbh_pipe_t * pipe); + + /* Read the first 8 bytes of the device descriptor */ + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; + setup->wValue = (uint16_t)((USB_DESCRIPTOR_TYPE_DEVICE << 8) | 0); + setup->wIndex = 0; + setup->wLength = descsize; + + ret = usbh_control_transfer(hport->ep0, setup, ep0_request_buffer); + if (ret < 0) { + USB_LOG_ERR("Failed to get device descriptor,errorcode:%d\r\n", ret); + goto errout; + } + + parse_device_descriptor(hport, (struct usb_device_descriptor *)ep0_request_buffer, descsize); + + /* Extract the correct max packetsize from the device descriptor */ + ep_mps = ((struct usb_device_descriptor *)ep0_request_buffer)->bMaxPacketSize0; + + /* And reconfigure EP0 with the correct maximum packet size */ + usbh_ep0_pipe_reconfigure(hport->ep0, 0, ep_mps, hport->speed); + + dev_addr = usbh_get_xhci_devaddr(hport->ep0); + if (dev_addr < 0) { + USB_LOG_ERR("Failed to allocate devaddr,errorcode:%d\r\n", ret); + goto errout; + } + + /* Assign the function address to the port */ + hport->dev_addr = dev_addr; + +#else + /* Configure EP0 with the initial maximum packet size */ usbh_ep0_pipe_reconfigure(hport->ep0, 0, ep_mps, hport->speed); @@ -461,6 +497,8 @@ int usbh_enumerate(struct usbh_hubport *hport) /* And reconfigure EP0 with the correct address */ usbh_ep0_pipe_reconfigure(hport->ep0, dev_addr, ep_mps, hport->speed); +#endif + /* Read the full device descriptor */ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; diff --git a/demo/phytium-e2000/usbhost/Kconfig b/demo/phytium-e2000/usbhost/Kconfig new file mode 100644 index 00000000..a4938ffb --- /dev/null +++ b/demo/phytium-e2000/usbhost/Kconfig @@ -0,0 +1,19 @@ +# +# For a description of the syntax of this configuration file, +# see tools/kconfiglib/kconfig-language.txt. +# + + +mainmenu "Phytium Freertos Configuration" + +menu "Freertos Configuration" + config TARGET_NAME + string "Build Target Name" + default "freertos" + help + Build Target name for the demo + +endmenu + +source "$(FREERTOS_SDK_ROOT)/Kconfig" + diff --git a/demo/phytium-e2000/usbhost/README.md b/demo/phytium-e2000/usbhost/README.md new file mode 100644 index 00000000..bbbc6a1f --- /dev/null +++ b/demo/phytium-e2000/usbhost/README.md @@ -0,0 +1,3 @@ +# how to use +1. install [Phytium FreeRTOS SDK](https://gitee.com/phytium_embedded/phytium-free-rtos-sdk) +2. make and flash image to Phytium-E2000-Dev Board \ No newline at end of file diff --git a/demo/phytium-e2000/usbhost/inc/usb_host.h b/demo/phytium-e2000/usbhost/inc/usb_host.h new file mode 100644 index 00000000..11aaab8e --- /dev/null +++ b/demo/phytium-e2000/usbhost/inc/usb_host.h @@ -0,0 +1,50 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: usb_host.h + * Date: 2022-07-19 09:26:25 + * LastEditTime: 2022-07-19 09:26:25 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#ifndef EXAMPLE_USB_HOST_H +#define EXAMPLE_USB_HOST_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ + +/************************** Constant Definitions *****************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ +BaseType_t FFreeRTOSInitUsb(void); +BaseType_t FFreeRTOSWriteReadUsbDisk(void); +BaseType_t FFreeRTOSRecvHidInput(void); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/demo/phytium-e2000/usbhost/main.c b/demo/phytium-e2000/usbhost/main.c new file mode 100644 index 00000000..8fba640d --- /dev/null +++ b/demo/phytium-e2000/usbhost/main.c @@ -0,0 +1,43 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: main.c + * Date: 2022-06-17 08:17:59 + * LastEditTime: 2022-06-17 08:17:59 + * Description: This file is for c main entry + * + * Modify History: + * Ver Who Date Changes + * ----- ------ -------- -------------------------------------- + * 1.0 zhugengyu 2022/9/20 init commit + */ + +#include "shell.h" +#include "shell_port.h" +#include + +int main(void) +{ + BaseType_t ret; + + ret = LSUserShellTask(); + if(ret != pdPASS) + goto FAIL_EXIT; + + vTaskStartScheduler(); /* 启动任务,开启调度 */ + while (1); /* 正常不会执行到这里 */ + +FAIL_EXIT: + printf("failed 0x%x \r\n", ret); + return 0; +} diff --git a/demo/phytium-e2000/usbhost/makefile b/demo/phytium-e2000/usbhost/makefile new file mode 100644 index 00000000..d5622739 --- /dev/null +++ b/demo/phytium-e2000/usbhost/makefile @@ -0,0 +1,29 @@ +export PROJECT_DIR ?= $(shell pwd) + +export USR_SRC_DIR = $(PROJECT_DIR) \ + ./src +export USR_INC_DIR = $(PROJECT_DIR) \ + ./inc + +# 用户定义的编译目标文件上传路径 +ifeq ($(OS),Windows_NT) + USR_BOOT_DIR ?= $(subst \,/, $(PHYTIUM_DEV_PATH))/tftp +else + USR_BOOT_DIR ?= /mnt/d/tftboot +endif + + +# 设置启动镜像名 +BOOT_IMG_NAME ?= freertos + +# 指定编译freertos项目使用的makefile +include $(FREERTOS_SDK_ROOT)/make/build_freertos.mk + +# 完成编译 +boot: + make -j + @cp ./$(CONFIG_TARGET_NAME).elf $(USR_BOOT_DIR)/$(BOOT_IMG_NAME).elf + @cp ./$(CONFIG_TARGET_NAME).bin $(USR_BOOT_DIR)/$(BOOT_IMG_NAME).bin + @ls -l $(USR_BOOT_DIR)/$(BOOT_IMG_NAME).* + + diff --git a/demo/phytium-e2000/usbhost/sdkconfig b/demo/phytium-e2000/usbhost/sdkconfig new file mode 100644 index 00000000..51d47a84 --- /dev/null +++ b/demo/phytium-e2000/usbhost/sdkconfig @@ -0,0 +1,223 @@ + +# +# Freertos Configuration +# +CONFIG_TARGET_NAME="e2000d_freertos_a64" +# end of Freertos Configuration + +# +# Standalone Setting +# +CONFIG_USE_FREERTOS=y + +# +# Arch Configuration +# +# CONFIG_TARGET_ARMV8_AARCH32 is not set +CONFIG_TARGET_ARMV8_AARCH64=y +CONFIG_USE_CACHE=y +CONFIG_USE_MMU=y +CONFIG_USE_SYS_TICK=y +# CONFIG_MMU_DEBUG_PRINTS is not set +# end of Arch Configuration + +# +# Board Configuration +# +# CONFIG_TARGET_F2000_4 is not set +# CONFIG_TARGET_D2000 is not set +# CONFIG_TARGET_E2000Q is not set +CONFIG_TARGET_E2000D=y +# CONFIG_TARGET_E2000S is not set +CONFIG_TARGET_E2000=y +CONFIG_DEFAULT_DEBUG_PRINT_UART1=y +# CONFIG_DEFAULT_DEBUG_PRINT_UART0 is not set +# CONFIG_DEFAULT_DEBUG_PRINT_UART2 is not set +# end of Board Configuration + +# +# Components Configuration +# +CONFIG_USE_SPI=y +CONFIG_USE_FSPIM=y +# CONFIG_USE_QSPI is not set +CONFIG_USE_GIC=y +CONFIG_ENABLE_GICV3=y +CONFIG_USE_SERIAL=y + +# +# Usart Configuration +# +CONFIG_ENABLE_Pl011_UART=y +# end of Usart Configuration + +# CONFIG_USE_GPIO is not set +# CONFIG_USE_ETH is not set +# CONFIG_USE_CAN is not set +# CONFIG_USE_I2C is not set +# CONFIG_USE_TIMER is not set +# CONFIG_USE_MIO is not set +# CONFIG_USE_SDMMC is not set +# CONFIG_USE_PCIE is not set +# CONFIG_USE_WDT is not set +CONFIG_USE_DMA=y +# CONFIG_ENABLE_FGDMA is not set +CONFIG_ENABLE_FDDMA=y +# CONFIG_USE_NAND is not set +# CONFIG_USE_RTC is not set +# CONFIG_USE_SATA is not set +# CONFIG_USE_USB is not set +# CONFIG_USE_ADC is not set +# CONFIG_USE_PWM is not set +# CONFIG_USE_IPC is not set +# end of Components Configuration + +CONFIG_USE_NEW_LIBC=y +# end of Standalone Setting + +# +# Building Option +# +# CONFIG_LOG_VERBOS is not set +CONFIG_LOG_DEBUG=y +# CONFIG_LOG_INFO is not set +# CONFIG_LOG_WARN is not set +# CONFIG_LOG_ERROR is not set +# CONFIG_LOG_NONE is not set +CONFIG_USE_DEFAULT_INTERRUPT_CONFIG=y +CONFIG_INTERRUPT_ROLE_MASTER=y +# CONFIG_INTERRUPT_ROLE_SLAVE is not set +CONFIG_LOG_EXTRA_INFO=y +# CONFIG_BOOTUP_DEBUG_PRINTS is not set + +# +# Linker Options +# +# CONFIG_AARCH32_RAM_LD is not set +CONFIG_AARCH64_RAM_LD=y +# CONFIG_USER_DEFINED_LD is not set +CONFIG_LINK_SCRIPT_ROM=y +CONFIG_ROM_START_UP_ADDR=0x80100000 +CONFIG_ROM_SIZE_MB=1 +CONFIG_LINK_SCRIPT_RAM=y +CONFIG_RAM_START_UP_ADDR=0x81000000 +CONFIG_RAM_SIZE_MB=64 +CONFIG_HEAP_SIZE=1 +CONFIG_STACK_SIZE=0x100000 +CONFIG_FPU_STACK_SIZE=0x1000 +# end of Linker Options + +# +# Compiler Options +# +CONFIG_OUTPUT_BINARY=y +# end of Compiler Options +# end of Building Option + +# +# Component Configuration +# + +# +# Freertos Uart Drivers +# +CONFIG_FREERTOS_USE_UART=y +# end of Freertos Uart Drivers + +# +# Freertos Pwm Drivers +# +# CONFIG_FREERTOS_USE_PWM is not set +# end of Freertos Pwm Drivers + +# +# Freertos Qspi Drivers +# +# CONFIG_FREERTOS_USE_QSPI is not set +# end of Freertos Qspi Drivers + +# +# Freertos Wdt Drivers +# +# CONFIG_FREERTOS_USE_WDT is not set +# end of Freertos Wdt Drivers + +# +# Freertos Eth Drivers +# +# CONFIG_FREERTOS_USE_XMAC is not set +# end of Freertos Eth Drivers + +# +# Freertos Gpio Drivers +# +# CONFIG_FREERTOS_USE_GPIO is not set +# end of Freertos Gpio Drivers + +# +# Freertos Spim Drivers +# +CONFIG_FREERTOS_USE_FSPIM=y +# end of Freertos Spim Drivers + +# +# Freertos DMA Drivers +# +CONFIG_FREERTOS_USE_FDDMA=y +# CONFIG_FREERTOS_USE_FGDMA is not set +# end of Freertos DMA Drivers + +# +# Freertos MMC Drivers +# +# CONFIG_FREERTOS_USE_FSDIO is not set +# end of Freertos MMC Drivers + +# +# Freertos Adc Drivers +# +# CONFIG_FREERTOS_USE_ADC is not set +# end of Freertos Adc Drivers +# end of Component Configuration + +# +# FreeRTOS Setting +# +# CONFIG_USE_LWIP is not set +CONFIG_USE_BACKTRACE=y +# CONFIG_USE_FATFS is not set +# CONFIG_USE_SFUD is not set +# CONFIG_USE_SPIFFS is not set +# CONFIG_USE_AMP is not set +CONFIG_USE_LETTER_SHELL=y + +# +# Letter Shell Configuration +# +CONFIG_LS_PL011_UART=y +CONFIG_DEFAULT_LETTER_SHELL_USE_UART1=y +# CONFIG_DEFAULT_LETTER_SHELL_USE_UART0 is not set +# CONFIG_DEFAULT_LETTER_SHELL_USE_UART2 is not set +# end of Letter Shell Configuration + +CONFIG_USE_TLSF=y +# CONFIG_USE_SDMMC_CMD is not set +CONFIG_USE_CHERRY_USB=y + +# +# CherryUSB Configuration +# +CONFIG_CHERRY_USB_PORT_XHCI=y +# CONFIG_CHERRY_USB_PORT_PHYTIUM_OTG is not set +CONFIG_CHERRYUSB_HOST=y +# CONFIG_CHERRYUSB_DEVICE is not set +CONFIG_CHERRY_USB_HOST_HUB=y +CONFIG_CHERRY_USB_HOST_MSC=y +CONFIG_CHERRY_USB_HOST_HID=y +# CONFIG_CHERRY_USB_HOST_MTP is not set +# CONFIG_CHERRY_USB_HOST_CDC is not set +# CONFIG_CHERRY_USB_HOST_PRINTER is not set +# CONFIG_CHERRY_USB_HOST_AXU_NET is not set +# CONFIG_CHERRY_USB_HOST_RNDIS_WIRELESS is not set +# end of CherryUSB Configuration +# end of FreeRTOS Setting diff --git a/demo/phytium-e2000/usbhost/sdkconfig.h b/demo/phytium-e2000/usbhost/sdkconfig.h new file mode 100644 index 00000000..6ed02741 --- /dev/null +++ b/demo/phytium-e2000/usbhost/sdkconfig.h @@ -0,0 +1,200 @@ +#ifndef SDK_CONFIG_H__ +#define SDK_CONFIG_H__ + +/* Freertos Configuration */ + +#define CONFIG_TARGET_NAME "e2000d_freertos_a64" +/* end of Freertos Configuration */ + +/* Standalone Setting */ + +#define CONFIG_USE_FREERTOS + +/* Arch Configuration */ + +/* CONFIG_TARGET_ARMV8_AARCH32 is not set */ +#define CONFIG_TARGET_ARMV8_AARCH64 +#define CONFIG_USE_CACHE +#define CONFIG_USE_MMU +#define CONFIG_USE_SYS_TICK +/* CONFIG_MMU_DEBUG_PRINTS is not set */ +/* end of Arch Configuration */ + +/* Board Configuration */ + +/* CONFIG_TARGET_F2000_4 is not set */ +/* CONFIG_TARGET_D2000 is not set */ +/* CONFIG_TARGET_E2000Q is not set */ +#define CONFIG_TARGET_E2000D +/* CONFIG_TARGET_E2000S is not set */ +#define CONFIG_TARGET_E2000 +#define CONFIG_DEFAULT_DEBUG_PRINT_UART1 +/* CONFIG_DEFAULT_DEBUG_PRINT_UART0 is not set */ +/* CONFIG_DEFAULT_DEBUG_PRINT_UART2 is not set */ +/* end of Board Configuration */ + +/* Components Configuration */ + +#define CONFIG_USE_SPI +#define CONFIG_USE_FSPIM +/* CONFIG_USE_QSPI is not set */ +#define CONFIG_USE_GIC +#define CONFIG_ENABLE_GICV3 +#define CONFIG_USE_SERIAL + +/* Usart Configuration */ + +#define CONFIG_ENABLE_Pl011_UART +/* end of Usart Configuration */ +/* CONFIG_USE_GPIO is not set */ +/* CONFIG_USE_ETH is not set */ +/* CONFIG_USE_CAN is not set */ +/* CONFIG_USE_I2C is not set */ +/* CONFIG_USE_TIMER is not set */ +/* CONFIG_USE_MIO is not set */ +/* CONFIG_USE_SDMMC is not set */ +/* CONFIG_USE_PCIE is not set */ +/* CONFIG_USE_WDT is not set */ +#define CONFIG_USE_DMA +/* CONFIG_ENABLE_FGDMA is not set */ +#define CONFIG_ENABLE_FDDMA +/* CONFIG_USE_NAND is not set */ +/* CONFIG_USE_RTC is not set */ +/* CONFIG_USE_SATA is not set */ +/* CONFIG_USE_USB is not set */ +/* CONFIG_USE_ADC is not set */ +/* CONFIG_USE_PWM is not set */ +/* CONFIG_USE_IPC is not set */ +/* end of Components Configuration */ +#define CONFIG_USE_NEW_LIBC +/* end of Standalone Setting */ + +/* Building Option */ + +/* CONFIG_LOG_VERBOS is not set */ +#define CONFIG_LOG_DEBUG +/* CONFIG_LOG_INFO is not set */ +/* CONFIG_LOG_WARN is not set */ +/* CONFIG_LOG_ERROR is not set */ +/* CONFIG_LOG_NONE is not set */ +#define CONFIG_USE_DEFAULT_INTERRUPT_CONFIG +#define CONFIG_INTERRUPT_ROLE_MASTER +/* CONFIG_INTERRUPT_ROLE_SLAVE is not set */ +#define CONFIG_LOG_EXTRA_INFO +/* CONFIG_BOOTUP_DEBUG_PRINTS is not set */ + +/* Linker Options */ + +/* CONFIG_AARCH32_RAM_LD is not set */ +#define CONFIG_AARCH64_RAM_LD +/* CONFIG_USER_DEFINED_LD is not set */ +#define CONFIG_LINK_SCRIPT_ROM +#define CONFIG_ROM_START_UP_ADDR 0x80100000 +#define CONFIG_ROM_SIZE_MB 1 +#define CONFIG_LINK_SCRIPT_RAM +#define CONFIG_RAM_START_UP_ADDR 0x81000000 +#define CONFIG_RAM_SIZE_MB 64 +#define CONFIG_HEAP_SIZE 1 +#define CONFIG_STACK_SIZE 0x100000 +#define CONFIG_FPU_STACK_SIZE 0x1000 +/* end of Linker Options */ + +/* Compiler Options */ + +#define CONFIG_OUTPUT_BINARY +/* end of Compiler Options */ +/* end of Building Option */ + +/* Component Configuration */ + +/* Freertos Uart Drivers */ + +#define CONFIG_FREERTOS_USE_UART +/* end of Freertos Uart Drivers */ + +/* Freertos Pwm Drivers */ + +/* CONFIG_FREERTOS_USE_PWM is not set */ +/* end of Freertos Pwm Drivers */ + +/* Freertos Qspi Drivers */ + +/* CONFIG_FREERTOS_USE_QSPI is not set */ +/* end of Freertos Qspi Drivers */ + +/* Freertos Wdt Drivers */ + +/* CONFIG_FREERTOS_USE_WDT is not set */ +/* end of Freertos Wdt Drivers */ + +/* Freertos Eth Drivers */ + +/* CONFIG_FREERTOS_USE_XMAC is not set */ +/* end of Freertos Eth Drivers */ + +/* Freertos Gpio Drivers */ + +/* CONFIG_FREERTOS_USE_GPIO is not set */ +/* end of Freertos Gpio Drivers */ + +/* Freertos Spim Drivers */ + +#define CONFIG_FREERTOS_USE_FSPIM +/* end of Freertos Spim Drivers */ + +/* Freertos DMA Drivers */ + +#define CONFIG_FREERTOS_USE_FDDMA +/* CONFIG_FREERTOS_USE_FGDMA is not set */ +/* end of Freertos DMA Drivers */ + +/* Freertos MMC Drivers */ + +/* CONFIG_FREERTOS_USE_FSDIO is not set */ +/* end of Freertos MMC Drivers */ + +/* Freertos Adc Drivers */ + +/* CONFIG_FREERTOS_USE_ADC is not set */ +/* end of Freertos Adc Drivers */ +/* end of Component Configuration */ + +/* FreeRTOS Setting */ + +/* CONFIG_USE_LWIP is not set */ +#define CONFIG_USE_BACKTRACE +/* CONFIG_USE_FATFS is not set */ +/* CONFIG_USE_SFUD is not set */ +/* CONFIG_USE_SPIFFS is not set */ +/* CONFIG_USE_AMP is not set */ +#define CONFIG_USE_LETTER_SHELL + +/* Letter Shell Configuration */ + +#define CONFIG_LS_PL011_UART +#define CONFIG_DEFAULT_LETTER_SHELL_USE_UART1 +/* CONFIG_DEFAULT_LETTER_SHELL_USE_UART0 is not set */ +/* CONFIG_DEFAULT_LETTER_SHELL_USE_UART2 is not set */ +/* end of Letter Shell Configuration */ +#define CONFIG_USE_TLSF +/* CONFIG_USE_SDMMC_CMD is not set */ +#define CONFIG_USE_CHERRY_USB + +/* CherryUSB Configuration */ + +#define CONFIG_CHERRY_USB_PORT_XHCI +/* CONFIG_CHERRY_USB_PORT_PHYTIUM_OTG is not set */ +#define CONFIG_CHERRYUSB_HOST +/* CONFIG_CHERRYUSB_DEVICE is not set */ +#define CONFIG_CHERRY_USB_HOST_HUB +#define CONFIG_CHERRY_USB_HOST_MSC +#define CONFIG_CHERRY_USB_HOST_HID +/* CONFIG_CHERRY_USB_HOST_MTP is not set */ +/* CONFIG_CHERRY_USB_HOST_CDC is not set */ +/* CONFIG_CHERRY_USB_HOST_PRINTER is not set */ +/* CONFIG_CHERRY_USB_HOST_AXU_NET is not set */ +/* CONFIG_CHERRY_USB_HOST_RNDIS_WIRELESS is not set */ +/* end of CherryUSB Configuration */ +/* end of FreeRTOS Setting */ + +#endif diff --git a/demo/phytium-e2000/usbhost/sdkconfig.old b/demo/phytium-e2000/usbhost/sdkconfig.old new file mode 100644 index 00000000..ac6ed6ab --- /dev/null +++ b/demo/phytium-e2000/usbhost/sdkconfig.old @@ -0,0 +1,225 @@ + +# +# Freertos Configuration +# +CONFIG_TARGET_NAME="e2000d_freertos_a64" +CONFIG_XHCI_HOST_INSTANCE_0=y +# CONFIG_XHCI_HOST_INSTANCE_1 is not set +# end of Freertos Configuration + +# +# Standalone Setting +# +CONFIG_USE_FREERTOS=y + +# +# Arch Configuration +# +# CONFIG_TARGET_ARMV8_AARCH32 is not set +CONFIG_TARGET_ARMV8_AARCH64=y +CONFIG_USE_CACHE=y +CONFIG_USE_MMU=y +CONFIG_USE_SYS_TICK=y +# CONFIG_MMU_DEBUG_PRINTS is not set +# end of Arch Configuration + +# +# Board Configuration +# +# CONFIG_TARGET_F2000_4 is not set +# CONFIG_TARGET_D2000 is not set +# CONFIG_TARGET_E2000Q is not set +CONFIG_TARGET_E2000D=y +# CONFIG_TARGET_E2000S is not set +CONFIG_TARGET_E2000=y +CONFIG_DEFAULT_DEBUG_PRINT_UART1=y +# CONFIG_DEFAULT_DEBUG_PRINT_UART0 is not set +# CONFIG_DEFAULT_DEBUG_PRINT_UART2 is not set +# end of Board Configuration + +# +# Components Configuration +# +CONFIG_USE_SPI=y +CONFIG_USE_FSPIM=y +# CONFIG_USE_QSPI is not set +CONFIG_USE_GIC=y +CONFIG_ENABLE_GICV3=y +CONFIG_USE_SERIAL=y + +# +# Usart Configuration +# +CONFIG_ENABLE_Pl011_UART=y +# end of Usart Configuration + +# CONFIG_USE_GPIO is not set +# CONFIG_USE_ETH is not set +# CONFIG_USE_CAN is not set +# CONFIG_USE_I2C is not set +# CONFIG_USE_TIMER is not set +# CONFIG_USE_MIO is not set +# CONFIG_USE_SDMMC is not set +# CONFIG_USE_PCIE is not set +# CONFIG_USE_WDT is not set +CONFIG_USE_DMA=y +# CONFIG_ENABLE_FGDMA is not set +CONFIG_ENABLE_FDDMA=y +# CONFIG_USE_NAND is not set +# CONFIG_USE_RTC is not set +# CONFIG_USE_SATA is not set +# CONFIG_USE_USB is not set +# CONFIG_USE_ADC is not set +# CONFIG_USE_PWM is not set +# CONFIG_USE_IPC is not set +# end of Components Configuration + +CONFIG_USE_NEW_LIBC=y +# end of Standalone Setting + +# +# Building Option +# +# CONFIG_LOG_VERBOS is not set +CONFIG_LOG_DEBUG=y +# CONFIG_LOG_INFO is not set +# CONFIG_LOG_WARN is not set +# CONFIG_LOG_ERROR is not set +# CONFIG_LOG_NONE is not set +CONFIG_USE_DEFAULT_INTERRUPT_CONFIG=y +CONFIG_INTERRUPT_ROLE_MASTER=y +# CONFIG_INTERRUPT_ROLE_SLAVE is not set +CONFIG_LOG_EXTRA_INFO=y +# CONFIG_BOOTUP_DEBUG_PRINTS is not set + +# +# Linker Options +# +# CONFIG_AARCH32_RAM_LD is not set +CONFIG_AARCH64_RAM_LD=y +# CONFIG_USER_DEFINED_LD is not set +CONFIG_LINK_SCRIPT_ROM=y +CONFIG_ROM_START_UP_ADDR=0x80100000 +CONFIG_ROM_SIZE_MB=1 +CONFIG_LINK_SCRIPT_RAM=y +CONFIG_RAM_START_UP_ADDR=0x81000000 +CONFIG_RAM_SIZE_MB=64 +CONFIG_HEAP_SIZE=1 +CONFIG_STACK_SIZE=0x100000 +CONFIG_FPU_STACK_SIZE=0x1000 +# end of Linker Options + +# +# Compiler Options +# +CONFIG_OUTPUT_BINARY=y +# end of Compiler Options +# end of Building Option + +# +# Component Configuration +# + +# +# Freertos Uart Drivers +# +CONFIG_FREERTOS_USE_UART=y +# end of Freertos Uart Drivers + +# +# Freertos Pwm Drivers +# +# CONFIG_FREERTOS_USE_PWM is not set +# end of Freertos Pwm Drivers + +# +# Freertos Qspi Drivers +# +# CONFIG_FREERTOS_USE_QSPI is not set +# end of Freertos Qspi Drivers + +# +# Freertos Wdt Drivers +# +# CONFIG_FREERTOS_USE_WDT is not set +# end of Freertos Wdt Drivers + +# +# Freertos Eth Drivers +# +# CONFIG_FREERTOS_USE_XMAC is not set +# end of Freertos Eth Drivers + +# +# Freertos Gpio Drivers +# +# CONFIG_FREERTOS_USE_GPIO is not set +# end of Freertos Gpio Drivers + +# +# Freertos Spim Drivers +# +CONFIG_FREERTOS_USE_FSPIM=y +# end of Freertos Spim Drivers + +# +# Freertos DMA Drivers +# +CONFIG_FREERTOS_USE_FDDMA=y +# CONFIG_FREERTOS_USE_FGDMA is not set +# end of Freertos DMA Drivers + +# +# Freertos MMC Drivers +# +# CONFIG_FREERTOS_USE_FSDIO is not set +# end of Freertos MMC Drivers + +# +# Freertos Adc Drivers +# +# CONFIG_FREERTOS_USE_ADC is not set +# end of Freertos Adc Drivers +# end of Component Configuration + +# +# FreeRTOS Setting +# +# CONFIG_USE_LWIP is not set +CONFIG_USE_BACKTRACE=y +# CONFIG_USE_FATFS is not set +# CONFIG_USE_SFUD is not set +# CONFIG_USE_SPIFFS is not set +# CONFIG_USE_AMP is not set +CONFIG_USE_LETTER_SHELL=y + +# +# Letter Shell Configuration +# +CONFIG_LS_PL011_UART=y +CONFIG_DEFAULT_LETTER_SHELL_USE_UART1=y +# CONFIG_DEFAULT_LETTER_SHELL_USE_UART0 is not set +# CONFIG_DEFAULT_LETTER_SHELL_USE_UART2 is not set +# end of Letter Shell Configuration + +CONFIG_USE_TLSF=y +# CONFIG_USE_SDMMC_CMD is not set +CONFIG_USE_CHERRY_USB=y + +# +# CherryUSB Configuration +# +CONFIG_CHERRY_USB_PORT_XHCI=y +# CONFIG_CHERRY_USB_PORT_PHYTIUM_OTG is not set +CONFIG_CHERRYUSB_HOST=y +# CONFIG_CHERRYUSB_DEVICE is not set +CONFIG_CHERRY_USB_HOST_HUB=y +CONFIG_CHERRY_USB_HOST_MSC=y +CONFIG_CHERRY_USB_HOST_HID=y +# CONFIG_CHERRY_USB_HOST_MTP is not set +# CONFIG_CHERRY_USB_HOST_CDC is not set +# CONFIG_CHERRY_USB_HOST_PRINTER is not set +# CONFIG_CHERRY_USB_HOST_AXU_NET is not set +# CONFIG_CHERRY_USB_HOST_RNDIS_WIRELESS is not set +# end of CherryUSB Configuration +# end of FreeRTOS Setting diff --git a/demo/phytium-e2000/usbhost/src/cmd_usb.c b/demo/phytium-e2000/usbhost/src/cmd_usb.c new file mode 100644 index 00000000..09e8ffd4 --- /dev/null +++ b/demo/phytium-e2000/usbhost/src/cmd_usb.c @@ -0,0 +1,55 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: cmd_usb.c + * Date: 2022-09-19 14:34:44 + * LastEditTime: 2022-09-19 14:34:45 + * Description:  This files is for letter shell command implmentation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022/9/20 init commit + */ +#include +#include +#include "strto.h" +#include "sdkconfig.h" + +#include "FreeRTOS.h" + +#include "../src/shell.h" +#include "usb_host.h" + +static int USBCmdEntry(int argc, char *argv[]) +{ + int ret = 0; + u32 bytes = 32; + u32 usb_id = 0U; + + if (!strcmp(argv[1], "init")) + { + ret = FFreeRTOSInitUsb(); + } + else if (!strcmp(argv[1], "disk")) + { + + } + else if (!strcmp(argv[1], "hid")) + { + + } + + return ret; +} +SHELL_EXPORT_CMD(SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), usb, USBCmdEntry, test freertos usb driver); \ No newline at end of file diff --git a/demo/phytium-e2000/usbhost/src/usb_host.c b/demo/phytium-e2000/usbhost/src/usb_host.c new file mode 100644 index 00000000..7ac40c4e --- /dev/null +++ b/demo/phytium-e2000/usbhost/src/usb_host.c @@ -0,0 +1,149 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: usb_host.c + * Date: 2022-07-22 13:57:42 + * LastEditTime: 2022-07-22 13:57:43 + * Description:  This files is for cherry usb host function implementation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022/9/20 init commit + */ +/***************************** Include Files *********************************/ +#include +#include + +#include "FreeRTOS.h" +#include "task.h" + +#include "ft_assert.h" +#include "interrupt.h" +#include "cpu_info.h" +#include "ft_debug.h" + +#include "usbh_core.h" + +#include "fmemory_pool.h" +/************************** Constant Definitions *****************************/ +#define FUSB_MEMP_TOTAL_SIZE SZ_1M + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ +static FMemp memp; +static u8 memp_buf[FUSB_MEMP_TOTAL_SIZE]; + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FUSB_DEBUG_TAG "USB-HC" +#define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ +extern void USBH_IRQHandler(void); + +/*****************************************************************************/ +static void UsbHcInterrruptHandler(s32 vector, void *param) +{ + USBH_IRQHandler(); +} + +static void UsbHcSetupInterrupt(void) +{ + u32 cpu_id; + u32 irq_num = CONFIG_XHCI_IRQ_NUM; + u32 irq_priority = 13U; + + GetCpuId(&cpu_id); + InterruptSetTargetCpus(irq_num, cpu_id); + + InterruptSetPriority(irq_num, irq_priority); + + /* register intr callback */ + InterruptInstall(irq_num, + UsbHcInterrruptHandler, + NULL, + NULL); + + /* enable irq */ + InterruptUmask(irq_num); +} + +void UsbHcSetupMemp(void) +{ + if (FT_COMPONENT_IS_READY != memp.is_ready) + { + USB_ASSERT(FT_SUCCESS == FMempInit(&memp, &memp_buf[0], &memp_buf[0] + FUSB_MEMP_TOTAL_SIZE)); + } +} + +/* implement cherryusb */ +void usb_hc_low_level_init(void) +{ + UsbHcSetupMemp(); + UsbHcSetupInterrupt(); +} + +void *usb_hc_malloc(size_t size) +{ + return usb_hc_malloc_align(sizeof(void *), size); +} + +void *usb_hc_malloc_align(size_t align, size_t size) +{ + void *result = FMempMallocAlign(&memp, size, align); + + if (result) + memset(result, 0U, size); + + return result; +} + +void usb_hc_free(void *ptr) +{ + if (NULL != ptr) + FMempFree(&memp, ptr); +} + +void usb_assert(const char *filename, int linenum) +{ + FAssert(filename, linenum, 0xff); +} + +static void UsbInitTask(void * args) +{ + usbh_initialize(); + vTaskDelete(NULL); +} + +BaseType_t FFreeRTOSInitUsb(void) +{ + BaseType_t ret = pdPASS; + + taskENTER_CRITICAL(); /* no schedule when create task */ + + ret = xTaskCreate((TaskFunction_t )UsbInitTask, + (const char* )"UsbInitTask", + (uint16_t )2048, + NULL, + (UBaseType_t )configMAX_PRIORITIES - 1, + NULL); + FASSERT_MSG(pdPASS == ret, "create task failed"); + + taskEXIT_CRITICAL(); /* allow schedule since task created */ + + return ret; +} \ No newline at end of file diff --git a/port/xhci/usb_config.h b/port/xhci/usb_config.h new file mode 100644 index 00000000..92cc24b5 --- /dev/null +++ b/port/xhci/usb_config.h @@ -0,0 +1,143 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: usb_config.h + * Date: 2022-09-19 17:28:44 + * LastEditTime: 2022-09-19 17:28:45 + * Description:  This files is for usb hc xhci configuration + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022/9/19 init commit + */ + +#ifndef CHERRYUSB_CONFIG_H +#define CHERRYUSB_CONFIG_H + + +/* ================ USB common Configuration ================ */ + +#ifndef CONFIG_USB_DBG_LEVEL +#define CONFIG_USB_DBG_LEVEL USB_DBG_INFO +#endif + +#ifndef CONFIG_USB_PRINTF +#define CONFIG_USB_PRINTF printf +#endif + +/* Enable print with color */ +#define CONFIG_USB_PRINTF_COLOR_ENABLE + +/* data align size when use dma */ +#ifndef CONFIG_USB_ALIGN_SIZE +#define CONFIG_USB_ALIGN_SIZE 4 +#endif + +/* attribute data into no cache ram */ +#define USB_NOCACHE_RAM_SECTION __attribute__((section(".noncacheable"))) + +/* ================= USB Device Stack Configuration ================ */ + +/* Ep0 max transfer buffer, specially for receiving data from ep0 out */ +#define CONFIG_USBDEV_REQUEST_BUFFER_LEN 256 + + +#ifndef CONFIG_USBDEV_MSC_BLOCK_SIZE +#define CONFIG_USBDEV_MSC_BLOCK_SIZE 512 +#endif + +#ifndef CONFIG_USBDEV_MSC_MANUFACTURER_STRING +#define CONFIG_USBDEV_MSC_MANUFACTURER_STRING "" +#endif + +#ifndef CONFIG_USBDEV_MSC_PRODUCT_STRING +#define CONFIG_USBDEV_MSC_PRODUCT_STRING "" +#endif + +#ifndef CONFIG_USBDEV_MSC_VERSION_STRING +#define CONFIG_USBDEV_MSC_VERSION_STRING "0.01" +#endif + +#define CONFIG_USBHOST_GET_STRING_DESC + +// #define CONFIG_USBDEV_MSC_THREAD + +#ifdef CONFIG_USBDEV_MSC_THREAD +#ifndef CONFIG_USBDEV_MSC_STACKSIZE +#define CONFIG_USBDEV_MSC_STACKSIZE 2048 +#endif + +#ifndef CONFIG_USBDEV_MSC_PRIO +#define CONFIG_USBDEV_MSC_PRIO 4 +#endif +#endif + +#ifndef CONFIG_USBDEV_AUDIO_VERSION +#define CONFIG_USBDEV_AUDIO_VERSION 0x0100 +#endif + +#ifndef CONFIG_USBDEV_AUDIO_MAX_CHANNEL +#define CONFIG_USBDEV_AUDIO_MAX_CHANNEL 8 +#endif + +/* ================ USB HOST Stack Configuration ================== */ + +#define CONFIG_USBHOST_MAX_RHPORTS 1 +#define CONFIG_USBHOST_MAX_EXTHUBS 1 +#define CONFIG_USBHOST_MAX_EHPORTS 4 +#define CONFIG_USBHOST_MAX_INTERFACES 6 +#define CONFIG_USBHOST_MAX_INTF_ALTSETTINGS 1 +#define CONFIG_USBHOST_MAX_ENDPOINTS 4 + +#define CONFIG_USBHOST_DEV_NAMELEN 16 + +#ifndef CONFIG_USBHOST_PSC_PRIO +#define CONFIG_USBHOST_PSC_PRIO 4 +#endif +#ifndef CONFIG_USBHOST_PSC_STACKSIZE +#define CONFIG_USBHOST_PSC_STACKSIZE 2048 +#endif + +/* Ep0 max transfer buffer */ +#define CONFIG_USBHOST_REQUEST_BUFFER_LEN 512 + +#ifndef CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT +#define CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT 500 +#endif + +#ifndef CONFIG_USBHOST_MSC_TIMEOUT +#define CONFIG_USBHOST_MSC_TIMEOUT 5000 +#endif + +/* ================ USB Device Port Configuration ================*/ + +/* Phytium-E2000 instance-0 */ +#define CONFIG_XHCI_BASE_ADDR 0x31A08000U +#define CONFIG_XHCI_IRQ_NUM 48U + +/* Phytium-E2000 instance-1 */ +// #define CONFIG_XHCI_BASE_ADDR 0x31A28000U +// #define CONFIG_XHCI_IRQ_NUM 49U + +#define CONFIG_XHCI_PAGE_SIZE 4096U +#define CONFIG_XHCI_PAGE_SHIFT 12U + +/* ================ USB Host Port Configuration ==================*/ + +#define CONFIG_USBHOST_PIPE_NUM 10 + +/* ================ XHCI Configuration ================ */ +#define CONFIG_USBHOST_XHCI + +#endif diff --git a/port/xhci/usb_hc_xhci.c b/port/xhci/usb_hc_xhci.c new file mode 100644 index 00000000..da07da8d --- /dev/null +++ b/port/xhci/usb_hc_xhci.c @@ -0,0 +1,1218 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: usb_hc_xhci.c + * Date: 2022-09-19 17:24:36 + * LastEditTime: 2022-09-19 17:24:36 + * Description:  This files is for xhci function implementation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022/9/19 init commit + */ + +/***************************** Include Files *********************************/ +#include "usbh_core.h" +#include "usbh_hub.h" +#include "xhci_reg.h" +#include "usb_hc_xhci.h" + +/************************** Constant Definitions *****************************/ + +/************************** Variable Definitions *****************************/ +/* input is xhci speed */ +static const char *speed_name[16] = { + [ 0 ] = " - ", + [ 1 ] = "Full", + [ 2 ] = "Low", + [ 3 ] = "High", + [ 4 ] = "Super", +}; + +static const int speed_from_xhci[16] = { + [ 0 ] = -1, + [ 1 ] = USB_SPEED_FULL, + [ 2 ] = USB_SPEED_LOW, + [ 3 ] = USB_SPEED_HIGH, + [ 4 ] = USB_SPEED_SUPER, + [ 5 ... 15 ] = -1, +}; + +static const int speed_to_xhci[] = { + [ USB_SPEED_FULL ] = 1, + [ USB_SPEED_LOW ] = 2, + [ USB_SPEED_HIGH ] = 3, + [ USB_SPEED_SUPER ] = 4, +}; + +static volatile struct xhci_pipe *cur_cmd_pipe = NULL; /* pass current command pipe to interrupt */ +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ +/* Wait until bit = value or timeout */ +static int wait_bit(unsigned long reg_off, uint32_t mask, uint32_t value, uint32_t timeout) +{ + uint32_t delay = 0U; /* calc wait end time */ + + while ((readl(reg_off) & mask) != value) { + if (++delay > timeout) { /* check if timeout */ + USB_LOG_ERR("wait timeout !!!\n"); + return -1; + } + usb_osal_msleep(1U); + } + return 0; +} + +/* Check if device attached to port */ +static void xhci_print_port_state(const char *prefix, uint32_t port, uint32_t portsc) +{ + uint32_t pls = XHCI_REG_OP_PORTS_PORTSC_PLS_GET(portsc); + uint32_t speed = XHCI_REG_OP_PORTS_PORTSC_PORT_SPEED_GET(portsc); + + USB_LOG_DBG("%s port #%d: 0x%08x,%s%s pls %d, speed %d [%s]\n", + prefix, port + 1, portsc, + (portsc & XHCI_REG_OP_PORTS_PORTSC_PP) ? " powered," : "", + (portsc & XHCI_REG_OP_PORTS_PORTSC_PED) ? " enabled," : "", + pls, speed, speed_name[speed]); +} + +static inline uint32_t xhci_readl_port(struct xhci_s *xhci, uint32_t port, uint32_t offset) +{ + /* Operational Base + (400h + (10h * (n–1))) */ + return readl(xhci->pr + port * XHCI_REG_OP_PORTS_SIZE + offset); +} + +static inline void xhci_writel_port(struct xhci_s *xhci, uint32_t port, uint32_t offset, uint32_t val) +{ + /* Operational Base + (400h + (10h * (n–1))) */ + return writel(xhci->pr + port * XHCI_REG_OP_PORTS_SIZE + offset, val); +} + +static void xhci_setup_mmio(struct xhci_s *xhci, unsigned long base_addr) +{ + xhci->base = base_addr; + xhci->caps = xhci->base; + + /* add to register base to find the beginning of the Operational Register Space */ + xhci->op = xhci->base + readb(xhci->caps + XHCI_REG_CAP_CAPLENGTH); + xhci->db = xhci->base + XHCI_REG_CAP_DBOFF_GET(readl(xhci->caps + XHCI_REG_CAP_DBOFF)); + xhci->ir = xhci->base + XHCI_REG_CAP_RTSOFF_GET(readl(xhci->caps + XHCI_REG_CAP_RTSOFF)) + + XHCI_REG_RT_IR0; + xhci->pr = xhci->op + XHCI_REG_OP_PORTS_BASE; + + /* cache static information of CAP_HCSPARAMS */ + xhci->hcs[0] = readl(xhci->caps + XHCI_REG_CAP_HCS1); + xhci->hcs[1] = readl(xhci->caps + XHCI_REG_CAP_HCS2); + xhci->hcs[2] = readl(xhci->caps + XHCI_REG_CAP_HCS3); + xhci->hcc = readl(xhci->caps + XHCI_REG_CAP_HCC); + + /* avoid access XHCI_REG_CAP_HCIVERSION = 0x2, unaligned memory */ + xhci->version = ((readl(xhci->caps + XHCI_REG_CAP_CAPLENGTH) >> 16) & 0xffff); + + xhci->ports = XHCI_REG_CAP_HCS1_MAX_PORTS_GET(xhci->hcs[0]); /* bit[31:24] max ports */ + xhci->slots = XHCI_REG_CAP_HCS1_MAX_SLOTS_GET(xhci->hcs[0]); /* bit[7:0] max device slots */ + + /* For example, using the offset of Base is 1000h and the xECP value of 0068h, we can calculated + the following effective address of the first extended capability: + 1000h + (0068h << 2) -> 1000h + 01A0h -> 11A0h */ + xhci->xcap = XHCI_REG_CAP_HCC_XECP_GET(xhci->hcc) << 2; /* bit[31:16] xHCI extended cap pointer */ + xhci->context64 = (XHCI_REG_CAP_HCC_CSZ & xhci->hcc) ? true : false; + + USB_LOG_INFO(" hc version: 0x%x\n", xhci->version); + USB_LOG_INFO(" mmio base: 0x%x\n", xhci->base); + USB_LOG_INFO(" oper base: 0x%x\n", xhci->op); + USB_LOG_INFO(" doorbell base: 0x%x\n", xhci->db); + USB_LOG_INFO(" runtime base: 0x%x\n", xhci->ir); + USB_LOG_INFO(" port base: 0x%x\n", xhci->pr); + USB_LOG_INFO(" xcap base: 0x%x\n", xhci->xcap); + + USB_LOG_INFO(" slot num: 0x%x\n", xhci->slots); + USB_LOG_INFO(" port num: 0x%x\n", xhci->ports); + USB_LOG_INFO(" context: %d bit\n", xhci->context64 ? 64 : 32); +} + + +/* Reset device on port */ +static int xhci_hub_reset(struct xhci_s *xhci, uint32_t port) +{ + uint32_t portsc = xhci_readl_port(xhci, port, XHCI_REG_OP_PORTS_PORTSC); + if (!(portsc & XHCI_REG_OP_PORTS_PORTSC_CCS)) /* double check if connected */ + /* Device no longer connected?! */ + return -1; + + switch (XHCI_REG_OP_PORTS_PORTSC_PLS_GET(portsc)) { + case PLS_U0: + /* A USB3 port - controller automatically performs reset */ + break; + case PLS_POLLING: + /* A USB2 port - perform device reset */ + xhci_print_port_state(__func__, port, portsc); + xhci_writel_port(xhci, port, XHCI_REG_OP_PORTS_PORTSC, + portsc | XHCI_REG_OP_PORTS_PORTSC_PR); /* reset port */ + break; + default: + return -1; + } + + // Wait for device to complete reset and be enabled + uint32_t end = 1000U, start = 0U; + for (;;) { + portsc = xhci_readl_port(xhci, port, XHCI_REG_OP_PORTS_PORTSC); + if (!(portsc & XHCI_REG_OP_PORTS_PORTSC_CCS)) + /* Device disconnected during reset */ + return -1; + + if (portsc & XHCI_REG_OP_PORTS_PORTSC_PED) /* check if port enabled */ + /* Reset complete */ + break; + + if (++start > end) { + USB_LOG_ERR("wait timeout, portsc=0x%x!!!\n", portsc); + return -1; + } + + usb_osal_msleep(1); + } + + int rc = speed_from_xhci[XHCI_REG_OP_PORTS_PORTSC_PORT_SPEED_GET(portsc)]; + xhci_print_port_state("XHCI", port, portsc); + return rc; +} + +/* Add a TRB to the given ring */ +static void xhci_trb_fill(struct xhci_ring *ring, void *data, uint32_t xferlen, uint32_t flags) +{ + struct xhci_trb *dst = &ring->ring[ring->nidx]; + if (flags & TRB_TR_IDT) { + memcpy(&dst->ptr_low, data, xferlen); + } else { + dst->ptr_low = (uint32_t)(unsigned long)data; +#ifdef XHCI_AARCH64 + dst->ptr_high = (uint32_t)((uint64_t)data >> 32U); +#else + dst->ptr_high = 0U; +#endif + } + dst->status = xferlen; + /* cycle bit */ + dst->control = flags | (ring->cs ? TRB_C : 0); +} + +/* Queue a TRB onto a ring, wrapping ring as needed */ +static void xhci_trb_queue(struct xhci_ring *ring, void *data, uint32_t xferlen, uint32_t flags) +{ + if (ring->nidx >= ARRAY_SIZE(ring->ring) - 1) { /* if it is the last trb in the list */ + /* indicate the end of ring, bit[1], toggle cycle = 1, xHc shall toggle cycle bit interpretation */ + xhci_trb_fill(ring, ring->ring, 0, (TR_LINK << 10) | TRB_LK_TC); + ring->nidx = 0; /* adjust dequeue index to 0 */ + ring->cs ^= 1; /* toggle cycle interpretation of sw */ + } + + xhci_trb_fill(ring, data, xferlen, flags); + ring->nidx++; /* ahead dequeue index */ +} + +/* Signal the hardware to process events on a TRB ring */ +static void xhci_doorbell(struct xhci_s *xhci, uint32_t slotid, uint32_t value) +{ + writel(xhci->db + slotid * XHCI_REG_DB_SIZE, value); /* bit[7:0] db target, is ep_id */ +} + +/* Wait for a ring to empty (all TRBs processed by hardware) */ +static int xhci_event_wait(struct xhci_s *xhci, + struct xhci_pipe *pipe, + struct xhci_ring *ring) +{ + int ret = CC_SUCCESS; + + if (pipe->timeout > 0){ + ret = usb_osal_sem_take(pipe->waitsem, pipe->timeout); + if (ret < 0) { + ret = CC_TIMEOUT; + } else { + ret = TRB_CC_GET(ring->evt.status); /* bit[31:24] completion code */ + } + } + + return ret; +} + +/* Submit a command to the xhci controller ring */ +static int xhci_cmd_submit(struct xhci_s *xhci, struct xhci_inctx *inctx, + struct xhci_pipe *pipe, uint32_t flags) +{ + if (inctx) { + struct xhci_slotctx *slot = (void*)&inctx[1 << xhci->context64]; + uint32_t port = XHCI_SLOTCTX_1_ROOT_PORT_GET(slot->ctx[1]) - 1; + USB_LOG_DBG("port=%d\n", port); + uint32_t portsc = xhci_readl_port(xhci, port, XHCI_REG_OP_PORTS_PORTSC); + if (!(portsc & XHCI_REG_OP_PORTS_PORTSC_CCS)) { + /* Device no longer connected?! */ + xhci_print_port_state(__func__, port, portsc); + return CC_DISCONNECTED; + } + } + + usb_osal_mutex_take(xhci->cmds->lock); /* handle command one by one */ + + pipe->timeout = 5000; + pipe->waiter = true; + cur_cmd_pipe = pipe; + + xhci_trb_queue(xhci->cmds, inctx, 0, flags); + + /* pass command trb to hardware */ + DSB(); + + xhci_doorbell(xhci, 0, 0); /* 0 = db host controller, 0 = db targe hc command */ + int rc = xhci_event_wait(xhci, pipe, xhci->cmds); + + usb_osal_mutex_give(xhci->cmds->lock); + + return rc; +} + +static int xhci_cmd_nop(struct xhci_s *xhci, struct xhci_pipe *pipe) +{ + USB_LOG_DBG("%s\n", __func__); + return xhci_cmd_submit(xhci, NULL, pipe, TRB_TYPE_SET(CR_NOOP)); +} + +static int xhci_cmd_enable_slot(struct xhci_s *xhci, struct xhci_pipe *pipe) +{ + int cc = xhci_cmd_submit(xhci, NULL, pipe, TRB_TYPE_SET(CR_ENABLE_SLOT)); + if (cc != CC_SUCCESS) + return cc; + + struct xhci_trb *evt = &(xhci->cmds->evt); + return TRB_CR_SLOTID_GET(evt->control); /* bit [31:24] slot id */ +} + +static int xhci_cmd_disable_slot(struct xhci_s *xhci, struct xhci_pipe *pipe) +{ + USB_LOG_DBG("%s: slotid %d\n", __func__, pipe->slotid); + return xhci_cmd_submit(xhci, NULL, pipe, TRB_TYPE_SET(CR_DISABLE_SLOT) | TRB_CR_SLOTID_SET(pipe->slotid)); +} + +static int xhci_cmd_address_device(struct xhci_s *xhci, struct xhci_pipe *pipe, struct xhci_inctx *inctx) +{ + USB_LOG_DBG("%s: slotid %d\n", __func__, pipe->slotid); + return xhci_cmd_submit(xhci, inctx, pipe, TRB_TYPE_SET(CR_ADDRESS_DEVICE) | TRB_CR_SLOTID_SET(pipe->slotid)); +} + +static int xhci_cmd_configure_endpoint(struct xhci_s *xhci, struct xhci_pipe *pipe, struct xhci_inctx *inctx) +{ + USB_LOG_DBG("%s: slotid %d\n", __func__, pipe->slotid); + return xhci_cmd_submit(xhci, inctx, pipe, TRB_TYPE_SET(CR_CONFIGURE_ENDPOINT) | TRB_CR_SLOTID_SET(pipe->slotid)); +} + +static int xhci_cmd_evaluate_context(struct xhci_s *xhci, struct xhci_pipe *pipe, struct xhci_inctx *inctx) +{ + USB_LOG_DBG("%s: slotid %d\n", __func__, pipe->slotid); + /* bit[15:10] TRB type, bit[31:24] Slot id */ + return xhci_cmd_submit(xhci, inctx, pipe, TRB_TYPE_SET(CR_EVALUATE_CONTEXT) | TRB_CR_SLOTID_SET(pipe->slotid)); +} + +static int xhci_controller_configure(struct xhci_s *xhci) +{ + uint32_t reg; + + /* trbs */ + xhci->devs = usb_align(XHCI_ALIGMENT, sizeof(*xhci->devs) * (xhci->slots + 1)); /* device slot */ + xhci->eseg = usb_align(XHCI_ALIGMENT, sizeof(*xhci->eseg)); /* event segment */ + xhci->cmds = usb_align(XHCI_RING_SIZE, sizeof(*xhci->cmds)); /* command ring */ + xhci->evts = usb_align(XHCI_RING_SIZE, sizeof(*xhci->evts)); /* event ring */ + + if (!xhci->devs || !xhci->cmds || !xhci->evts || !xhci->eseg) { + USB_LOG_ERR("allocate memory failed !!!\n"); + goto fail; + } + + memset(xhci->devs, 0U, sizeof(*xhci->devs) * (xhci->slots + 1)); + memset(xhci->cmds, 0U, sizeof(*xhci->cmds)); + memset(xhci->evts, 0U, sizeof(*xhci->evts)); + memset(xhci->eseg, 0U, sizeof(*xhci->eseg)); + + xhci->cmds->lock = usb_osal_mutex_create(); + USB_ASSERT(xhci->cmds->lock); + + reg = readl(xhci->op + XHCI_REG_OP_USBCMD); + if (reg & XHCI_REG_OP_USBCMD_RUN_STOP) { /* if xHc running, stop it first */ + reg &= ~XHCI_REG_OP_USBCMD_RUN_STOP; + writel(xhci->op + XHCI_REG_OP_USBCMD, reg); /* stop xHc */ + + if (wait_bit(xhci->op + XHCI_REG_OP_USBSTS, + XHCI_REG_OP_USBSTS_HCH, XHCI_REG_OP_USBSTS_HCH, 32) != 0) /* wait xHc halt */ + goto fail; + } + + USB_LOG_DBG("%s: resetting\n", __func__); + + writel(xhci->op + XHCI_REG_OP_USBCMD, XHCI_REG_OP_USBCMD_HCRST); /* reset xHc */ + if (wait_bit(xhci->op + XHCI_REG_OP_USBCMD, XHCI_REG_OP_USBCMD_HCRST, 0, 1000) != 0) /* wait reset process done */ + goto fail; + + if (wait_bit(xhci->op + XHCI_REG_OP_USBSTS, XHCI_REG_OP_USBSTS_CNR, 0, 1000) != 0) /* wait until xHc ready */ + goto fail; + + writel(xhci->op + XHCI_REG_OP_CONFIG, xhci->slots); /* bit[7:0], set max num of device slot enabled */ + writeq(xhci->op + XHCI_REG_OP_DCBAAP, (uint64_t)xhci->devs); /* bit[63:6] device context base address array pointer */ + writeq(xhci->op + XHCI_REG_OP_CRCR, (uint64_t)xhci->cmds | 1); /* command ring pointer, cycle state = 1 */ + xhci->cmds->cs = 1; /* cycle state = 1 */ + + xhci->eseg->ptr_low = (uint32_t)((unsigned long)xhci->evts); /* event ring pointer */ +#ifdef XHCI_AARCH64 + xhci->eseg->ptr_high = (uint32_t)((unsigned long)xhci->evts >> 32U); +#else + xhci->eseg->ptr_high = 0U; +#endif + + xhci->eseg->size = XHCI_RING_ITEMS; /* items of event ring TRB */ + writel(xhci->ir + XHCI_REG_RT_IR_ERSTSZ, 1); /* bit[15:0] event ring segment table size */ + writeq(xhci->ir + XHCI_REG_RT_IR_ERDP, (uint64_t)xhci->evts); /* bit[63:4] event ring dequeue pointer */ + writeq(xhci->ir + XHCI_REG_RT_IR_ERSTBA, (uint64_t)xhci->eseg); /* bit[63:6] event ring segment table base addr */ + xhci->evts->cs = 1; /* cycle state = 1 */ + + reg = xhci->hcs[1]; + uint32_t spb = XHCI_REG_CAP_HCS2_MAX_SCRATCHPAD_BUFS_GET(reg); /* bit [25:21] | bit [31:27] max scratchpad buffers */ + if (spb) { + /* reserve scratchpad buffers for xHc */ + USB_LOG_DBG("%s: setup %d scratch pad buffers\n", __func__, spb); + uint64_t *spba = usb_align(XHCI_ALIGMENT, sizeof(*spba) * spb); /* base addr of scratchpad buffers */ + void *pad = usb_align(CONFIG_XHCI_PAGE_SIZE, CONFIG_XHCI_PAGE_SIZE * spb); /* the whole scratchpad buffers */ + if (!spba || !pad) { + USB_LOG_ERR("allocate memory failed !!!\n"); + usb_free(spba); + usb_free(pad); + goto fail; + } + for (uint32_t i = 0; i < spb; i++) + spba[i] = (uint64_t)(pad + (i * CONFIG_XHCI_PAGE_SIZE)); /* assign base addr for each pad */ + xhci->devs[0].ptr_low = (uint32_t)((unsigned long)spba); /* bit[63:6] scratchpad buffers base addr */ +#ifdef XHCI_AARCH64 + xhci->devs[0].ptr_high = (uint32_t)((uint64_t)spba >> 32U); +#else + xhci->devs[0].ptr_high = 0U; +#endif + } + + /* enable port interrupt */ + writel(xhci->ir + XHCI_REG_RT_IR_IMOD, 500U); + reg = readl(xhci->ir + XHCI_REG_RT_IR_IMAN); + reg |= XHCI_REG_RT_IR_IMAN_IE; + writel(xhci->ir + + XHCI_REG_RT_IR_IMAN, reg); + + reg = readl(xhci->op + XHCI_REG_OP_USBCMD); + reg |= XHCI_REG_OP_USBCMD_INTE; /* enable interrupt */ + writel(xhci->op + XHCI_REG_OP_USBCMD, reg); + + USB_LOG_DBG("Start xHc ....\n"); + + reg = readl(xhci->op + XHCI_REG_OP_USBCMD); + reg |= XHCI_REG_OP_USBCMD_RUN_STOP; /* start xHc */ + writel(xhci->op + XHCI_REG_OP_USBCMD, reg); + + return 0U; /* Success */ +fail: + USB_LOG_ERR("Configure Roothub failed !!!\n"); + usb_free(xhci->eseg); + usb_free(xhci->evts); + usb_free(xhci->cmds); + usb_free(xhci->devs); + return -1; /* Failed */ +} + +static void xhci_check_xcap(struct xhci_s *xhci) +{ + if (xhci->xcap) { /* read Extended Capabilities */ + uint32_t off; + unsigned long addr = xhci->base + xhci->xcap; /* start read ext-cap */ + + do { + unsigned long xcap = addr; + uint32_t ports, name, cap = readl(xcap + XHCI_REG_EXT_CAP_USBSPCF_OFFSET); + switch (XHCI_REG_EXT_CAP_CAP_ID_GET(cap)) { + case XHCI_EXT_CAP_ID_SUPPORT_PROTOCOL: /* 0x2, supported protocol */ + name = readl(xcap + XHCI_REG_EXT_CAP_USBSPCFDEF_OFFSET); + ports = readl(xcap + XHCI_REG_EXT_CAP_USBSPCFDEF2_OFFSET); + uint8_t major = XHCI_USBSPCF_MAJOR_REVERSION_GET(cap); + uint8_t minor = XHCI_USBSPCF_MINOR_REVERSION_GET(cap); + uint8_t count = XHCI_USBSPCFDEF2_COMPATIBLE_PORT_CNT_GET(ports); + uint8_t start = XHCI_USBSPCFDEF2_COMPATIBLE_PORT_OFF_GET(ports); + USB_LOG_INFO("XHCI protocol %c%c%c%c %x.%02x" + ", %d ports (offset %d), def %x\n" + , (name >> 0) & 0xff + , (name >> 8) & 0xff + , (name >> 16) & 0xff + , (name >> 24) & 0xff /* Print string 'USB' */ + , major, minor + , count, start + , ports >> 16); + if (name == XHCI_USBSPCFDEF_NAME_STRING_USB /* ASCII "USB " */) { + if (major == 2) { + xhci->usb2.start = start; /* USB 2.0 port range */ + xhci->usb2.count = count; + } + if (major == 3) { + xhci->usb3.start = start; /* USB 3.0 port range */ + xhci->usb3.count = count; + } + } + break; + default: + USB_LOG_INFO("XHCI extcap 0x%x @ %p\n", + XHCI_REG_EXT_CAP_CAP_ID_GET(cap), addr); + break; + } + off = XHCI_REG_EXT_NEXT_CAP_PTR_GET(cap); + addr += off << 2; /* addr of next ext-cap */ + } while (off > 0); + } +} + +static int xhci_controller_setup(struct xhci_s *xhci, unsigned long baseaddr) +{ + USB_LOG_INFO("%s@0x%x\n", __func__, baseaddr); + + /* get register offset */ + xhci_setup_mmio(xhci, baseaddr); + if (xhci->version < 0x96 || xhci->version > 0x120) { + USB_LOG_ERR("xHCI-0x%x not support\n", xhci->version); + return -1; + } + + xhci_check_xcap(xhci); + + uint32_t pagesize = readl(xhci->op + XHCI_REG_OP_PAGESIZE); /* get page-size */ + USB_LOG_INFO("XHCI page size %d \n", (pagesize << CONFIG_XHCI_PAGE_SHIFT)); + if (CONFIG_XHCI_PAGE_SIZE != (pagesize << CONFIG_XHCI_PAGE_SHIFT)) { + USB_LOG_ERR("XHCI driver does not support page size code %d\n" + , pagesize << CONFIG_XHCI_PAGE_SHIFT); + return -1; + } + + USB_LOG_INFO("config XHCI ....\n"); + if (xhci_controller_configure(xhci)) { + USB_LOG_ERR("init XHCI failed !!!\n"); + return -1; + } else { + USB_LOG_INFO("init XHCI success !!!\n"); + } + + return 0; +} + +static struct xhci_inctx *xhci_alloc_inctx(struct xhci_s *xhci, struct usbh_hubport *hport, int maxepid) +{ + USB_ASSERT(xhci && hport); + + int size = (sizeof(struct xhci_inctx) * XHCI_INCTX_ENTRY_NUM) << xhci->context64; + struct xhci_inctx *in = usb_align(XHCI_INCTX_ALIGMENT << xhci->context64, size); + int devport = hport->port; + + if (!in) { + USB_LOG_ERR("allocate memory failed !!!\n"); + return NULL; + } + memset(in, 0, size); + + struct xhci_slotctx *slot = (void*)&in[1 << xhci->context64]; /* slot context */ + slot->ctx[0] |= XHCI_SLOTCTX_0_MAX_EPID_SET(maxepid); /* bit[31:27] index of the last valid ep context */ + slot->ctx[0] |= XHCI_SLOTCTX_0_SPEED_SET(speed_to_xhci[hport->speed]); /* bit[23:20] speed of this device */ + + /* Set high-speed hub flags. */ + struct usbh_hub *hubdev = hport->parent; + USB_ASSERT(hubdev); + if (!hubdev->is_roothub) { /* if device is not under roothub */ + struct usbh_hubport *hub_port = hubdev->parent; + if (hport->speed == USB_SPEED_LOW || hport->speed == USB_SPEED_FULL) { + if (hub_port->speed == USB_SPEED_HIGH) { + slot->ctx[2] |= XHCI_SLOTCTX_2_HUB_SLOT_SET(hub_port->dev_addr); /* bit[7:0] parent hub slot id */ + slot->ctx[2] |= XHCI_SLOTCTX_2_HUB_PORT_SET(hport->port); /* bit[15:8] parent port num */ + } else { + struct xhci_slotctx *hslot = (void*)(unsigned long)xhci->devs[hub_port->dev_addr].ptr_low; + slot->ctx[2] = hslot->ctx[2]; /* 08h, copy hub slot context */ + } + } + + uint32_t route = 0U; + do { + route <<= 4; + route |= (hport->port) & 0xf; /* record port for each tire */ + + hport = hubdev->parent; + hubdev = hport->parent; + } while ((!hubdev) || (!hubdev->is_roothub)); + + slot->ctx[0] |= XHCI_SLOTCTX_0_ROUTE_SET(route); /* bit[19:0] route string, max 5 tires */ + } + + /* refer to spec. Ports are numbered from 1 to MaxPorts. */ + slot->ctx[1] |= XHCI_SLOTCTX_1_ROOT_PORT_SET(hport->port); /* bit[23:16] root hub port number */ + + return in; +} + + +// Submit a USB "setup" message request to the pipe's ring +static void xhci_xfer_setup(struct xhci_s *xhci, struct xhci_pipe *pipe, + bool dir_in, void *cmd, void *data, int datalen) +{ + /* SETUP TRB ctrl, bit[15:10] trb type + bit[6] Immediate Data (IDT), parameters take effect + bit[17:16] Transfer type, 2 = OUT Data, 3 = IN Data */ + uint32_t trans_type = (datalen > 0) ? (dir_in ? TRB_TR_IN_DATA : TRB_TR_OUT_DATA): TRB_TR_NO_DATA; + xhci_trb_queue(&pipe->reqs, cmd, USB_SIZEOF_SETUP_PACKET, + TRB_TYPE_SET(TR_SETUP) | TRB_TR_IDT | TRB_TR_TYPE_SET(trans_type)); + + /* DATA TRB ctrl, bit[15:10] trb type + bit[16] Direction, 0 = OUT, 1 = IN */ + if (datalen) { + xhci_trb_queue(&pipe->reqs, data, datalen, + TRB_TYPE_SET(TR_DATA) | (dir_in ? TRB_TR_DIR : 0)); + } + + /* STATUS TRB ctrl, bit[5] Interrupt On Completion (IOC). + bit[16] Direction, 0 = OUT, 1 = IN */ + xhci_trb_queue(&pipe->reqs, NULL, 0, + TRB_TYPE_SET(TR_STATUS) | TRB_TR_IOC | ((dir_in ? 0 : TRB_TR_DIR))); + + /* pass command trb to hardware */ + DSB(); + + /* notfiy xHc that device slot - epid */ + xhci_doorbell(xhci, pipe->slotid, pipe->epid); +} + +/* Submit a USB transfer request to the pipe's ring */ +static void xhci_xfer_normal(struct xhci_s *xhci, struct xhci_pipe *pipe, + void *data, int datalen) +{ + /* Normal trb, used in bulk and interrupt transfer */ + xhci_trb_queue(&pipe->reqs, data, datalen, TRB_TYPE_SET(TR_NORMAL) | TRB_TR_IOC); + + /* pass command trb to hardware */ + DSB(); + + xhci_doorbell(xhci, pipe->slotid, pipe->epid); +} + +/* -------------------------------------------------------------- */ +/* port functions */ +static struct xhci_s xhci_host; + +__WEAK void usb_hc_low_level_init(void) +{ +} + +__WEAK void *usb_hc_malloc(size_t size) +{ + return NULL; +} + +__WEAK void *usb_hc_malloc_align(size_t align, size_t size) +{ + return NULL; +} + +__WEAK void usb_hc_free() +{ +} + +int usb_hc_init(void) +{ + usb_hc_low_level_init(); + + memset(&xhci_host, 0, sizeof(xhci_host)); + if (xhci_controller_setup(&xhci_host, CONFIG_XHCI_BASE_ADDR)) + return -1; + + return 0; +} + +int usbh_roothub_control(struct usb_setup_packet *setup, uint8_t *buf) +{ + uint8_t nports; + uint8_t port; + uint32_t portsc; + uint32_t status; + int ret = 0; + struct xhci_s *xhci = &xhci_host; + + nports = CONFIG_USBHOST_MAX_RHPORTS; + + port = setup->wIndex; + if (setup->bmRequestType & USB_REQUEST_RECIPIENT_DEVICE) { + switch (setup->bRequest) { + case HUB_REQUEST_CLEAR_FEATURE: + switch (setup->wValue) { + case HUB_FEATURE_HUB_C_LOCALPOWER: + break; + case HUB_FEATURE_HUB_C_OVERCURRENT: + break; + default: + return -EPIPE; + } + break; + case HUB_REQUEST_SET_FEATURE: + switch (setup->wValue) { + case HUB_FEATURE_HUB_C_LOCALPOWER: + break; + case HUB_FEATURE_HUB_C_OVERCURRENT: + break; + default: + return -EPIPE; + } + break; + case HUB_REQUEST_GET_DESCRIPTOR: + break; + case HUB_REQUEST_GET_STATUS: + memset(buf, 0, 4); + break; + default: + break; + } + } else if (setup->bmRequestType & USB_REQUEST_RECIPIENT_OTHER) { + switch (setup->bRequest) { + case HUB_REQUEST_CLEAR_FEATURE: + if (!port || port > nports) { + return -EPIPE; + } + + portsc = xhci_readl_port(xhci, port - 1, XHCI_REG_OP_PORTS_PORTSC); + + switch (setup->wValue) { + case HUB_PORT_FEATURE_ENABLE: + break; + case HUB_PORT_FEATURE_SUSPEND: + case HUB_PORT_FEATURE_C_SUSPEND: + break; + case HUB_PORT_FEATURE_POWER: + break; + case HUB_PORT_FEATURE_C_CONNECTION: + portsc |= XHCI_REG_OP_PORTS_PORTSC_CSC; + break; + case HUB_PORT_FEATURE_C_ENABLE: + portsc |= XHCI_REG_OP_PORTS_PORTSC_PEC; + break; + case HUB_PORT_FEATURE_C_OVER_CURREN: + break; + case HUB_PORT_FEATURE_C_RESET: + break; + default: + return -EPIPE; + } + + uint32_t pclear = portsc & XHCI_REG_OP_PORTS_PORTSC_RW_MASK; + xhci_writel_port(xhci, port - 1, XHCI_REG_OP_PORTS_PORTSC, pclear); /* clear port status */ + + break; + case HUB_REQUEST_SET_FEATURE: + if (!port || port > nports) { + return -EPIPE; + } + + switch (setup->wValue) { + case HUB_PORT_FEATURE_SUSPEND: + break; + case HUB_PORT_FEATURE_POWER: + break; + case HUB_PORT_FEATURE_RESET: + ret = xhci_hub_reset(xhci, port - 1); + break; + + default: + return -EPIPE; + } + break; + case HUB_REQUEST_GET_STATUS: + if (!port || port > nports) { + return -EPIPE; + } + + portsc = xhci_readl_port(xhci, port - 1, XHCI_REG_OP_PORTS_PORTSC); + + status = 0; + if (portsc & XHCI_REG_OP_PORTS_PORTSC_CSC) { + /* Port connection status changed */ + status |= (1 << HUB_PORT_FEATURE_C_CONNECTION); + } + + if (portsc & XHCI_REG_OP_PORTS_PORTSC_PEC) { + /* Port enabled status changed */ + status |= (1 << HUB_PORT_FEATURE_C_ENABLE); + } + + if (portsc & XHCI_REG_OP_PORTS_PORTSC_OCC) { + /* Port status changed due to over-current */ + status |= (1 << HUB_PORT_FEATURE_C_OVER_CURREN); + } + + if (portsc & XHCI_REG_OP_PORTS_PORTSC_CCS) { + /* Port connected */ + status |= (1 << HUB_PORT_FEATURE_CONNECTION); + } + + if (portsc & XHCI_REG_OP_PORTS_PORTSC_PED) { + /* Port enabled */ + status |= (1 << HUB_PORT_FEATURE_ENABLE); + + const int speed = speed_from_xhci[XHCI_REG_OP_PORTS_PORTSC_PORT_SPEED_GET(portsc)]; + if (speed == USB_SPEED_LOW) { + status |= (1 << HUB_PORT_FEATURE_LOWSPEED); + } else if ((speed == USB_SPEED_HIGH) || (speed == USB_SPEED_SUPER) || + (speed == USB_SPEED_FULL)) { + status |= (1 << HUB_PORT_FEATURE_HIGHSPEED); + } + } + + if (portsc & XHCI_REG_OP_PORTS_PORTSC_OCA) { + /* Over-current condition */ + status |= (1 << HUB_PORT_FEATURE_OVERCURRENT); + } + + if (portsc & XHCI_REG_OP_PORTS_PORTSC_PR) { + /* Reset is in progress */ + status |= (1 << HUB_PORT_FEATURE_RESET); + } + + if (portsc & XHCI_REG_OP_PORTS_PORTSC_PP) { + /* Port is not power off */ + status |= (1 << HUB_PORT_FEATURE_POWER); + } + memcpy(buf, &status, 4); + break; + default: + break; + } + } + + return ret; +} + +int usbh_get_xhci_devaddr(usbh_pipe_t *pipe) +{ + struct xhci_pipe *ppipe = (struct xhci_pipe *)pipe; + return ppipe->slotid; +} + +int usbh_ep0_pipe_reconfigure(usbh_pipe_t pipe, uint8_t dev_addr, uint8_t ep_mps, uint8_t speed) +{ + int ret = 0; + struct xhci_s *xhci = &xhci_host; + struct xhci_pipe *ppipe = (struct xhci_pipe *)pipe; + int oldmaxpacket = ppipe->maxpacket; /* original max packetsz */ + + if (ep_mps != oldmaxpacket) { + /* maxpacket has changed on control endpoint - update controller. */ + struct xhci_inctx *in = xhci_alloc_inctx(xhci, ppipe->hport, 1); /* allocate input context */ + if (!in) + return -1; + + in->add = (1 << 1); /* update ep0 context */ + + /* + input ctrl context + slot + ep-0 context, offset = 2 * xhci->context64, e.g, for 64 bit (0x40), offset = 0x80 + */ + struct xhci_epctx *ep = (void*)&in[2 << xhci->context64]; /* ep context */ + ep->ctx[1] |= XHCI_EPCTX_1_MPS_SET(ppipe->maxpacket); /* bit[31:16] update maxpacket size */ + int cc = xhci_cmd_evaluate_context(xhci, ppipe, in); + if (cc != CC_SUCCESS) { + USB_LOG_ERR("%s: reconf ctl endpoint: failed (cc %d)\n", + __func__, cc); + ret = -1; + } else { + ppipe->maxpacket = ep_mps; /* mps update success */ + } + + usb_free(in); + } + + return ret; +} + +static int usbh_get_period(int speed, int interval) +{ + if (speed != USB_SPEED_HIGH){ + /* fls - find last (most-significant) bit set */ + return (interval <= 4) ? 0 : fls(interval); + } + + return (interval <= 4) ? 0 : interval - 4; +} + +int usbh_pipe_alloc(usbh_pipe_t *pipe, const struct usbh_endpoint_cfg *ep_cfg) +{ + int ret = 0; + struct xhci_s *xhci = &xhci_host; + struct xhci_pipe *ppipe = usb_align(XHCI_RING_SIZE, sizeof(struct xhci_pipe)); + struct usbh_hubport *hport = ep_cfg->hport; + + uint32_t epid = 0U; + uint8_t eptype = 0U; + + if (ppipe == NULL) { + return -ENOMEM; + } + + memset(ppipe, 0, sizeof(struct xhci_pipe)); + + ppipe->epaddr = ep_cfg->ep_addr; + if (ep_cfg->ep_addr == 0) { + epid = 1; /* ep0, ep_id = 1 */ + } else { + /* refer to spec. Figure 4-4: Endpoint Context Addressing + ep0 = 1, ep1-out = 2, ep1-in = 3, ... ep15-out = 30, ep15-in = 31 */ + epid = (ep_cfg->ep_addr & 0x0f) * 2; + epid += (ep_cfg->ep_addr & USB_EP_DIR_IN) ? 1 : 0; + } + ppipe->epid = epid; + eptype = ep_cfg->ep_type; + ppipe->eptype = eptype; + ppipe->maxpacket = ep_cfg->ep_mps; + ppipe->interval = ep_cfg->ep_interval; + ppipe->speed = ep_cfg->hport->speed; + ppipe->hport = ep_cfg->hport; + ppipe->waitsem = usb_osal_sem_create(0); + ppipe->waiter = false; + ppipe->urb = NULL; + + USB_LOG_DBG("%s epid = %d, eptype = %d, mps = %d, speed = %d, urb = %p\n", + __func__, ppipe->epid, ppipe->eptype, ppipe->maxpacket, + ppipe->speed, ppipe->urb); + + ppipe->reqs.cs = 1; /* cycle state = 1 */ + + /* Allocate input context and initialize endpoint info. */ + struct xhci_inctx *in = xhci_alloc_inctx(xhci, hport, epid); + if (!in){ + ret = -1; + goto fail; + } + + /* add slot context and ep context */ + in->add = 0x01 /* Slot Context */ | (1 << epid); /* EP Context */ + struct xhci_epctx *ep = (void*)&in[(ppipe->epid+1) << xhci->context64]; + if (eptype == USB_ENDPOINT_TYPE_INTERRUPT) + ep->ctx[0] = XHCI_EPCTX_0_INTERVAL_SET(usbh_get_period(ppipe->speed, ppipe->interval) + 3); /* bit[23:16] for interrupt ep, set interval to control interrupt period */ + + /* + Value Endpoint Type Direction + 0 Not Valid N/A + 1 Isoch Out + 2 Bulk Out + 3 Interrupt Out + 4 Control Bidirectional + 5 Isoch In + 6 Bulk In + 7 Interrupt In + */ + ep->ctx[1] |= eptype << 3; /* bit[5:3] endpoint type */ + if (ppipe->epaddr & USB_EP_DIR_IN || eptype == USB_ENDPOINT_TYPE_CONTROL) + ep->ctx[1] |= 1 << 5; /* ep_type 4 ~ 7 */ + + ep->ctx[1] |= XHCI_EPCTX_1_MPS_SET(ppipe->maxpacket); /* bit[31:16] max packet size */ + ep->deq_low = (uint32_t)((unsigned long)&ppipe->reqs.ring[0]); /* bit[63:4] tr dequeue pointer */ + ep->deq_low |= 1; /* bit[0] dequeue cycle state */ +#ifdef XHCI_AARCH64 + ep->deq_high = (uint32_t)((uint64_t)&ppipe->reqs.ring[0] >> 32U); +#else + ep->deq_high = 0U; +#endif + ep->length = XHCI_EPCTX_AVE_TRB_LEN_SET(ppipe->maxpacket); /* bit[15:0] average trb length */ + + if (ppipe->epid == 1) { /* when allocate ep-0, allocate device first */ + struct usbh_hub *hubdev = hport->parent; + if (!hubdev->is_roothub){ + /* make sure parent hub has been configured */ + struct usbh_hubport *hubport = hubdev->parent; + struct xhci_pipe *hubep0 = (struct xhci_pipe *)hubport->ep0; + struct xhci_slotctx *hdslot = (void*)(unsigned long)xhci->devs[hubep0->slotid].ptr_low; + if (XHCI_SLOTCTX_3_SLOT_STATE_GET(hdslot->ctx[3]) != XHCI_SLOT_CONFIG) { /* bit [31:27] slot state, 3 means configured */ + USB_LOG_ERR("%s parent hub-%d not yet configured\n", __func__, hubep0->slotid); + ret = -1; + goto fail; + } + } + + /* enable slot. */ + size_t size = (sizeof(struct xhci_slotctx) * XHCI_SLOTCTX_ENTRY_NUM) << xhci->context64; + struct xhci_slotctx *dev = usb_align(XHCI_SLOTCTX_ALIGMENT << xhci->context64, size); + if (!dev) { + USB_LOG_ERR("allocate memory failed !!!\n"); + ret = -1; + goto fail; + } + + /* send nop command to test if command ring ok */ + for (int i = 0 ; i < 3; ++i) { + int cc = xhci_cmd_nop(xhci, ppipe); + if (cc != CC_SUCCESS) { + USB_LOG_ERR("%s: nop: failed\n", __func__); + usb_free(dev); + ret = -1; + goto fail; + } + } + + int slotid = xhci_cmd_enable_slot(xhci, ppipe); /* get slot-id */ + if (slotid < 0) { + USB_LOG_ERR("%s: enable slot: failed\n", __func__); + usb_free(dev); + ret = -1; + goto fail; + } + + ppipe->slotid = slotid; + USB_LOG_DBG("%s: enable slot: got slotid %d\n", __func__, ppipe->slotid); + memset(dev, 0, size); + xhci->devs[slotid].ptr_low = (uint32_t)(unsigned long)dev; /* DCBAA */ +#ifdef XHCI_AARCH64 + xhci->devs[slotid].ptr_high = (uint32_t)((uint64_t)dev >> 32U); +#else + xhci->devs[slotid].ptr_high = 0; +#endif + + /* Send set_address command. */ + int cc = xhci_cmd_address_device(xhci, ppipe, in); + if (cc != CC_SUCCESS) { + USB_LOG_ERR("%s: address device: failed (cc %d)\n", __func__, cc); + cc = xhci_cmd_disable_slot(xhci, ppipe); + if (cc != CC_SUCCESS) { + USB_LOG_ERR("%s: disable failed (cc %d)\n", __func__, cc); + ret = -1; + goto fail; + } + + xhci->devs[slotid].ptr_low = 0; /* free DCBAA */ + xhci->devs[slotid].ptr_high = 0; + usb_free(dev); + + ret = -1; + goto fail; + } + } else { /* when allocate other ep, config ep */ + struct xhci_pipe *defpipe = (struct xhci_pipe *)hport->ep0; + ppipe->slotid = defpipe->slotid; + + /* Send configure command. */ + int cc = xhci_cmd_configure_endpoint(xhci, defpipe, in); + if (cc != CC_SUCCESS) { + USB_LOG_ERR("%s: configure endpoint: failed (cc %d)\n", __func__, cc); + goto fail; + ret = -1; + } + } + + *pipe = (usbh_pipe_t)ppipe; + +fail: + usb_free(in); + return ret; +} + +int usbh_pipe_free(usbh_pipe_t pipe) +{ + int ret = 0; + struct xhci_s *xhci = &xhci_host; + struct xhci_pipe *ppipe = (struct xhci_pipe *)pipe; + + if (!ppipe) { + return -EINVAL; + } + + struct usbh_urb *urb = ppipe->urb; + + if (urb) { + usbh_kill_urb(urb); + } + + return -1; +} + +int usbh_submit_urb(struct usbh_urb *urb) +{ + int ret = 0; + if (!urb || !urb->pipe) { + return -EINVAL; + } + + struct xhci_s *xhci = &xhci_host; + struct xhci_pipe *ppipe = urb->pipe; + struct usb_setup_packet *setup = urb->setup; + size_t flags; + + flags = usb_osal_enter_critical_section(); + + ppipe->waiter = false; + ppipe->urb = urb; + ppipe->timeout = urb->timeout; + + if (ppipe->timeout > 0) { + ppipe->waiter = true; + } + + usb_osal_leave_critical_section(flags); + switch (ppipe->eptype){ + case USB_ENDPOINT_TYPE_CONTROL: + USB_ASSERT(setup); + if (setup->bRequest == USB_REQUEST_SET_ADDRESS){ + /* Set address command sent during xhci_alloc_pipe. */ + goto skip_req; + } + + /* send setup in/out for command */ + xhci_xfer_setup(xhci, ppipe, setup->bmRequestType & USB_EP_DIR_IN, (void*)setup, + urb->transfer_buffer, urb->transfer_buffer_length); + break; + case USB_ENDPOINT_TYPE_INTERRUPT: + case USB_ENDPOINT_TYPE_BULK: + xhci_xfer_normal(xhci, ppipe, urb->transfer_buffer, urb->transfer_buffer_length); + break; + default: + USB_ASSERT(0U); + break; + } + + /* wait all ring handled by xHc */ + int cc = xhci_event_wait(xhci, ppipe, &ppipe->reqs); + if (cc != CC_SUCCESS) { + USB_LOG_ERR("%s: xfer failed (cc %d)\n", __func__, cc); + ret = -1; + urb->errorcode = cc; + goto errout_timeout; + } + +skip_req: +errout_timeout: + /* Timeout will run here */ + usbh_kill_urb(urb); + return ret; +} + +int usbh_kill_urb(struct usbh_urb *urb) +{ + return 0; +} + +void USBH_IRQHandler(void) +{ + uint32_t reg, status; + struct xhci_s *xhci = &xhci_host; + struct xhci_ring *evts = xhci->evts; + struct xhci_pipe *work_pipe = NULL; + + USB_LOG_DBG("%s\n", __func__); + + status = readl(xhci->op + XHCI_REG_OP_USBSTS); + status |= XHCI_REG_OP_USBSTS_EINT; /* clear interrupt status */ + writel(xhci->op + XHCI_REG_OP_USBSTS, status); + + reg = readl(xhci->ir + XHCI_REG_RT_IR_IMAN); + reg |= XHCI_REG_RT_IR_IMAN_IP; /* ack interrupt */ + writel(xhci->ir + XHCI_REG_RT_IR_IMAN, reg); + + /* check and ack event */ + for (;;) { + uint32_t nidx = evts->nidx; /* index of dequeue trb */ + uint32_t cs = evts->cs; /* cycle state toggle by xHc */ + struct xhci_trb *etrb = evts->ring + nidx; /* current trb */ + uint32_t control = etrb->control; /* trb control field */ + + if ((control & TRB_C) != (cs ? 1 : 0)) /* if cycle state not toggle, no events need to handle */ + break; + + /* process event on etrb */ + uint32_t evt_type = TRB_TYPE_GET(control); + uint32_t evt_cc = TRB_CC_GET(etrb->status); /* bit[31:24] completion code */ + + if (ER_PORT_STATUS_CHANGE == evt_type) { + /* bit[31:24] port id, the port num of root hub port that generated this event */ + uint32_t port = TRB_PORT_ID_GET(etrb->ptr_low) - 1; + uint32_t portsc = xhci_readl_port(xhci, port, XHCI_REG_OP_PORTS_PORTSC); /* Read status */ + + xhci_print_port_state(__func__, port, portsc); + if (portsc & XHCI_REG_OP_PORTS_PORTSC_CSC) { + usbh_roothub_thread_wakeup(port + 1); /* wakeup when connection status changed */ + } + } else if ((ER_COMMAND_COMPLETE == evt_type) || (ER_TRANSFER_COMPLETE == evt_type)) { + struct xhci_trb *rtrb = (void*)(unsigned long)etrb->ptr_low; + struct xhci_ring *ring = XHCI_RING(rtrb); /* to align addr is ring base */ + struct xhci_trb *evt = &ring->evt; /* first event trb */ + uint32_t eidx = rtrb - ring->ring + 1; /* calculate current evt trb index */ + + memcpy(evt, etrb, sizeof(*etrb)); /* copy current trb to cmd/transfer ring */ + ring->eidx = eidx; + + if (ER_COMMAND_COMPLETE == evt_type) { + work_pipe = (struct xhci_pipe *)cur_cmd_pipe; + } else if (ER_TRANSFER_COMPLETE == evt_type) { + /* xhci_pipe begin with reqs ring, therefore we get pipe instance from reqs ring */ + work_pipe = (struct xhci_pipe *)(void *)ring; + } + + if (work_pipe) { + if (work_pipe->waiter) { + work_pipe->waiter = false; + usb_osal_sem_give(work_pipe->waitsem); + } + + if (work_pipe->urb) { + struct usbh_urb *cur_urb = work_pipe->urb; + cur_urb->errorcode = evt_cc; + /* bit [23:0] TRB Transfer length, residual number of bytes not transferred + for OUT, is the value of (len of trb) - (data bytes transmitted), '0' means successful + for IN, is the value of (len of trb) - (data bytes received), + if cc is Short Packet, value is the diff between expected trans size and actual recv bytes + if cc is other error, value is the diff between expected trans size and actual recv bytes */ + cur_urb->actual_length += cur_urb->transfer_buffer_length - + (evt->status) & 0xffffff; /* bit [23:0] */ + } + } + } else { + USB_LOG_ERR("%s: unknown event, type %d, cc %d\n", + __func__, evt_type, evt_cc); + } + + /* move ring index, notify xhci */ + nidx++; /* head to next trb */ + if (nidx == XHCI_RING_ITEMS) { + nidx = 0; /* roll-back if reach end of list */ + cs = cs ? 0 : 1; + evts->cs = cs; /* sw toggle cycle state */ + } + evts->nidx = nidx; + uint64_t erdp = (uint64_t)(evts->ring + nidx); + writeq(xhci->ir + XHCI_REG_RT_IR_ERDP, erdp | XHCI_REG_RT_IR_ERDP_EHB); /* bit[63:4] update current event ring dequeue pointer */ + } + + /* handle callbacks in interrupt */ + if ((work_pipe) && (work_pipe->urb)) { + struct usbh_urb *cur_urb = work_pipe->urb; + if (cur_urb->complete) { + if (cur_urb->errorcode < 0) { + cur_urb->complete(cur_urb->arg, cur_urb->errorcode); + } else { + cur_urb->complete(cur_urb->arg, cur_urb->actual_length); + } + } + } + + return; +} \ No newline at end of file diff --git a/port/xhci/usb_hc_xhci.h b/port/xhci/usb_hc_xhci.h new file mode 100644 index 00000000..a86b4a9b --- /dev/null +++ b/port/xhci/usb_hc_xhci.h @@ -0,0 +1,175 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: usb_hc_xhci.h + * Date: 2022-07-19 09:26:25 + * LastEditTime: 2022-07-19 09:26:25 + * Description:  This files is for xhci data structure definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022/9/19 init commit + */ +#ifndef USB_HC_XHCI_H +#define USB_HC_XHCI_H + +/***************************** Include Files *********************************/ +#include "usbh_core.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/************************** Constant Definitions *****************************/ + +/************************** Type Definitions *****************************/ +/* slot context */ +struct xhci_slotctx { + uint32_t ctx[4]; +#define XHCI_SLOTCTX_0_MAX_EPID_SET(maxid) XHCI32_SET_BITS(maxid, 31, 27) +#define XHCI_SLOTCTX_0_SPEED_SET(speed) XHCI32_SET_BITS(speed, 23, 20) +#define XHCI_SLOTCTX_1_ROOT_PORT_GET(val) XHCI32_GET_BITS(val, 23, 16) +#define XHCI_SLOTCTX_1_ROOT_PORT_SET(port) XHCI32_SET_BITS(port, 23, 16) +#define XHCI_SLOTCTX_0_ROUTE_SET(route) XHCI32_SET_BITS(route, 19, 0) +#define XHCI_SLOTCTX_2_HUB_SLOT_SET(slot) XHCI32_SET_BITS(slot, 7, 0) +#define XHCI_SLOTCTX_2_HUB_PORT_SET(port) XHCI32_SET_BITS(port, 15, 8) +#define XHCI_SLOTCTX_3_SLOT_STATE_GET(ctx) XHCI32_GET_BITS(ctx, 31, 27) + uint32_t reserved_01[4]; +#define XHCI_SLOTCTX_ENTRY_NUM 32U +#define XHCI_SLOTCTX_ALIGMENT 1024U +} __PACKED; + +enum xhci_slot_state { + XHCI_SLOT_DEFAULT = 1, + XHCI_SLOT_ADDRESS = 2, + XHCI_SLOT_CONFIG = 3 +}; + +/* endpoint context */ +struct xhci_epctx { + uint32_t ctx[2]; +#define XHCI_EPCTX_0_INTERVAL_SET(interval) XHCI32_SET_BITS(interval, 23, 16) +#define XHCI_EPCTX_1_MPS_SET(mps) XHCI32_SET_BITS(mps, 31, 16) + uint32_t deq_low; + uint32_t deq_high; + uint32_t length; +#define XHCI_EPCTX_AVE_TRB_LEN_SET(len) XHCI32_SET_BITS(len, 15, 0) + uint32_t reserved_01[3]; +} __PACKED; + +/* device context array element */ +struct xhci_devlist { + uint32_t ptr_low; + uint32_t ptr_high; +} __PACKED; + +/* input context */ +struct xhci_inctx { + uint32_t del; + uint32_t add; + uint32_t reserved_01[6]; +/* refer to spec. The Input Context is an array of up to 33 context data structure entries */ +#define XHCI_INCTX_ENTRY_NUM 33U +#define XHCI_INCTX_ALIGMENT 2048 +} __PACKED; + +/* transfer block (ring element) */ +struct xhci_trb { + uint32_t ptr_low; + uint32_t ptr_high; + uint32_t status; + uint32_t control; +} __PACKED; + +/* event ring segment */ +struct xhci_er_seg { + uint32_t ptr_low; + uint32_t ptr_high; + uint32_t size; + uint32_t reserved_01; +} __PACKED; + +struct xhci_portmap { + uint8_t start; + uint8_t count; +}; + +struct xhci_ring { + struct xhci_trb ring[XHCI_RING_ITEMS]; + struct xhci_trb evt; + uint32_t eidx; + uint32_t nidx; + uint32_t cs; + usb_osal_mutex_t lock; +}; + +struct xhci_pipe { + struct xhci_ring reqs; /* DO NOT MOVE reqs from structure beg */ + uint8_t epaddr; + uint8_t speed; + uint8_t interval; + uint8_t eptype; + uint16_t maxpacket; + uint32_t slotid; + uint32_t epid; + + /* command or transfer waiter */ + int timeout; /* = 0 no need to wait */ + bool waiter; + usb_osal_sem_t waitsem; + + /* handle urb */ + struct usbh_hubport *hport; + struct usbh_urb *urb; /* NULL if no active URB */ +}; + +struct xhci_s { + /* devinfo */ + uint32_t ports; + uint32_t slots; + bool context64; + struct xhci_portmap usb2; + struct xhci_portmap usb3; + + /* xhci registers base addr */ + unsigned long base; /* register base */ + unsigned long caps; /* capability register base */ + unsigned long op; /* operational register base */ + unsigned long pr; /* port register base */ + unsigned long ir; /* interrupt runtime register base */ + unsigned long db; /* doorbell register base */ + unsigned long xcap; /* extended capability */ + uint32_t hcs[3]; /* capability cache */ + uint32_t hcc; + uint16_t version; /* xhci version */ + + /* xhci data structures */ + struct xhci_devlist *devs; + struct xhci_ring *cmds; + struct xhci_ring *evts; + struct xhci_er_seg *eseg; +}; + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/port/xhci/xhci_reg.h b/port/xhci/xhci_reg.h new file mode 100644 index 00000000..7b087409 --- /dev/null +++ b/port/xhci/xhci_reg.h @@ -0,0 +1,528 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: xhci_reg.h + * Date: 2022-07-19 09:26:25 + * LastEditTime: 2022-07-19 09:26:25 + * Description:  This files is for xhci register definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022/9/19 init commit + */ +#ifndef XHCI_REG_H +#define XHCI_REG_H + +/***************************** Include Files *********************************/ +#include "usbh_core.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/************************** Constant Definitions *****************************/ +#if defined(__aarch64__) +#define BITS_PER_LONG 64U +#define XHCI_AARCH64 +#else +#define BITS_PER_LONG 32U +#define XHCI_AARCH32 +#endif + +#define XHCI_GENMASK(h, l) \ + (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) + +#define XHCI_GENMASK_ULL(h, l) \ + (((~0ULL) - (1ULL << (l)) + 1) & \ + (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h)))) + +#define XHCI32_GET_BITS(x, a, b) (uint32_t)((((uint32_t)(x)) & XHCI_GENMASK(a, b)) >> b) +#define XHCI32_SET_BITS(x, a, b) (uint32_t)((((uint32_t)(x)) << b) & XHCI_GENMASK(a, b)) +#define XHCI64_GET_BITS(x, a, b) (uint64_t)((((uint64_t)(x)) & XHCI_GENMASK_ULL(a, b)) >> b) +#define XHCI64_SET_BITS(x, a, b) (uint64_t)((((uint64_t)(x)) << b) & XHCI_GENMASK_ULL(a, b)) + +/** @name Register Map + * + * Register offsets from the base address of an XHCI device. + * @{ + */ +#define XHCI_REG_CAP_CAPLENGTH 0x00 /* specify the limits, restrictions and capabilities */ +#define XHCI_REG_CAP_HCIVERSION 0x02 /* Interface Version Number */ +#define XHCI_REG_CAP_HCS1 0x04 /* Host Controller Structural Parameters 1 */ +#define XHCI_REG_CAP_HCS2 0x08 /* Host Controller Structural Parameters 2 */ +#define XHCI_REG_CAP_HCS3 0x0C /* Host Controller Structural Parameters 3 */ +#define XHCI_REG_CAP_HCC 0x10 /* Capability Parameters 1 */ +#define XHCI_REG_CAP_DBOFF 0x14 /* Doorbell Offset Register */ +#define XHCI_REG_CAP_RTSOFF 0x18 /* Runtime Register Space Offset Register */ + +/***************** Host Controller Operational Registers ***********************/ +#define XHCI_REG_OP_USBCMD 0x00 /* USB Command Register */ +#define XHCI_REG_OP_USBSTS 0x04 /* USB Status Register */ +#define XHCI_REG_OP_PAGESIZE 0x08 /* Page Size Register */ +#define XHCI_REG_OP_DNCTRL 0x14 /* Device Notification Control Register */ +#define XHCI_REG_OP_CRCR 0x18 /* Command Ring Control Register */ +#define XHCI_REG_OP_DCBAAP 0x30 /* Device Context Base Address Array Pointer Register */ +#define XHCI_REG_OP_CONFIG 0x38 /* Configure Register */ + +/* Port Status and Ctrl Register : OP Base + (400h + (10h * (n–1))) 'n' is port num */ +#define XHCI_REG_OP_PORTS_BASE 0x400 /* Port Status and Control Register Base */ +#define XHCI_REG_OP_PORTS_SIZE 0x10 /* Size of one Port SC Register */ +#define XHCI_REG_OP_PORTS_OFF(port, off) ((port) * XHCI_REG_OP_PORTS_SIZE + offset) + +#define XHCI_REG_OP_PORTS_PORTSC 0x00 /* Port Status and Control Register */ +#define XHCI_REG_OP_PORTS_PORTPMSC 0x04 /* USB3 Port Power Management Status and Control Register */ +#define XHCI_REG_OP_PORTS_PORTLI 0x08 /* Port Link Info Register */ + +/***************** Host Controller Runtime Registers ***********************/ +#define XHCI_REG_RT_MFINDEX 0x00 /* Microframe Index */ +#define XHCI_REG_RT_IR0 0x20 /* Interrupter Register Set 0 */ +#define XHCI_REG_RT_IR1023 0x8000 /* Interrupter Register Set 1023 */ + +/* Interrupter Register Set : RT Base + 020h + (32 * Interrupter) */ +#define XHCI_REG_RT_IR_IMAN 0x00 /* Interrupter Management Register */ +#define XHCI_REG_RT_IR_IMOD 0x04 /* Interrupter Moderation Register */ +#define XHCI_REG_RT_IR_ERSTSZ 0x08 /* Event Ring Segment Table Size Register */ +#define XHCI_REG_RT_IR_ERSTBA 0x10 /* Event Ring Segment Table Base Address Register */ +#define XHCI_REG_RT_IR_ERDP 0x18 /* Event Ring Dequeue Pointer Register */ +#define XHCI_REG_RT_IR_SIZE 0x20 /* Size of one IR Register */ + +/***************** Doorbell Register ***********************/ +#define XHCI_REG_DB_SIZE 4 /* Doorbell registers are 32 bits in length */ + +/***************** eXtensible Host Controller Capability Registers ***********************/ + +/** @name XHCI_REG_CAP_HCS1 Register + */ +#define XHCI_REG_CAP_HCS1_MAX_SLOTS_GET(x) XHCI32_GET_BITS(x, 7, 0) /* Number of Device Slots (MaxSlots) */ +#define XHCI_REG_CAP_HCS1_MAX_INTRS_GET(x) XHCI32_GET_BITS(x, 18, 8) /* Number of Interrupters (MaxIntrs) */ +#define XHCI_REG_CAP_HCS1_MAX_PORTS_GET(x) XHCI32_GET_BITS(x, 31, 24) /* Number of Ports (MaxPorts) */ + +/** @name XHCI_REG_CAP_HCS2 Register + */ +#define XHCI_REG_CAP_HCS2_IST_GET(x) XHCI32_GET_BITS(x, 3, 0) /* Isochronous Scheduling Threshold (IST) */ +#define XHCI_REG_CAP_HCS2_ERST_MAX_GET(x) XHCI32_GET_BITS(x, 7, 4) /* Event Ring Segment Table Max (ERST Max) */ +#define XHCI_REG_CAP_HCS2_SPR (1 << 26) /* Scratchpad Restore (SPR) */ +#define XHCI_REG_CAP_HCS2_MAX_SCRATCHPAD_BUFS_GET(x) XHCI32_GET_BITS(x, 25, 21) | XHCI32_GET_BITS(x, 31, 27) /* Max Scratchpad Buffers (Max Scratchpad Bufs) */ + +/** @name XHCI_REG_CAP_HCS3 Register + */ +#define XHCI_REG_CAP_HCS3_U1_DEV_EXIT_LATENCY_GET(x) XHCI32_GET_BITS(x, 7, 0) /* U1 Device Exit Latency */ +#define XHCI_REG_CAP_HCS3_U2_DEV_EXIT_LATENCY_GET(x) XHCI32_GET_BITS(x, 31, 16) /* U2 Device Exit Latency */ + +/** @name XHCI_REG_CAP_HCC Register + */ +#define XHCI_REG_CAP_HCC_AC64 (1 << 0) /* 64-bit Addressing Capabilitya 1: 64-bit */ +#define XHCI_REG_CAP_HCC_BNC (1 << 1) /* BW Negotiation Capability (BNC) 1: support */ +#define XHCI_REG_CAP_HCC_CSZ (1 << 2) /* Context Size (CSZ) 1: 64 byte context data */ +#define XHCI_REG_CAP_HCC_PPC (1 << 3) /* Port Power Control (PPC) 1: support */ +#define XHCI_REG_CAP_HCC_PIND (1 << 4) /* Port Indicators (PIND) 1: support */ +#define XHCI_REG_CAP_HCC_LHRC (1 << 5) /* Light HC Reset Capability (LHRC) 1: support */ +#define XHCI_REG_CAP_HCC_LTC (1 << 6) /* Latency Tolerance Messaging Capability (LTC) */ +#define XHCI_REG_CAP_HCC_NSS (1 << 7) /* No Secondary SID Support (NSS) */ +#define XHCI_REG_CAP_HCC_MAX_PSA_SIZE_GET(x) XHCI32_GET_BITS(x, 15, 12) /* Maximum Primary Stream Array Size (MaxPSASize) */ +#define XHCI_REG_CAP_HCC_XECP_GET(x) XHCI32_GET_BITS(x, 31, 16) /* xHCI Extended Capabilities Pointer (xECP) */ + +/** @name XHCI_REG_CAP_DBOFF Register + */ +#define XHCI_REG_CAP_DBOFF_GET(x) ((x) & XHCI_GENMASK(31, 2)) /* 32-byte offset of the Doorbell Array base address from the Base */ + +/** @name XHCI_REG_CAP_RTSOFF Register + */ +#define XHCI_REG_CAP_RTSOFF_GET(x) ((x) & XHCI_GENMASK(31, 5)) /* 32-byte offset of the xHCI Runtime Registers */ + + +/***************** Host Controller Operational Registers ***********************/ + +/** @name XHCI_REG_OP_USBCMD Register + */ +#define XHCI_REG_OP_USBCMD_RUN_STOP (1 << 0) /* Run/Stop (R/S) 1: RUN, 0: STOP - RW */ +#define XHCI_REG_OP_USBCMD_HCRST (1 << 1) /* Host Controller Reset (HCRST) 1: RESET - RW */ +#define XHCI_REG_OP_USBCMD_INTE (1 << 2) /* Interrupter Enable (INTE) 1: enabled - RW */ +#define XHCI_REG_OP_USBCMD_HSEE (1 << 3) /* Host System Error Enable (HSEE) - RW */ +#define XHCI_REG_OP_USBCMD_LHCRST (1 << 7) /* Light Host Controller Reset (LHCRST) - RW */ +#define XHCI_REG_OP_USBCMD_CSS (1 << 8) /* Controller Save State (CSS) - RW */ +#define XHCI_REG_OP_USBCMD_CRS (1 << 9) /* Controller Restore State (CRS) - RW */ +#define XHCI_REG_OP_USBCMD_EWE (1 << 10) /* Enable Wrap Event (EWE) - RW */ +#define XHCI_REG_OP_USBCMD_EU3S (1 << 11) /* Enable U3 MFINDEX Stop (EU3S) - RW */ + +/** @name XHCI_REG_OP_USBSTS Register + */ +#define XHCI_REG_OP_USBSTS_HCH (1 << 0) /* 1: Stopped executing */ +#define XHCI_REG_OP_USBSTS_HSE (1 << 2) /* 1: Serious error detected */ +#define XHCI_REG_OP_USBSTS_EINT (1 << 3) /* 1: Interrupt Pending (IP) */ +#define XHCI_REG_OP_USBSTS_PCD (1 << 4) /* 1: Port Change Detect */ +#define XHCI_REG_OP_USBSTS_SSS (1 << 8) /* remain 1 while the xHC saves its internal state */ +#define XHCI_REG_OP_USBSTS_RSS (1 << 9) /* remain 1 while the xHC restores its internal state */ +#define XHCI_REG_OP_USBSTS_SRE (1 << 10) /* if error occurs during a Save or Restore operation this bit shall be set to ‘1’. */ +#define XHCI_REG_OP_USBSTS_CNR (1 << 11) /* 1: Controller Not Ready */ +#define XHCI_REG_OP_USBSTS_HCE (1 << 12) /* 1: Internal xHC error condition */ +#define XHCI_REG_OP_USBSTS_PRSRV_MASK ((1 << 1) | 0xffffe000) /* Rsvd bits */ + + +/** @name XHCI_REG_OP_PAGESIZE Register + */ +/* This xHC supports a page size of 2^(n+12) if bit n is Set */ +#define XHCI_REG_OP_PAGESIZE_4K (1 << 0) /* if bit 0 is Set, the xHC supports 4k byte page sizes */ + +/** @name XHCI_REG_OP_CRCR Register + */ +#define XHCI_REG_OP_CRCR_RCS (1 << 0) /* Ring Cycle State, value of the xHC Consumer Cycle State (CCS) flag */ +#define XHCI_REG_OP_CRCR_CS (1 << 1) /* Command Stop, 1 */ +#define XHCI_REG_OP_CRCR_CA (1 << 2) /* Command Abort, 1 */ +#define XHCI_REG_OP_CRCR_CRR (1 << 3) /* Command Ring Running */ +#define XHCI_REG_OP_CRCR_CR_PTR_MASK XHCI_GENMASK_ULL(63, 6) /* Command Ring Pointer, Dequeue Ptr of Command Ring */ + +/** @name XHCI_REG_OP_DCBAAP Register + */ +#define XHCI_REG_OP_DCBAAP_MASK XHCI_GENMASK_ULL(63, 6) /* bit[31:6] Ptr of DCBAA */ + +/** @name XHCI_REG_OP_CONFIG Register + */ +#define XHCI_REG_OP_CONFIG_MAX_SLOTS_EN_MASK XHCI_GENMASK(7, 0) /* Max Device Slots Enabled (MaxSlotsEn) – RW */ +#define XHCI_REG_OP_CONFIG_MAX_SLOTS_EN_SET(x) XHCI32_SET_BITS(x, 7, 0) /* bit[7:0] Max Device Slots Enabled */ +#define XHCI_REG_OP_CONFIG_MAX_SLOTS_EN_GET(x) XHCI32_GET_BITS(x, 7, 0) + +/** @name XHCI_REG_OP_PORTS_PORTSC Register + */ +#define XHCI_REG_OP_PORTS_PORTSC_CCS (1 << 0) /* Current Connect Status (CCS) – ROS */ +#define XHCI_REG_OP_PORTS_PORTSC_PED (1 << 1) /* Port Enabled/Disabled (PED) – RW1CS */ +#define XHCI_REG_OP_PORTS_PORTSC_OCA (1 << 3) /* Over-current Active (OCA) – RO */ +#define XHCI_REG_OP_PORTS_PORTSC_PR (1 << 4) /* Port Reset (PR) – RW1S */ +#define XHCI_REG_OP_PORTS_PORTSC_PLS_GET(x) XHCI32_GET_BITS(x, 8, 5) /* Port Link State (PLS) – RWS */ +#define XHCI_REG_OP_PORTS_PORTSC_PLS_SET(x) XHCI32_SET_BITS(x, 8, 5) +#define XHCI_REG_OP_PORTS_PORTSC_PLS_MASK XHCI_GENMASK(8, 5) +#define XHCI_REG_OP_PORTS_PORTSC_PLS(x) (x << 5) +#define XHCI_REG_OP_PORTS_PORTSC_PLS_SET(x) XHCI32_SET_BITS(x, 8, 5) + +enum PLSStatus{ + PLS_U0 = 0, + PLS_U1 = 1, + PLS_U2 = 2, + PLS_U3 = 3, + PLS_DISABLED = 4, + PLS_RX_DETECT = 5, + PLS_INACTIVE = 6, + PLS_POLLING = 7, + PLS_RECOVERY = 8, + PLS_HOT_RESET = 9, + PLS_COMPILANCE_MODE = 10, + PLS_TEST_MODE = 11, + PLS_RESUME = 15, +}; /* Port status type */ + +#define XHCI_REG_OP_PORTS_PORTSC_PP (1 << 9) /* Port Power (PP) – RWS */ +#define XHCI_REG_OP_PORTS_PORTSC_PORT_SPEED_GET(x) XHCI32_GET_BITS(x, 13, 10) /* Port Speed (Port Speed) – ROS */ + +/* Protocol Speed ID (PSI) */ +#define XHCI_PORT_SPEED_UNKOWN 0U +#define XHCI_PORT_SPEED_FULL 1U +#define XHCI_PORT_SPEED_LOW 2U +#define XHCI_PORT_SPEED_HIGH 3U +#define XHCI_PORT_SPEED_SUPER 4U + +#define XHCI_REG_OP_PORTS_PORTSC_PIC_SET(x) XHCI32_SET_BITS(x, 15, 14) +#define XHCI_REG_OP_PORTS_PORTSC_PIC_MASK XHCI_GENMASK(15, 14) + +#define XHCI_REG_OP_PORTS_PORTSC_LWS (1 << 16) /* Port Link State Write Strobe (LWS) */ +#define XHCI_REG_OP_PORTS_PORTSC_CSC (1 << 17) /* Connect Status Change (CSC) */ +#define XHCI_REG_OP_PORTS_PORTSC_PEC (1 << 18) /* Port Enabled/Disabled Change (PEC) 1: clear PED */ +#define XHCI_REG_OP_PORTS_PORTSC_WRC (1 << 19) /* Warm Port Reset Change 1: Warm Reset complete */ +#define XHCI_REG_OP_PORTS_PORTSC_OCC (1 << 20) /* Over-current Change 1: Over-current Active */ +#define XHCI_REG_OP_PORTS_PORTSC_PRC (1 << 21) /* Port Reset Change 1: Transition of Port Reset */ +#define XHCI_REG_OP_PORTS_PORTSC_PLC (1 << 22) /* Port Link State Change 1: PLS transition */ +#define XHCI_REG_OP_PORTS_PORTSC_CEC (1 << 23) /* Port Config Error Change 1: Port Config Error detected */ +#define XHCI_REG_OP_PORTS_PORTSC_CAS (1 << 24) /* Cold Attach Status 1: Far-end Receiver Terminations were detected */ +#define XHCI_REG_OP_PORTS_PORTSC_WCE (1 << 25) /* Wake on Connect Enable 1: enable port to be sensitive to device connects */ +#define XHCI_REG_OP_PORTS_PORTSC_WDE (1 << 26) /* Wake on Disconnect Enable 1: enable port to be sensitive to device disconnects */ +#define XHCI_REG_OP_PORTS_PORTSC_WOE (1 << 27) /* Wake on Over-current Enable 1: enable port to be sensitive to over-current conditions */ +#define XHCI_REG_OP_PORTS_PORTSC_DR (1 << 30) /* Device Removable, 0: Device is removable. 1: Device is non-removable */ +#define XHCI_REG_OP_PORTS_PORTSC_WPR (1 << 31) /* Warm Port Reset 1: follow Warm Reset sequence */ +#define XHCI_REG_OP_PORTS_PORTSC_RW_MASK (XHCI_REG_OP_PORTS_PORTSC_PR | XHCI_REG_OP_PORTS_PORTSC_PLS_MASK | XHCI_REG_OP_PORTS_PORTSC_PP \ + | XHCI_REG_OP_PORTS_PORTSC_PIC_MASK | XHCI_REG_OP_PORTS_PORTSC_LWS | XHCI_REG_OP_PORTS_PORTSC_WCE \ + | XHCI_REG_OP_PORTS_PORTSC_WDE | XHCI_REG_OP_PORTS_PORTSC_WOE) + + +/***************** Host Controller Runtime Registers ***********************/ + +/** @name XHCI_REG_RT_IR_IMAN Register + */ +#define XHCI_REG_RT_IR_IMAN_IP (1 << 0) /* Interrupt Pending, 1: an interrupt is pending for this Interrupter */ +#define XHCI_REG_RT_IR_IMAN_IE (1 << 1) /* Interrupt Enable, 1: capable of generating an interrupt. */ + +/** @name XHCI_REG_RT_IR_IMOD Register + */ +#define XHCI_REG_RT_IR_IMOD_IMODI_MASK XHCI_GENMASK(15, 0) /* bit[15:0] Interrupt Moderation Interval default 4000 ==> 1ms */ +#define XHCI_REG_RT_IR_IMOD_IMODC_MASK XHCI_GENMASK(31, 16) /* bit[31:16] Interrupt Moderation Counter(Down counter) */ + +/** @name XHCI_REG_RT_IR_ERSTSZ Register + */ +#define XHCI_REG_RT_IR_ERSTSZ_MASK XHCI_GENMASK(15, 0) /* bit[15:0] the number of valid Event Ring Segment Table entries */ + +/** @name XHCI_REG_RT_IR_ERSTBA Register + */ +#define XHCI_REG_RT_IR_ERSTBA_MASK XHCI_GENMASK_ULL(63, 6) /* Event Ring Segment Table Base Address */ + +/** @name XHCI_REG_RT_IR_ERDP Register + */ +#define XHCI_REG_RT_IR_ERDP_DESI_MASK XHCI_GENMASK_ULL(2, 0) /* bit[2:0] Dequeue ERST Segment Index */ +#define XHCI_REG_RT_IR_ERDP_EHB (1 << 3) /* Event Handler Busy */ +#define XHCI_REG_RT_IR_ERDP_MASK XHCI_GENMASK_ULL(63, 4) /* Event Ring Dequeue Pointer */ + +/***************** Doorbell Register ***********************/ +#define XHCI_REG_DB_TARGET_HC_COMMAND 0 /* Host Controller Doorbell (0) Command Doorbell */ +#define XHCI_REG_DB_TARGET_EP0 1 /* Device Context Doorbells Control EP 0 Enqueue Pointer Update */ +#define XHCI_REG_DB_TARGET_EP1_OUT 2 /* EP 1 OUT Enqueue Pointer Update */ +#define XHCI_REG_DB_TARGET_EP1_IN 3 /* EP 1 IN Enqueue Pointer Update */ +#define XHCI_REG_DB_TARGET_EP15_OUT 30 /* EP 15 OUT Enqueue Pointer Update */ +#define XHCI_REG_DB_TARGET_EP15_IN 31 /* EP 15 IN Enqueue Pointer Update */ + +/***************** xHCI Extended Capabilities Registers ***********************/ +#define XHCI_REG_EXT_CAP_USBSPCF_OFFSET 0x0 +#define XHCI_REG_EXT_CAP_CAP_ID_GET(x) XHCI32_GET_BITS(x, 7, 0) +#define XHCI_REG_EXT_NEXT_CAP_PTR_GET(x) XHCI32_GET_BITS(x, 15, 8) +/* refer to 'Table 138: xHCI Extended Capability Codes' for more details */ +enum +{ + XHCI_EXT_CAP_ID_USB_LEGACY_SUPPORT = 1, + XHCI_EXT_CAP_ID_SUPPORT_PROTOCOL = 2, + XHCI_EXT_CAP_ID_EXTEND_POWER_MANAGEMENT = 3, + XHCI_EXT_CAP_ID_IO_VIRTUALIZATION = 4, + XHCI_EXT_CAP_ID_MESSAGE_INTERRUPT = 5, + XHCI_EXT_CAP_ID_LOCAL_MEMORY = 6, + XHCI_EXT_CAP_ID_USB_DEBUG_CAPABILITY = 10, + XHCI_EXT_CAP_ID_EXT_MESSAGE_INTERRUPT = 17, + + XHCI_EXT_CAP_ID_VENDOR_DEFINED_MIN = 192, + XHCI_EXT_CAP_ID_VENDOR_DEFINED_MAX = 255 +}; + +/* xHCI Supported Protocol Capability */ +#define XHCI_REG_EXT_CAP_USBSPCFDEF_OFFSET 0x4 + +#define XHCI_USBSPCF_MINOR_REVERSION_GET(x) XHCI32_GET_BITS(x, 23, 16) +#define XHCI_USBSPCF_MAJOR_REVERSION_GET(x) XHCI32_GET_BITS(x, 31, 24) + +#define XHCI_USBSPCFDEF_NAME_STRING_GET(x) XHCI32_GET_BITS(x, 31, 0) /* four ASCII characters may be defined */ +#define XHCI_USBSPCFDEF_NAME_STRING_USB 0x20425355 /* ASCII = "USB" */ + +#define XHCI_REG_EXT_CAP_USBSPCFDEF2_OFFSET 0x8 +#define XHCI_USBSPCFDEF2_COMPATIBLE_PORT_OFF_GET(x) XHCI32_GET_BITS(x, 7, 0) +#define XHCI_USBSPCFDEF2_COMPATIBLE_PORT_CNT_GET(x) XHCI32_GET_BITS(x, 15, 8) +#define XHCI_USBSPCFDEF2_PROTOCOL_DEFINED_GET(x) XHCI32_GET_BITS(x, 27, 16) + +/* trb bit definitions */ + +/* configuration */ +#define XHCI_RING_ITEMS 16U +#define XHCI_ALIGMENT 64U +#define XHCI_RING_SIZE (XHCI_RING_ITEMS*sizeof(struct xhci_trb)) + +#define TRB_C (1<<0) +#define TRB_TYPE_SHIFT 10 +#define TRB_TYPE_MASK 0x3f +#define TRB_TYPE_GET(val) XHCI32_GET_BITS(val, 15, 10) +#define TRB_TYPE_SET(t) XHCI32_SET_BITS(t, 15, 10) + +#define TRB_EV_ED (1<<2) + +#define TRB_TR_ENT (1<<1) +#define TRB_TR_ISP (1<<2) +#define TRB_TR_NS (1<<3) +#define TRB_TR_CH (1<<4) +#define TRB_TR_IOC (1<<5) +#define TRB_TR_IDT (1<<6) +#define TRB_TR_TBC_SHIFT 7 +#define TRB_TR_TBC_MASK 0x3 +#define TRB_TR_BEI (1<<9) +#define TRB_TR_TLBPC_SHIFT 16 +#define TRB_TR_TLBPC_MASK 0xf +#define TRB_TR_FRAMEID_SHIFT 20 +#define TRB_TR_FRAMEID_MASK 0x7ff +#define TRB_TR_SIA (1<<31) + +#define TRB_TR_DIR (1<<16) +#define TRB_TR_TYPE_SET(t) XHCI32_SET_BITS(t, 17, 16) +#define TRB_TR_NO_DATA 0U +#define TRB_TR_OUT_DATA 2U +#define TRB_TR_IN_DATA 3U + +#define TRB_CC_GET(val) XHCI32_GET_BITS(val, 31, 24) +#define TRB_PORT_ID_GET(val) XHCI32_GET_BITS(val, 31, 24) + + +#define TRB_CR_SLOTID_SHIFT 24 +#define TRB_CR_SLOTID_MASK 0xff +#define TRB_CR_SLOTID_SET(id) XHCI32_SET_BITS(id, 31, 24) +#define TRB_CR_SLOTID_GET(val) XHCI32_GET_BITS(val, 31, 24) + +#define TRB_CR_EPID_SHIFT 16 +#define TRB_CR_EPID_MASK 0x1f + +#define TRB_CR_BSR (1<<9) +#define TRB_CR_DC (1<<9) + +#define TRB_LK_TC (1<<1) + +#define TRB_INTR_SHIFT 22 +#define TRB_INTR_MASK 0x3ff +#define TRB_INTR(t) (((t).status >> TRB_INTR_SHIFT) & TRB_INTR_MASK) + +/************************** Type Definitions *********************************/ +enum TRBType { + TRB_RESERVED = 0, + TR_NORMAL, + TR_SETUP, + TR_DATA, + TR_STATUS, + TR_ISOCH, + TR_LINK, + TR_EVDATA, + TR_NOOP, + CR_ENABLE_SLOT, + CR_DISABLE_SLOT, + CR_ADDRESS_DEVICE, + CR_CONFIGURE_ENDPOINT, + CR_EVALUATE_CONTEXT, + CR_RESET_ENDPOINT, + CR_STOP_ENDPOINT, + CR_SET_TR_DEQUEUE, + CR_RESET_DEVICE, + CR_FORCE_EVENT, + CR_NEGOTIATE_BW, + CR_SET_LATENCY_TOLERANCE, + CR_GET_PORT_BANDWIDTH, + CR_FORCE_HEADER, + CR_NOOP, + ER_TRANSFER_COMPLETE = 32, + ER_COMMAND_COMPLETE, + ER_PORT_STATUS_CHANGE, + ER_BANDWIDTH_REQUEST, + ER_DOORBELL, + ER_HOST_CONTROLLER, + ER_DEVICE_NOTIFICATION, + ER_MFINDEX_WRAP, +}; + +enum TRBCCode { + CC_DISCONNECTED = -2, + CC_TIMEOUT = -1, + CC_INVALID = 0, + CC_SUCCESS, + CC_DATA_BUFFER_ERROR, + CC_BABBLE_DETECTED, + CC_USB_TRANSACTION_ERROR, + CC_TRB_ERROR, + CC_STALL_ERROR, + CC_RESOURCE_ERROR, + CC_BANDWIDTH_ERROR, + CC_NO_SLOTS_ERROR, + CC_INVALID_STREAM_TYPE_ERROR, + CC_SLOT_NOT_ENABLED_ERROR, + CC_EP_NOT_ENABLED_ERROR, + CC_SHORT_PACKET, + CC_RING_UNDERRUN, + CC_RING_OVERRUN, + CC_VF_ER_FULL, + CC_PARAMETER_ERROR, + CC_BANDWIDTH_OVERRUN, + CC_CONTEXT_STATE_ERROR, + CC_NO_PING_RESPONSE_ERROR, + CC_EVENT_RING_FULL_ERROR, + CC_INCOMPATIBLE_DEVICE_ERROR, + CC_MISSED_SERVICE_ERROR, + CC_COMMAND_RING_STOPPED, + CC_COMMAND_ABORTED, + CC_STOPPED, + CC_STOPPED_LENGTH_INVALID, + CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR = 29, + CC_ISOCH_BUFFER_OVERRUN = 31, + CC_EVENT_LOST_ERROR, + CC_UNDEFINED_ERROR, + CC_INVALID_STREAM_ID_ERROR, + CC_SECONDARY_BANDWIDTH_ERROR, + CC_SPLIT_TRANSACTION_ERROR +}; + +/***************** Macros (Inline Functions) Definitions *********************/ +/* + * xhci_ring structs are allocated with XHCI_RING_SIZE alignment, + * then we can get it from a trb pointer (provided by evt ring). + */ +#define XHCI_RING(_trb) \ + ((struct xhci_ring*)((unsigned long)(_trb) & ~(XHCI_RING_SIZE-1))) + +#define XHCI_GET_FIELD(data, field) \ + (((data) >> field##_SHIFT) & field##_MASK) + +#define BARRIER() __asm__ __volatile__("": : :"memory") + +#ifdef XHCI_AARCH64 +#define DSB() __asm__ __volatile__("dsb sy": : : "memory") +#else +#define DSB() __asm__ __volatile__("dsb": : : "memory") +#endif + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ +static inline void writeq(unsigned long addr, uint64_t val) { + BARRIER(); + *(volatile uint64_t *)addr = val; +} + +static inline void writel(unsigned long addr, uint32_t val) { + BARRIER(); + *(volatile uint32_t *)addr = val; +} + +static inline void writew(unsigned long addr, uint16_t val) { + BARRIER(); + *(volatile uint16_t *)addr = val; +} + +static inline void writeb(unsigned long addr, uint8_t val) { + BARRIER(); + *(volatile uint8_t *)addr = val; +} + +static inline uint64_t readq(unsigned long addr) { + uint64_t val = *(volatile const uint64_t *)addr; + BARRIER(); + return val; +} + +static inline uint32_t readl(unsigned long addr) { + uint32_t val = *(volatile const uint32_t *)addr; + BARRIER(); + return val; +} + +static inline uint16_t readw(unsigned long addr) { + uint16_t val = *(volatile const uint16_t *)addr; + BARRIER(); + return val; +} + +static inline uint8_t readb(unsigned long addr) { + uint8_t val = *(volatile const uint8_t *)addr; + BARRIER(); + return val; +} + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file