diff --git a/drivers/usb/udc/Kconfig.mcux b/drivers/usb/udc/Kconfig.mcux index 7ebace0e7f7..79273fcbc85 100644 --- a/drivers/usb/udc/Kconfig.mcux +++ b/drivers/usb/udc/Kconfig.mcux @@ -8,6 +8,7 @@ config UDC_NXP_EHCI select UDC_DRIVER_HAS_HIGH_SPEED_SUPPORT select PINCTRL select NOCACHE_MEMORY if CPU_HAS_DCACHE + select DYNAMIC_INTERRUPTS imply UDC_BUF_FORCE_NOCACHE imply UDC_WORKQUEUE help diff --git a/drivers/usb/udc/udc_mcux_ehci.c b/drivers/usb/udc/udc_mcux_ehci.c index 0feda0bd414..a5a468da7c9 100644 --- a/drivers/usb/udc/udc_mcux_ehci.c +++ b/drivers/usb/udc/udc_mcux_ehci.c @@ -873,10 +873,10 @@ static usb_phy_config_struct_t phy_config_##n = { \ \ static void udc_irq_enable_func##n(const struct device *dev) \ { \ - IRQ_CONNECT(DT_INST_IRQN(n), \ - DT_INST_IRQ(n, priority), \ - udc_mcux_isr, \ - DEVICE_DT_INST_GET(n), 0); \ + irq_connect_dynamic(DT_INST_IRQN(n), \ + DT_INST_IRQ(n, priority), \ + (void (*)(const void *))udc_mcux_isr, \ + DEVICE_DT_INST_GET(n), 0); \ \ irq_enable(DT_INST_IRQN(n)); \ } \ diff --git a/drivers/usb/uhc/CMakeLists.txt b/drivers/usb/uhc/CMakeLists.txt index 7f38ffd3146..69f1cea8433 100644 --- a/drivers/usb/uhc/CMakeLists.txt +++ b/drivers/usb/uhc/CMakeLists.txt @@ -1,4 +1,5 @@ # Copyright (c) 2022 Nordic Semiconductor +# Copyright 2024 NXP # SPDX-License-Identifier: Apache-2.0 zephyr_library() @@ -6,3 +7,7 @@ zephyr_library() zephyr_library_sources(uhc_common.c) zephyr_library_sources_ifdef(CONFIG_UHC_MAX3421E uhc_max3421e.c) zephyr_library_sources_ifdef(CONFIG_UHC_VIRTUAL uhc_virtual.c) +zephyr_library_sources_ifdef(CONFIG_UHC_NXP_EHCI uhc_mcux_common.c uhc_mcux_ehci.c) +zephyr_library_sources_ifdef(CONFIG_UHC_NXP_KHCI uhc_mcux_common.c uhc_mcux_khci.c) +zephyr_library_sources_ifdef(CONFIG_UHC_NXP_OHCI uhc_mcux_common.c uhc_mcux_ohci.c) +zephyr_library_sources_ifdef(CONFIG_UHC_NXP_IP3516HS uhc_mcux_common.c uhc_mcux_ip3516hs.c) diff --git a/drivers/usb/uhc/Kconfig b/drivers/usb/uhc/Kconfig index e6da1d4d3df..052e9437f6a 100644 --- a/drivers/usb/uhc/Kconfig +++ b/drivers/usb/uhc/Kconfig @@ -38,5 +38,6 @@ source "subsys/logging/Kconfig.template.log_config" source "drivers/usb/uhc/Kconfig.max3421e" source "drivers/usb/uhc/Kconfig.virtual" +source "drivers/usb/uhc/Kconfig.mcux" endif # UHC_DRIVER diff --git a/drivers/usb/uhc/Kconfig.mcux b/drivers/usb/uhc/Kconfig.mcux new file mode 100644 index 00000000000..11e2befcc81 --- /dev/null +++ b/drivers/usb/uhc/Kconfig.mcux @@ -0,0 +1,46 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +config UHC_NXP_EHCI + bool "NXP MCUX USB EHCI Host controller driver" + default y + depends on DT_HAS_NXP_UHC_EHCI_ENABLED + select EVENTS + select NOCACHE_MEMORY if CPU_HAS_DCACHE + select DYNAMIC_INTERRUPTS + help + NXP MCUX USB Host Controller Driver for EHCI. + +config UHC_NXP_KHCI + bool "NXP MCUX USB KHCI Host controller driver" + default y + depends on DT_HAS_NXP_UHC_KHCI_ENABLED + select EVENTS + help + NXP MCUX USB Host Controller Driver for KHCI. + +config UHC_NXP_IP3516HS + bool "NXP MCUX USB IP3516HS Host controller driver" + default y + depends on DT_HAS_NXP_UHC_IP3516HS_ENABLED + select EVENTS + help + NXP MCUX USB Host Controller Driver for ip3516hs. + +config UHC_NXP_OHCI + bool "NXP MCUX USB IP3516HS Host controller driver" + default y + depends on DT_HAS_NXP_UHC_OHCI_ENABLED + select EVENTS + help + NXP MCUX USB Host Controller Driver for ohci. + +config HEAP_MEM_POOL_ADD_SIZE_UHC_MCUX + int "The heap that NXP hal usb host controller drivers need" + default 4096 + +config UHC_NXP_THREAD_STACK_SIZE + int "MCUX UHC Driver internal thread stack size" + default 2048 + help + Size of the stack used in the driver. diff --git a/drivers/usb/uhc/uhc_mcux_common.c b/drivers/usb/uhc/uhc_mcux_common.c new file mode 100644 index 00000000000..9fcf61c723c --- /dev/null +++ b/drivers/usb/uhc/uhc_mcux_common.c @@ -0,0 +1,358 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uhc_common.h" +#include "usb.h" +#include "usb_host_config.h" +#include "usb_host_mcux_drv_port.h" +#include "uhc_mcux_common.h" + +#include +LOG_MODULE_REGISTER(uhc_mcux, CONFIG_UHC_DRIVER_LOG_LEVEL); + +#define PRV_DATA_HANDLE(_handle) CONTAINER_OF(_handle, struct uhc_mcux_data, mcux_host) + +int uhc_mcux_lock(const struct device *dev) +{ + struct uhc_data *data = dev->data; + + return k_mutex_lock(&data->mutex, K_FOREVER); +} + +int uhc_mcux_unlock(const struct device *dev) +{ + struct uhc_data *data = dev->data; + + return k_mutex_unlock(&data->mutex); +} + +int uhc_mcux_control(const struct device *dev, uint32_t control, void *param) +{ + struct uhc_mcux_data *priv = uhc_get_private(dev); + usb_status_t status; + + status = priv->mcux_if->controllerIoctl(priv->mcux_host.controllerHandle, control, param); + if (status != kStatus_USB_Success) { + return -EIO; + } + + return 0; +} + +int uhc_mcux_bus_control(const struct device *dev, usb_host_bus_control_t type) +{ + return uhc_mcux_control(dev, kUSB_HostBusControl, &type); +} + +int uhc_mcux_enable(const struct device *dev) +{ + const struct uhc_mcux_config *config = dev->config; + + config->irq_enable_func(dev); + + return 0; +} + +int uhc_mcux_disable(const struct device *dev) +{ + const struct uhc_mcux_config *config = dev->config; + + config->irq_disable_func(dev); + + return 0; +} + +int uhc_mcux_shutdown(const struct device *dev) +{ + const struct uhc_mcux_config *config = dev->config; + struct uhc_mcux_data *priv = uhc_get_private(dev); + + config->irq_disable_func(dev); + k_thread_abort(&priv->drv_stack_data); + priv->mcux_if->controllerDestory(priv->mcux_host.controllerHandle); + + return 0; +} + +/* Signal bus reset, 50ms SE0 signal */ +int uhc_mcux_bus_reset(const struct device *dev) +{ + return uhc_mcux_bus_control(dev, kUSB_HostBusReset); +} + +/* Enable SOF generator */ +int uhc_mcux_sof_enable(const struct device *dev) +{ + /* MCUX doesn't need it. */ + return 0; +} + +/* Disable SOF generator and suspend bus */ +int uhc_mcux_bus_suspend(const struct device *dev) +{ + return uhc_mcux_bus_control(dev, kUSB_HostBusSuspend); +} + +/* Signal bus resume event, 20ms K-state + low-speed EOP */ +int uhc_mcux_bus_resume(const struct device *dev) +{ + return uhc_mcux_bus_control(dev, kUSB_HostBusResume); +} + +int uhc_mcux_dequeue(const struct device *dev, struct uhc_transfer *const xfer) +{ + usb_host_pipe_t *mcux_ep; + usb_host_cancel_param_t cancel_param; + + mcux_ep = uhc_mcux_init_hal_ep(dev, xfer); + if (mcux_ep == NULL) { + return -ENOMEM; + } + + cancel_param.pipeHandle = mcux_ep; + cancel_param.transfer = NULL; /* cancel all the transfers on the pipe */ + return uhc_mcux_control(dev, kUSB_HostCancelTransfer, &cancel_param); +} + +/* Controller driver calls this function when device attach. */ +usb_status_t USB_HostAttachDevice(usb_host_handle hostHandle, uint8_t speed, uint8_t hubNumber, + uint8_t portNumber, uint8_t level, + usb_device_handle *deviceHandle) +{ + enum uhc_event_type type; + struct uhc_mcux_data *priv; + + if (speed == USB_SPEED_HIGH) { + type = UHC_EVT_DEV_CONNECTED_HS; + } else if (speed == USB_SPEED_FULL) { + type = UHC_EVT_DEV_CONNECTED_FS; + } else { + type = UHC_EVT_DEV_CONNECTED_LS; + } + + priv = (struct uhc_mcux_data *)(PRV_DATA_HANDLE(hostHandle)); + uhc_submit_event(priv->dev, type, 0); + + return kStatus_USB_Success; +} + +/* Controller driver calls this function when device detaches. */ +usb_status_t USB_HostDetachDevice(usb_host_handle hostHandle, uint8_t hubNumber, uint8_t portNumber) +{ + struct uhc_mcux_data *priv; + + priv = (struct uhc_mcux_data *)(PRV_DATA_HANDLE(hostHandle)); + uhc_submit_event(priv->dev, UHC_EVT_DEV_REMOVED, 0); + uhc_mcux_bus_control(priv->dev, kUSB_HostBusEnableAttach); + + return kStatus_USB_Success; +} + +/* MCUX Controller driver calls this function to get the device information. */ +usb_status_t USB_HostHelperGetPeripheralInformation(usb_device_handle deviceHandle, + uint32_t infoCode, uint32_t *infoValue) +{ + /* The deviceHandle is struct usb_device pointer */ + struct usb_device *udev = (struct usb_device *)deviceHandle; + usb_host_dev_info_t info_code; + + if ((deviceHandle == NULL) || (infoValue == NULL)) { + return kStatus_USB_InvalidParameter; + } + info_code = (usb_host_dev_info_t)infoCode; + switch (info_code) { + case kUSB_HostGetDeviceAddress: + *infoValue = udev->addr; + break; + + case kUSB_HostGetDeviceHubNumber: + case kUSB_HostGetDevicePortNumber: + case kUSB_HostGetDeviceHSHubNumber: + case kUSB_HostGetDeviceHSHubPort: + case kUSB_HostGetHubThinkTime: + *infoValue = 0; + break; + case kUSB_HostGetDeviceLevel: + *infoValue = 1; + break; + + case kUSB_HostGetDeviceSpeed: + switch (udev->speed) { + case USB_SPEED_SPEED_LS: + *infoValue = USB_SPEED_LOW; + break; + case USB_SPEED_SPEED_FS: + *infoValue = USB_SPEED_FULL; + break; + case USB_SPEED_SPEED_HS: + *infoValue = USB_SPEED_HIGH; + break; + case USB_SPEED_UNKNOWN: + case USB_SPEED_SPEED_SS: + default: + *infoValue = USB_SPEED_HIGH; + /* TODO: report error */ + break; + } + break; + + default: + break; + } + + return kStatus_USB_Success; +} + +static usb_host_pipe_handle uhc_mcux_check_hal_ep(const struct device *dev, + struct uhc_transfer *const xfer) +{ + struct uhc_mcux_data *priv = uhc_get_private(dev); + usb_host_pipe_t *mcux_ep = NULL; + uint8_t i; + usb_status_t status; + + /* if already initialized */ + for (i = 0; i < USB_HOST_CONFIG_MAX_PIPES; i++) { + uint8_t direction; + + if (USB_EP_GET_IDX(xfer->ep) == 0) { + direction = 0; + } else { + direction = USB_EP_GET_DIR(xfer->ep) ? USB_IN : USB_OUT; + } + + if (priv->mcux_eps[i] != NULL && + priv->mcux_eps[i]->endpointAddress == USB_EP_GET_IDX(xfer->ep) && + priv->mcux_eps[i]->direction == direction && + priv->mcux_eps[i]->deviceHandle == xfer->udev) { + mcux_ep = priv->mcux_eps[i]; + break; + } + } + + /* TODO: need to check endpoint type too */ + if (mcux_ep != NULL && + (mcux_ep->maxPacketSize != xfer->mps || + mcux_ep->interval != xfer->interval)) { + /* re-initialize the ep */ + status = priv->mcux_if->controllerClosePipe(priv->mcux_host.controllerHandle, + mcux_ep); + if (status != kStatus_USB_Success) { + return NULL; + } + + uhc_mcux_lock(dev); + priv->mcux_eps[i] = NULL; + uhc_mcux_unlock(dev); + mcux_ep = NULL; + } + + return mcux_ep; +} + +usb_host_pipe_t *uhc_mcux_init_hal_ep(const struct device *dev, struct uhc_transfer *const xfer) +{ + usb_status_t status; + usb_host_pipe_t *mcux_ep; + struct uhc_mcux_data *priv = uhc_get_private(dev); + usb_host_pipe_init_t pipe_init; + uint8_t i; + + /* if already initialized */ + mcux_ep = uhc_mcux_check_hal_ep(dev, xfer); + if (mcux_ep != NULL) { + return mcux_ep; + } + + /* USB_HostHelperGetPeripheralInformation uses this value as first parameter */ + pipe_init.devInstance = xfer->udev; + pipe_init.nakCount = USB_HOST_CONFIG_MAX_NAK; + pipe_init.maxPacketSize = xfer->mps; + pipe_init.endpointAddress = USB_EP_GET_IDX(xfer->ep); + pipe_init.direction = USB_EP_GET_IDX(xfer->ep) == 0 ? USB_OUT : + USB_EP_GET_DIR(xfer->ep) ? USB_IN : USB_OUT; + /* Current Zephyr Host stack is experimental, the endpoint's interval, + * 'number per uframe' and the endpoint type cannot be got yet. + */ + pipe_init.numberPerUframe = 0; /* TODO: need right way to implement it. */ + pipe_init.interval = xfer->interval; + /* TODO: need right way to implement it. */ + if (pipe_init.endpointAddress == 0) { + pipe_init.pipeType = USB_ENDPOINT_CONTROL; + } else { + pipe_init.pipeType = USB_ENDPOINT_BULK; + } + + status = priv->mcux_if->controllerOpenPipe(priv->mcux_host.controllerHandle, + (usb_host_pipe_handle *)&mcux_ep, &pipe_init); + + if (status != kStatus_USB_Success) { + return NULL; + } + + /* Initialize mcux hal endpoint pipe + * TODO: Need one way to release the pipe. + * Otherwise the priv->mcux_eps will be used up after + * supporting hub and connecting/disconnecting multiple times. + * For example: add endpoint/pipe init and de-init controller + * interafce to resolve the issue. + */ + uhc_mcux_lock(dev); + for (i = 0; i < USB_HOST_CONFIG_MAX_PIPES; i++) { + if (priv->mcux_eps[i] == NULL) { + priv->mcux_eps[i] = mcux_ep; + break; + } + } + uhc_mcux_unlock(dev); + + if (i >= USB_HOST_CONFIG_MAX_PIPES) { + priv->mcux_if->controllerClosePipe(priv->mcux_host.controllerHandle, mcux_ep); + mcux_ep = NULL; + } + + return mcux_ep; +} + +int uhc_mcux_hal_init_transfer_common(const struct device *dev, usb_host_transfer_t *mcux_xfer, + usb_host_pipe_handle mcux_ep_handle, + struct uhc_transfer *const xfer, + host_inner_transfer_callback_t cb) +{ + mcux_xfer->uhc_xfer = xfer; + mcux_xfer->transferPipe = mcux_ep_handle; + mcux_xfer->transferSofar = 0; + mcux_xfer->next = NULL; + mcux_xfer->setupStatus = 0; + mcux_xfer->callbackFn = cb; + mcux_xfer->callbackParam = (void *)dev; + mcux_xfer->setupPacket = (usb_setup_struct_t *)&xfer->setup_pkt[0]; + if (xfer->buf != NULL) { + mcux_xfer->transferLength = xfer->buf->size; + mcux_xfer->transferBuffer = xfer->buf->__buf; + } else { + mcux_xfer->transferBuffer = NULL; + mcux_xfer->transferLength = 0; + } + + if (USB_EP_GET_IDX(xfer->ep) == 0) { + mcux_xfer->direction = USB_REQTYPE_GET_DIR(mcux_xfer->setupPacket->bmRequestType) + ? USB_IN + : USB_OUT; + } + + return 0; +} diff --git a/drivers/usb/uhc/uhc_mcux_common.h b/drivers/usb/uhc/uhc_mcux_common.h new file mode 100644 index 00000000000..d80ccdadcfc --- /dev/null +++ b/drivers/usb/uhc/uhc_mcux_common.h @@ -0,0 +1,70 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_UHC_MCUX_COMMON_H +#define ZEPHYR_INCLUDE_UHC_MCUX_COMMON_H + +struct uhc_mcux_data { + const struct device *dev; + const usb_host_controller_interface_t *mcux_if; + /* TODO: Maybe make it to link with udev->ep_in and udev->ep_out */ + usb_host_pipe_t *mcux_eps[USB_HOST_CONFIG_MAX_PIPES]; + usb_host_instance_t mcux_host; + struct k_thread drv_stack_data; + uint8_t controller_id; /* MCUX hal controller id, 0xFF is invalid value */ +}; + +struct uhc_mcux_config { + void (*irq_enable_func)(const struct device *dev); + void (*irq_disable_func)(const struct device *dev); + uintptr_t base; + k_thread_stack_t *drv_stack; + const struct pinctrl_dev_config *pincfg; +}; + +/* call MCUX HAL controller driver to control controller */ +int uhc_mcux_control(const struct device *dev, uint32_t control, void *param); + +/* call MCUX HAL controller driver to control controller */ +int uhc_mcux_bus_control(const struct device *dev, usb_host_bus_control_t type); + +/* MCUX controller driver common lock function */ +int uhc_mcux_lock(const struct device *dev); + +/* MCUX controller driver common unlock function */ +int uhc_mcux_unlock(const struct device *dev); + +int uhc_mcux_enable(const struct device *dev); + +int uhc_mcux_disable(const struct device *dev); + +int uhc_mcux_shutdown(const struct device *dev); + +/* Signal bus reset, 50ms SE0 signal */ +int uhc_mcux_bus_reset(const struct device *dev); + +/* Enable SOF generator */ +int uhc_mcux_sof_enable(const struct device *dev); + +/* Disable SOF generator and suspend bus */ +int uhc_mcux_bus_suspend(const struct device *dev); + +/* Signal bus resume event, 20ms K-state + low-speed EOP */ +int uhc_mcux_bus_resume(const struct device *dev); + +int uhc_mcux_dequeue(const struct device *dev, struct uhc_transfer *const xfer); + +/* Initialize endpoint for MCUX HAL driver */ +usb_host_pipe_t *uhc_mcux_init_hal_ep(const struct device *dev, + struct uhc_transfer *const xfer); + +/* Initialize transfer for MCUX HAL driver */ +int uhc_mcux_hal_init_transfer_common(const struct device *dev, usb_host_transfer_t *mcux_xfer, + usb_host_pipe_handle mcux_ep_handle, + struct uhc_transfer *const xfer, + host_inner_transfer_callback_t cb); + +#endif /* ZEPHYR_INCLUDE_UHC_MCUX_COMMON_H */ diff --git a/drivers/usb/uhc/uhc_mcux_ehci.c b/drivers/usb/uhc/uhc_mcux_ehci.c new file mode 100644 index 00000000000..9b362a4e86a --- /dev/null +++ b/drivers/usb/uhc/uhc_mcux_ehci.c @@ -0,0 +1,371 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_uhc_ehci + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uhc_common.h" +#include "usb.h" +#include "usb_host_config.h" +#include "usb_host_mcux_drv_port.h" +#include "uhc_mcux_common.h" +#include "usb_host_ehci.h" +#include "usb_phy.h" + +#include +LOG_MODULE_DECLARE(uhc_mcux); + +K_MEM_SLAB_DEFINE_STATIC(mcux_uhc_transfer_pool, sizeof(usb_host_transfer_t), + USB_HOST_CONFIG_MAX_TRANSFERS, sizeof(void *)); + +#if defined(CONFIG_NOCACHE_MEMORY) +K_HEAP_DEFINE_NOCACHE(mcux_transfer_alloc_pool, + USB_HOST_CONFIG_MAX_TRANSFERS * 8u + 1024u * USB_HOST_CONFIG_MAX_TRANSFERS); +#endif + +#define PRV_DATA_HANDLE(_handle) CONTAINER_OF(_handle, struct uhc_mcux_data, mcux_host) + +struct uhc_mcux_ehci_config { + struct uhc_mcux_config uhc_config; + usb_phy_config_struct_t *phy_config; +}; + +#if defined(CONFIG_NOCACHE_MEMORY) +/* allocate non-cached buffer for usb */ +static void *uhc_mcux_nocache_alloc(uint32_t size) +{ + void *p = (void *)k_heap_alloc(&mcux_transfer_alloc_pool, size, K_NO_WAIT); + + if (p != NULL) { + (void)memset(p, 0, size); + } + + return p; +} + +/* free the allocated non-cached buffer */ +static void uhc_mcux_nocache_free(void *p) +{ + k_heap_free(&mcux_transfer_alloc_pool, p); +} +#endif + +static void uhc_mcux_thread(void *p1, void *p2, void *p3) +{ + const struct device *dev = p1; + struct uhc_mcux_data *priv = uhc_get_private(dev); + + while (true) { + USB_HostEhciTaskFunction((void *)(&priv->mcux_host)); + } +} + +static void uhc_mcux_isr(const struct device *dev) +{ + struct uhc_mcux_data *priv = uhc_get_private(dev); + + USB_HostEhciIsrFunction((void *)(&priv->mcux_host)); +} + +/* MCUX controller dirver uses this callback to notify upper layer suspend related event */ +static usb_status_t mcux_host_callback(usb_device_handle deviceHandle, + usb_host_configuration_handle configurationHandle, + uint32_t eventCode) +{ + /* TODO: nothing need to do here currently. + * It will be used when implementing suspend/resume in future. + */ + return kStatus_USB_Success; +} + +static int uhc_mcux_init(const struct device *dev) +{ + const struct uhc_mcux_config *config = dev->config; + usb_phy_config_struct_t *phy_config; + struct uhc_mcux_data *priv = uhc_get_private(dev); + k_thread_entry_t thread_entry = NULL; + usb_status_t status; + + if ((priv->controller_id >= kUSB_ControllerEhci0) && + (priv->controller_id <= kUSB_ControllerEhci1)) { + thread_entry = uhc_mcux_thread; + } + + if (thread_entry == NULL) { + return -ENOMEM; + } + +#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED + phy_config = ((const struct uhc_mcux_ehci_config *)dev->config)->phy_config; + if (phy_config != NULL) { + USB_EhciPhyInit(priv->controller_id, 0u, phy_config); + } +#endif + + priv->dev = dev; + /* Init MCUX USB host driver. */ + priv->mcux_host.deviceCallback = mcux_host_callback; + status = priv->mcux_if->controllerCreate(priv->controller_id, &priv->mcux_host, + &(priv->mcux_host.controllerHandle)); + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + /* Create MCUX USB host driver task */ + k_thread_create(&priv->drv_stack_data, config->drv_stack, CONFIG_UHC_NXP_THREAD_STACK_SIZE, + thread_entry, (void *)dev, NULL, NULL, K_PRIO_COOP(2), 0, K_NO_WAIT); + k_thread_name_set(&priv->drv_stack_data, "uhc_mcux_ehci"); + + return 0; +} + +/* MCUX hal controller driver transfer callback */ +static void uhc_mcux_transfer_callback(void *param, usb_host_transfer_t *transfer, + usb_status_t status) +{ + const struct device *dev = (const struct device *)param; + struct uhc_transfer *const xfer = (struct uhc_transfer *const)(transfer->uhc_xfer); + int err = -EIO; + + if (status == kStatus_USB_Success) { + err = 0; + + if ((xfer->ep == 0) && (transfer->setupPacket->bRequest == USB_SREQ_SET_ADDRESS)) { + usb_host_pipe_t *mcux_ep; + + /* MCUX hal controller driver needs set the ep0's address + * by using kUSB_HostUpdateControlEndpointAddress after set_address. + * When executing kUSB_HostUpdateControlEndpointAddress, + * USB_HostHelperGetPeripheralInformation is called from MCUX hal + * controller driver to get the address. But + * USB_HostHelperGetPeripheralInformation can't get right address for udev + * before this set_address control transfer result call back to host stack. + * So, add one flag and execute kUSB_HostUpdateControlEndpointAddress later + * when there is ep0 transfer. + */ + mcux_ep = uhc_mcux_init_hal_ep(dev, xfer); + if (mcux_ep != NULL) { + mcux_ep->update_addr = 1U; + } + } + } + +#if defined(CONFIG_NOCACHE_MEMORY) + if (transfer->setupPacket != NULL) { + uhc_mcux_nocache_free(transfer->setupPacket); + } +#endif + if ((xfer->buf != NULL) && (transfer->transferBuffer != NULL)) { + if (transfer->transferSofar > 0) { +#if defined(CONFIG_NOCACHE_MEMORY) + memcpy(xfer->buf->__buf, transfer->transferBuffer, transfer->transferSofar); +#endif + net_buf_add(xfer->buf, transfer->transferSofar); + } +#if defined(CONFIG_NOCACHE_MEMORY) + uhc_mcux_nocache_free(transfer->transferBuffer); +#endif + } + + transfer->setupPacket = NULL; + transfer->transferBuffer = NULL; + k_mem_slab_free(&mcux_uhc_transfer_pool, transfer); + uhc_xfer_return(dev, xfer, err); +} + +static usb_host_transfer_t *uhc_mcux_hal_init_transfer(const struct device *dev, + usb_host_pipe_handle mcux_ep_handle, + struct uhc_transfer *const xfer) +{ + usb_host_transfer_t *mcux_xfer; + + if (k_mem_slab_alloc(&mcux_uhc_transfer_pool, (void **)&mcux_xfer, K_NO_WAIT)) { + return NULL; + } + (void)uhc_mcux_hal_init_transfer_common(dev, mcux_xfer, mcux_ep_handle, xfer, + uhc_mcux_transfer_callback); +#if defined(CONFIG_NOCACHE_MEMORY) + mcux_xfer->setupPacket = uhc_mcux_nocache_alloc(8u); + memcpy(mcux_xfer->setupPacket, xfer->setup_pkt, 8u); + if (xfer->buf != NULL) { + mcux_xfer->transferBuffer = uhc_mcux_nocache_alloc(mcux_xfer->transferLength); + } +#endif + + return mcux_xfer; +} + +static int uhc_mcux_enqueue(const struct device *dev, struct uhc_transfer *const xfer) +{ + usb_host_pipe_t *mcux_ep; + struct uhc_mcux_data *priv = uhc_get_private(dev); + usb_status_t status; + usb_host_transfer_t *mcux_xfer; + + uhc_xfer_append(dev, xfer); + + /* firstly check and init the mcux endpoint handle */ + mcux_ep = uhc_mcux_init_hal_ep(dev, xfer); + if (mcux_ep == NULL) { + return -ENOMEM; + } + + /* Initialize MCUX hal transfer. */ + mcux_xfer = uhc_mcux_hal_init_transfer(dev, mcux_ep, xfer); + if (mcux_xfer == NULL) { + return -ENOMEM; + } + + if (mcux_ep->endpointAddress == 0 && mcux_ep->update_addr) { + mcux_ep->update_addr = 0; + /* update control endpoint address here */ + uhc_mcux_control(dev, kUSB_HostUpdateControlEndpointAddress, mcux_ep); + } + + uhc_mcux_lock(dev); + /* give the transfer to MCUX drv. */ + status = priv->mcux_if->controllerWritePipe(priv->mcux_host.controllerHandle, + mcux_ep, mcux_xfer); + uhc_mcux_unlock(dev); + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + return 0; +} + +static inline void uhc_mcux_get_hal_driver_id(struct uhc_mcux_data *priv, + const struct uhc_mcux_config *config) +{ + /* + * MCUX USB controller drivers use an ID to tell the HAL drivers + * which controller is being used. This part of the code converts + * the base address to the ID value. + */ +#if defined(USBHS_STACK_BASE_ADDRS) + uintptr_t usb_base_addrs[] = USBHS_STACK_BASE_ADDRS; +#else + uintptr_t usb_base_addrs[] = USBHS_BASE_ADDRS; +#endif + + /* get the right controller id */ + priv->controller_id = 0xFFu; /* invalid value */ + for (uint8_t i = 0; i < ARRAY_SIZE(usb_base_addrs); i++) { + if (usb_base_addrs[i] == config->base) { + priv->controller_id = kUSB_ControllerEhci0 + i; + break; + } + } +} + +static int uhc_mcux_driver_preinit(const struct device *dev) +{ + const struct uhc_mcux_config *config = dev->config; + struct uhc_data *data = dev->data; + struct uhc_mcux_data *priv = uhc_get_private(dev); + + uhc_mcux_get_hal_driver_id(priv, config); + if (priv->controller_id == 0xFFu) { + return -ENOMEM; + } + + k_mutex_init(&data->mutex); + pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + LOG_DBG("MCUX controller initialized"); + + return 0; +} + +static const struct uhc_api mcux_uhc_api = { + .lock = uhc_mcux_lock, + .unlock = uhc_mcux_unlock, + .init = uhc_mcux_init, + .enable = uhc_mcux_enable, + .disable = uhc_mcux_disable, + .shutdown = uhc_mcux_shutdown, + + .bus_reset = uhc_mcux_bus_reset, + .sof_enable = uhc_mcux_sof_enable, + .bus_suspend = uhc_mcux_bus_suspend, + .bus_resume = uhc_mcux_bus_resume, + + .ep_enqueue = uhc_mcux_enqueue, + .ep_dequeue = uhc_mcux_dequeue, +}; + +static const usb_host_controller_interface_t uhc_mcux_if = { + USB_HostEhciCreate, USB_HostEhciDestory, USB_HostEhciOpenPipe, USB_HostEhciClosePipe, + USB_HostEhciWritePipe, USB_HostEhciReadpipe, USB_HostEhciIoctl, +}; + +#define UHC_MCUX_PHY_DEFINE(n) \ + static usb_phy_config_struct_t phy_config_##n = { \ + .D_CAL = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_d_cal, 0), \ + .TXCAL45DP = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_cal_45_dp_ohms, 0), \ + .TXCAL45DM = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_cal_45_dm_ohms, 0), \ + } + +#define UHC_MCUX_PHY_DEFINE_OR(n) \ + COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), (UHC_MCUX_PHY_DEFINE(n)), ()) + +#define UHC_MCUX_PHY_CFG_PTR_OR_NULL(n) \ + COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), (&phy_config_##n), (NULL)) + +#define UHC_MCUX_EHCI_DEVICE_DEFINE(n) \ + UHC_MCUX_PHY_DEFINE_OR(n); \ + \ + static void uhc_irq_enable_func##n(const struct device *dev) \ + { \ + irq_connect_dynamic(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \ + (void (*)(const void *))uhc_mcux_isr, DEVICE_DT_INST_GET(n), \ + 0); \ + \ + irq_enable(DT_INST_IRQN(n)); \ + } \ + \ + static void uhc_irq_disable_func##n(const struct device *dev) \ + { \ + irq_disable(DT_INST_IRQN(n)); \ + } \ + \ + static K_KERNEL_STACK_DEFINE(drv_stack_##n, CONFIG_UHC_NXP_THREAD_STACK_SIZE); \ + \ + PINCTRL_DT_INST_DEFINE(n); \ + \ + static struct uhc_mcux_ehci_config priv_config_##n = { \ + .uhc_config = { \ + .base = DT_INST_REG_ADDR(n), \ + .irq_enable_func = uhc_irq_enable_func##n, \ + .irq_disable_func = uhc_irq_disable_func##n, \ + .drv_stack = drv_stack_##n, \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + }, \ + .phy_config = UHC_MCUX_PHY_CFG_PTR_OR_NULL(n), \ + }; \ + \ + static struct uhc_mcux_data priv_data_##n = { \ + .mcux_if = &uhc_mcux_if, \ + }; \ + \ + static struct uhc_data uhc_data_##n = { \ + .mutex = Z_MUTEX_INITIALIZER(uhc_data_##n.mutex), \ + .priv = &priv_data_##n, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, uhc_mcux_driver_preinit, NULL, &uhc_data_##n, &priv_config_##n, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &mcux_uhc_api); + +DT_INST_FOREACH_STATUS_OKAY(UHC_MCUX_EHCI_DEVICE_DEFINE) diff --git a/drivers/usb/uhc/uhc_mcux_ip3516hs.c b/drivers/usb/uhc/uhc_mcux_ip3516hs.c new file mode 100644 index 00000000000..0796daae2cc --- /dev/null +++ b/drivers/usb/uhc/uhc_mcux_ip3516hs.c @@ -0,0 +1,324 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_uhc_ip3516hs + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uhc_common.h" +#include "usb.h" +#include "usb_host_config.h" +#include "usb_host_mcux_drv_port.h" +#include "uhc_mcux_common.h" +#include "usb_host_ip3516hs.h" +#include "usb_phy.h" + +#include +LOG_MODULE_DECLARE(uhc_mcux); + +K_MEM_SLAB_DEFINE_STATIC(mcux_uhc_transfer_pool, sizeof(usb_host_transfer_t), + USB_HOST_CONFIG_MAX_TRANSFERS, sizeof(void *)); + +#define PRV_DATA_HANDLE(_handle) CONTAINER_OF(_handle, struct uhc_mcux_data, mcux_host) + +struct uhc_mcux_ip3516hs_config { + struct uhc_mcux_config uhc_config; + usb_phy_config_struct_t *phy_config; +}; + +static void uhc_mcux_thread(void *p1, void *p2, void *p3) +{ + const struct device *dev = p1; + struct uhc_mcux_data *priv = uhc_get_private(dev); + + while (true) { + USB_HostIp3516HsTaskFunction((void *)(&priv->mcux_host)); + } +} + +static void uhc_mcux_isr(const struct device *dev) +{ + struct uhc_mcux_data *priv = uhc_get_private(dev); + + USB_HostIp3516HsIsrFunction((void *)(&priv->mcux_host)); +} + +/* MCUX controller dirver uses this callback to notify upper layer suspend related event */ +static usb_status_t mcux_host_callback(usb_device_handle deviceHandle, + usb_host_configuration_handle configurationHandle, + uint32_t eventCode) +{ + /* TODO: nothing need to do here currently. + * It will be used when implementing suspend/resume in future. + */ + return kStatus_USB_Success; +} + +static int uhc_mcux_init(const struct device *dev) +{ + const struct uhc_mcux_config *config = dev->config; + usb_phy_config_struct_t *phy_config; + struct uhc_mcux_data *priv = uhc_get_private(dev); + k_thread_entry_t thread_entry = NULL; + usb_status_t status; + + if ((priv->controller_id >= kUSB_ControllerIp3516Hs0) && + (priv->controller_id <= kUSB_ControllerIp3516Hs1)) { + thread_entry = uhc_mcux_thread; + } + + if (thread_entry == NULL) { + return -ENOMEM; + } + +#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED + phy_config = ((const struct uhc_mcux_ip3516hs_config *)dev->config)->phy_config; + if (phy_config != NULL) { + USB_EhciPhyInit(priv->controller_id, 0u, phy_config); + } +#endif + + priv->dev = dev; + /* Init MCUX USB host driver. */ + priv->mcux_host.deviceCallback = mcux_host_callback; + status = priv->mcux_if->controllerCreate(priv->controller_id, &priv->mcux_host, + &(priv->mcux_host.controllerHandle)); + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + /* Create MCUX USB host driver task */ + k_thread_create(&priv->drv_stack_data, config->drv_stack, CONFIG_UHC_NXP_THREAD_STACK_SIZE, + thread_entry, (void *)dev, NULL, NULL, K_PRIO_COOP(2), 0, K_NO_WAIT); + k_thread_name_set(&priv->drv_stack_data, "uhc_mcux_ip3516hs"); + + return 0; +} + +/* MCUX hal controller driver transfer callback */ +static void uhc_mcux_transfer_callback(void *param, usb_host_transfer_t *transfer, + usb_status_t status) +{ + const struct device *dev = (const struct device *)param; + struct uhc_transfer *const xfer = (struct uhc_transfer *const)(transfer->uhc_xfer); + int err = -EIO; + + if (status == kStatus_USB_Success) { + err = 0; + + if ((xfer->ep == 0) && (transfer->setupPacket->bRequest == USB_SREQ_SET_ADDRESS)) { + usb_host_pipe_t *mcux_ep; + + /* MCUX hal controller driver needs set the ep0's address + * by using kUSB_HostUpdateControlEndpointAddress after set_address. + * When executing kUSB_HostUpdateControlEndpointAddress, + * USB_HostHelperGetPeripheralInformation is called from MCUX hal + * controller driver to get the address. But + * USB_HostHelperGetPeripheralInformation can't get right address for udev + * before this set_address control transfer result call back to host stack. + * So, add one flag and execute kUSB_HostUpdateControlEndpointAddress later + * when there is ep0 transfer. + */ + mcux_ep = uhc_mcux_init_hal_ep(dev, xfer); + if (mcux_ep != NULL) { + mcux_ep->update_addr = 1U; + } + } + } + + if ((xfer->buf != NULL) && (transfer->transferBuffer != NULL)) { + if (transfer->transferSofar > 0) { + net_buf_add(xfer->buf, transfer->transferSofar); + } + } + + transfer->setupPacket = NULL; + transfer->transferBuffer = NULL; + k_mem_slab_free(&mcux_uhc_transfer_pool, transfer); + uhc_xfer_return(dev, xfer, err); +} + +static usb_host_transfer_t *uhc_mcux_hal_init_transfer(const struct device *dev, + usb_host_pipe_handle mcux_ep_handle, + struct uhc_transfer *const xfer) +{ + usb_host_transfer_t *mcux_xfer; + + if (k_mem_slab_alloc(&mcux_uhc_transfer_pool, (void **)&mcux_xfer, K_NO_WAIT)) { + return NULL; + } + (void)uhc_mcux_hal_init_transfer_common(dev, mcux_xfer, mcux_ep_handle, xfer, + uhc_mcux_transfer_callback); + + return mcux_xfer; +} + +static int uhc_mcux_enqueue(const struct device *dev, struct uhc_transfer *const xfer) +{ + usb_host_pipe_t *mcux_ep; + struct uhc_mcux_data *priv = uhc_get_private(dev); + usb_status_t status; + usb_host_transfer_t *mcux_xfer; + + uhc_xfer_append(dev, xfer); + + /* firstly check and init the mcux endpoint handle */ + mcux_ep = uhc_mcux_init_hal_ep(dev, xfer); + if (mcux_ep == NULL) { + return -ENOMEM; + } + + /* Initialize MCUX hal transfer. */ + mcux_xfer = uhc_mcux_hal_init_transfer(dev, mcux_ep, xfer); + if (mcux_xfer == NULL) { + return -ENOMEM; + } + + if (mcux_ep->endpointAddress == 0 && mcux_ep->update_addr) { + mcux_ep->update_addr = 0; + /* update control endpoint address here */ + uhc_mcux_control(dev, kUSB_HostUpdateControlEndpointAddress, mcux_ep); + } + + uhc_mcux_lock(dev); + /* give the transfer to MCUX drv. */ + status = priv->mcux_if->controllerWritePipe(priv->mcux_host.controllerHandle, + mcux_ep, mcux_xfer); + uhc_mcux_unlock(dev); + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + return 0; +} + +static inline void uhc_mcux_get_hal_driver_id(struct uhc_mcux_data *priv, + const struct uhc_mcux_config *config) +{ + /* + * MCUX USB controller drivers use an ID to tell the HAL drivers + * which controller is being used. This part of the code converts + * the base address to the ID value. + */ + uintptr_t usb_base_addrs[] = USBHSH_BASE_ADDRS; + + /* get the right controller id */ + priv->controller_id = 0xFFu; /* invalid value */ + for (uint8_t i = 0; i < ARRAY_SIZE(usb_base_addrs); i++) { + if (usb_base_addrs[i] == config->base) { + priv->controller_id = kUSB_ControllerIp3516Hs0 + i; + break; + } + } +} + +static int uhc_mcux_driver_preinit(const struct device *dev) +{ + const struct uhc_mcux_config *config = dev->config; + struct uhc_data *data = dev->data; + struct uhc_mcux_data *priv = uhc_get_private(dev); + + uhc_mcux_get_hal_driver_id(priv, config); + if (priv->controller_id == 0xFFu) { + return -ENOMEM; + } + + k_mutex_init(&data->mutex); + pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + LOG_DBG("MCUX controller initialized"); + + return 0; +} + +static const struct uhc_api mcux_uhc_api = { + .lock = uhc_mcux_lock, + .unlock = uhc_mcux_unlock, + .init = uhc_mcux_init, + .enable = uhc_mcux_enable, + .disable = uhc_mcux_disable, + .shutdown = uhc_mcux_shutdown, + + .bus_reset = uhc_mcux_bus_reset, + .sof_enable = uhc_mcux_sof_enable, + .bus_suspend = uhc_mcux_bus_suspend, + .bus_resume = uhc_mcux_bus_resume, + + .ep_enqueue = uhc_mcux_enqueue, + .ep_dequeue = uhc_mcux_dequeue, +}; + +static const usb_host_controller_interface_t uhc_mcux_if = { + USB_HostIp3516HsCreate, USB_HostIp3516HsDestory, USB_HostIp3516HsOpenPipe, + USB_HostIp3516HsClosePipe, USB_HostIp3516HsWritePipe, USB_HostIp3516HsReadPipe, + USB_HostIp3516HsIoctl, +}; + +#define UHC_MCUX_PHY_DEFINE(n) \ + static usb_phy_config_struct_t phy_config_##n = { \ + .D_CAL = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_d_cal, 0), \ + .TXCAL45DP = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_cal_45_dp_ohms, 0), \ + .TXCAL45DM = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_cal_45_dm_ohms, 0), \ + } + +#define UHC_MCUX_PHY_DEFINE_OR(n) \ + COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), (UHC_MCUX_PHY_DEFINE(n)), ()) + +#define UHC_MCUX_PHY_CFG_PTR_OR_NULL(n) \ + COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), (&phy_config_##n), (NULL)) + +#define UHC_MCUX_IP3516HS_DEVICE_DEFINE(n) \ + UHC_MCUX_PHY_DEFINE_OR(n); \ + \ + static void uhc_irq_enable_func##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), uhc_mcux_isr, \ + DEVICE_DT_INST_GET(n), 0); \ + \ + irq_enable(DT_INST_IRQN(n)); \ + } \ + \ + static void uhc_irq_disable_func##n(const struct device *dev) \ + { \ + irq_disable(DT_INST_IRQN(n)); \ + } \ + \ + static K_KERNEL_STACK_DEFINE(drv_stack_##n, CONFIG_UHC_NXP_THREAD_STACK_SIZE); \ + \ + PINCTRL_DT_INST_DEFINE(n); \ + \ + static struct uhc_mcux_ip3516hs_config priv_config_##n = { \ + .uhc_config = { \ + .base = DT_INST_REG_ADDR(n), \ + .irq_enable_func = uhc_irq_enable_func##n, \ + .irq_disable_func = uhc_irq_disable_func##n, \ + .drv_stack = drv_stack_##n, \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + }, \ + .phy_config = UHC_MCUX_PHY_CFG_PTR_OR_NULL(n), \ + }; \ + \ + static struct uhc_mcux_data priv_data_##n = { \ + .mcux_if = &uhc_mcux_if, \ + }; \ + \ + static struct uhc_data uhc_data_##n = { \ + .mutex = Z_MUTEX_INITIALIZER(uhc_data_##n.mutex), \ + .priv = &priv_data_##n, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, uhc_mcux_driver_preinit, NULL, &uhc_data_##n, &priv_config_##n, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &mcux_uhc_api); + +DT_INST_FOREACH_STATUS_OKAY(UHC_MCUX_IP3516HS_DEVICE_DEFINE) diff --git a/drivers/usb/uhc/uhc_mcux_khci.c b/drivers/usb/uhc/uhc_mcux_khci.c new file mode 100644 index 00000000000..fcab9aa8885 --- /dev/null +++ b/drivers/usb/uhc/uhc_mcux_khci.c @@ -0,0 +1,291 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_uhc_khci + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uhc_common.h" +#include "usb.h" +#include "usb_host_config.h" +#include "usb_host_mcux_drv_port.h" +#include "uhc_mcux_common.h" +#include "usb_host_khci.h" + +#include +LOG_MODULE_DECLARE(uhc_mcux); + +K_MEM_SLAB_DEFINE_STATIC(mcux_uhc_transfer_pool, sizeof(usb_host_transfer_t), + USB_HOST_CONFIG_MAX_TRANSFERS, sizeof(void *)); + +#define PRV_DATA_HANDLE(_handle) CONTAINER_OF(_handle, struct uhc_mcux_data, mcux_host) + +static void uhc_mcux_thread(void *p1, void *p2, void *p3) +{ + const struct device *dev = p1; + struct uhc_mcux_data *priv = uhc_get_private(dev); + + while (true) { + USB_HostKhciTaskFunction((void *)(&priv->mcux_host)); + } +} + +static void uhc_mcux_isr(const struct device *dev) +{ + struct uhc_mcux_data *priv = uhc_get_private(dev); + + USB_HostKhciIsrFunction((void *)(&priv->mcux_host)); +} + +/* MCUX controller dirver uses this callback to notify upper layer suspend related event */ +static usb_status_t mcux_host_callback(usb_device_handle deviceHandle, + usb_host_configuration_handle configurationHandle, + uint32_t eventCode) +{ + /* TODO: nothing need to do here currently. + * It will be used when implementing suspend/resume in future. + */ + return kStatus_USB_Success; +} + +static int uhc_mcux_init(const struct device *dev) +{ + const struct uhc_mcux_config *config = dev->config; + struct uhc_mcux_data *priv = uhc_get_private(dev); + k_thread_entry_t thread_entry = NULL; + usb_status_t status; + + if ((priv->controller_id >= kUSB_ControllerKhci0) && + (priv->controller_id <= kUSB_ControllerKhci1)) { + thread_entry = uhc_mcux_thread; + } + + if (thread_entry == NULL) { + return -ENOMEM; + } + + priv->dev = dev; + /* Init MCUX USB host driver. */ + priv->mcux_host.deviceCallback = mcux_host_callback; + status = priv->mcux_if->controllerCreate(priv->controller_id, &priv->mcux_host, + &(priv->mcux_host.controllerHandle)); + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + /* Create MCUX USB host driver task */ + k_thread_create(&priv->drv_stack_data, config->drv_stack, CONFIG_UHC_NXP_THREAD_STACK_SIZE, + thread_entry, (void *)dev, NULL, NULL, K_PRIO_COOP(2), 0, K_NO_WAIT); + k_thread_name_set(&priv->drv_stack_data, "uhc_mcux_khci"); + + return 0; +} + +/* MCUX hal controller driver transfer callback */ +static void uhc_mcux_transfer_callback(void *param, usb_host_transfer_t *transfer, + usb_status_t status) +{ + const struct device *dev = (const struct device *)param; + struct uhc_transfer *const xfer = (struct uhc_transfer *const)(transfer->uhc_xfer); + int err = -EIO; + + if (status == kStatus_USB_Success) { + err = 0; + + if ((xfer->ep == 0) && (transfer->setupPacket->bRequest == USB_SREQ_SET_ADDRESS)) { + usb_host_pipe_t *mcux_ep; + + /* MCUX hal controller driver needs set the ep0's address + * by using kUSB_HostUpdateControlEndpointAddress after set_address. + * When executing kUSB_HostUpdateControlEndpointAddress, + * USB_HostHelperGetPeripheralInformation is called from MCUX hal + * controller driver to get the address. But + * USB_HostHelperGetPeripheralInformation can't get right address for udev + * before this set_address control transfer result call back to host stack. + * So, add one flag and execute kUSB_HostUpdateControlEndpointAddress later + * when there is ep0 transfer. + */ + mcux_ep = uhc_mcux_init_hal_ep(dev, xfer); + if (mcux_ep != NULL) { + mcux_ep->update_addr = 1U; + } + } + } + + if ((xfer->buf != NULL) && (transfer->transferBuffer != NULL)) { + if (transfer->transferSofar > 0) { + net_buf_add(xfer->buf, transfer->transferSofar); + } + } + + transfer->setupPacket = NULL; + transfer->transferBuffer = NULL; + k_mem_slab_free(&mcux_uhc_transfer_pool, transfer); + uhc_xfer_return(dev, xfer, err); +} + +static usb_host_transfer_t *uhc_mcux_hal_init_transfer(const struct device *dev, + usb_host_pipe_handle mcux_ep_handle, + struct uhc_transfer *const xfer) +{ + usb_host_transfer_t *mcux_xfer; + + if (k_mem_slab_alloc(&mcux_uhc_transfer_pool, (void **)&mcux_xfer, K_NO_WAIT)) { + return NULL; + } + (void)uhc_mcux_hal_init_transfer_common(dev, mcux_xfer, mcux_ep_handle, xfer, + uhc_mcux_transfer_callback); + + return mcux_xfer; +} + +static int uhc_mcux_enqueue(const struct device *dev, struct uhc_transfer *const xfer) +{ + usb_host_pipe_t *mcux_ep; + struct uhc_mcux_data *priv = uhc_get_private(dev); + usb_status_t status; + usb_host_transfer_t *mcux_xfer; + + uhc_xfer_append(dev, xfer); + + /* firstly check and init the mcux endpoint handle */ + mcux_ep = uhc_mcux_init_hal_ep(dev, xfer); + if (mcux_ep == NULL) { + return -ENOMEM; + } + + /* Initialize MCUX hal transfer. */ + mcux_xfer = uhc_mcux_hal_init_transfer(dev, mcux_ep, xfer); + if (mcux_xfer == NULL) { + return -ENOMEM; + } + + if (mcux_ep->endpointAddress == 0 && mcux_ep->update_addr) { + mcux_ep->update_addr = 0; + /* update control endpoint address here */ + uhc_mcux_control(dev, kUSB_HostUpdateControlEndpointAddress, mcux_ep); + } + + uhc_mcux_lock(dev); + /* give the transfer to MCUX drv. */ + status = priv->mcux_if->controllerWritePipe(priv->mcux_host.controllerHandle, + mcux_ep, mcux_xfer); + uhc_mcux_unlock(dev); + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + return 0; +} + +static inline void uhc_mcux_get_hal_driver_id(struct uhc_mcux_data *priv, + const struct uhc_mcux_config *config) +{ + /* + * MCUX USB controller drivers use an ID to tell the HAL drivers + * which controller is being used. This part of the code converts + * the base address to the ID value. + */ + uintptr_t usb_base_addrs[] = USB_BASE_ADDRS; + + /* get the right controller id */ + priv->controller_id = 0xFFu; /* invalid value */ + for (uint8_t i = 0; i < ARRAY_SIZE(usb_base_addrs); i++) { + if (usb_base_addrs[i] == config->base) { + priv->controller_id = kUSB_ControllerKhci0 + i; + break; + } + } +} + +static int uhc_mcux_driver_preinit(const struct device *dev) +{ + const struct uhc_mcux_config *config = dev->config; + struct uhc_data *data = dev->data; + struct uhc_mcux_data *priv = uhc_get_private(dev); + + uhc_mcux_get_hal_driver_id(priv, config); + if (priv->controller_id == 0xFFu) { + return -ENOMEM; + } + + k_mutex_init(&data->mutex); + pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + LOG_DBG("MCUX controller initialized"); + + return 0; +} + +static const struct uhc_api mcux_uhc_api = { + .lock = uhc_mcux_lock, + .unlock = uhc_mcux_unlock, + .init = uhc_mcux_init, + .enable = uhc_mcux_enable, + .disable = uhc_mcux_disable, + .shutdown = uhc_mcux_shutdown, + + .bus_reset = uhc_mcux_bus_reset, + .sof_enable = uhc_mcux_sof_enable, + .bus_suspend = uhc_mcux_bus_suspend, + .bus_resume = uhc_mcux_bus_resume, + + .ep_enqueue = uhc_mcux_enqueue, + .ep_dequeue = uhc_mcux_dequeue, +}; + +static const usb_host_controller_interface_t uhc_mcux_if = { + USB_HostKhciCreate, USB_HostKhciDestory, USB_HostKhciOpenPipe, USB_HostKhciClosePipe, + USB_HostKhciWritePipe, USB_HostKhciReadpipe, USB_HostKciIoctl, +}; + +#define UHC_MCUX_KHCI_DEVICE_DEFINE(n) \ + static void uhc_irq_enable_func##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), uhc_mcux_isr, \ + DEVICE_DT_INST_GET(n), 0); \ + \ + irq_enable(DT_INST_IRQN(n)); \ + } \ + \ + static void uhc_irq_disable_func##n(const struct device *dev) \ + { \ + irq_disable(DT_INST_IRQN(n)); \ + } \ + \ + static K_KERNEL_STACK_DEFINE(drv_stack_##n, CONFIG_UHC_NXP_THREAD_STACK_SIZE); \ + \ + PINCTRL_DT_INST_DEFINE(n); \ + \ + static struct uhc_mcux_config priv_config_##n = { \ + .base = DT_INST_REG_ADDR(n), \ + .irq_enable_func = uhc_irq_enable_func##n, \ + .irq_disable_func = uhc_irq_disable_func##n, \ + .drv_stack = drv_stack_##n, \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + }; \ + \ + static struct uhc_mcux_data priv_data_##n = { \ + .mcux_if = &uhc_mcux_if, \ + }; \ + \ + static struct uhc_data uhc_data_##n = { \ + .mutex = Z_MUTEX_INITIALIZER(uhc_data_##n.mutex), \ + .priv = &priv_data_##n, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, uhc_mcux_driver_preinit, NULL, &uhc_data_##n, &priv_config_##n, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &mcux_uhc_api); + +DT_INST_FOREACH_STATUS_OKAY(UHC_MCUX_KHCI_DEVICE_DEFINE) diff --git a/drivers/usb/uhc/uhc_mcux_ohci.c b/drivers/usb/uhc/uhc_mcux_ohci.c new file mode 100644 index 00000000000..a377170d6d4 --- /dev/null +++ b/drivers/usb/uhc/uhc_mcux_ohci.c @@ -0,0 +1,291 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_uhc_ohci + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uhc_common.h" +#include "usb.h" +#include "usb_host_config.h" +#include "usb_host_mcux_drv_port.h" +#include "uhc_mcux_common.h" +#include "usb_host_ohci.h" + +#include +LOG_MODULE_DECLARE(uhc_mcux); + +K_MEM_SLAB_DEFINE_STATIC(mcux_uhc_transfer_pool, sizeof(usb_host_transfer_t), + USB_HOST_CONFIG_MAX_TRANSFERS, sizeof(void *)); + +#define PRV_DATA_HANDLE(_handle) CONTAINER_OF(_handle, struct uhc_mcux_data, mcux_host) + +static void uhc_mcux_thread(void *p1, void *p2, void *p3) +{ + const struct device *dev = p1; + struct uhc_mcux_data *priv = uhc_get_private(dev); + + while (true) { + USB_HostOhciTaskFunction((void *)(&priv->mcux_host)); + } +} + +static void uhc_mcux_isr(const struct device *dev) +{ + struct uhc_mcux_data *priv = uhc_get_private(dev); + + USB_HostOhciIsrFunction((void *)(&priv->mcux_host)); +} + +/* MCUX controller dirver uses this callback to notify upper layer suspend related event */ +static usb_status_t mcux_host_callback(usb_device_handle deviceHandle, + usb_host_configuration_handle configurationHandle, + uint32_t eventCode) +{ + /* TODO: nothing need to do here currently. + * It will be used when implementing suspend/resume in future. + */ + return kStatus_USB_Success; +} + +static int uhc_mcux_init(const struct device *dev) +{ + const struct uhc_mcux_config *config = dev->config; + struct uhc_mcux_data *priv = uhc_get_private(dev); + k_thread_entry_t thread_entry = NULL; + usb_status_t status; + + if ((priv->controller_id >= kUSB_ControllerOhci0) && + (priv->controller_id <= kUSB_ControllerOhci1)) { + thread_entry = uhc_mcux_thread; + } + + if (thread_entry == NULL) { + return -ENOMEM; + } + + priv->dev = dev; + /* Init MCUX USB host driver. */ + priv->mcux_host.deviceCallback = mcux_host_callback; + status = priv->mcux_if->controllerCreate(priv->controller_id, &priv->mcux_host, + &(priv->mcux_host.controllerHandle)); + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + /* Create MCUX USB host driver task */ + k_thread_create(&priv->drv_stack_data, config->drv_stack, CONFIG_UHC_NXP_THREAD_STACK_SIZE, + thread_entry, (void *)dev, NULL, NULL, K_PRIO_COOP(2), 0, K_NO_WAIT); + k_thread_name_set(&priv->drv_stack_data, "uhc_mcux_ohci"); + + return 0; +} + +/* MCUX hal controller driver transfer callback */ +static void uhc_mcux_transfer_callback(void *param, usb_host_transfer_t *transfer, + usb_status_t status) +{ + const struct device *dev = (const struct device *)param; + struct uhc_transfer *const xfer = (struct uhc_transfer *const)(transfer->uhc_xfer); + int err = -EIO; + + if (status == kStatus_USB_Success) { + err = 0; + + if ((xfer->ep == 0) && (transfer->setupPacket->bRequest == USB_SREQ_SET_ADDRESS)) { + usb_host_pipe_t *mcux_ep; + + /* MCUX hal controller driver needs set the ep0's address + * by using kUSB_HostUpdateControlEndpointAddress after set_address. + * When executing kUSB_HostUpdateControlEndpointAddress, + * USB_HostHelperGetPeripheralInformation is called from MCUX hal + * controller driver to get the address. But + * USB_HostHelperGetPeripheralInformation can't get right address for udev + * before this set_address control transfer result call back to host stack. + * So, add one flag and execute kUSB_HostUpdateControlEndpointAddress later + * when there is ep0 transfer. + */ + mcux_ep = uhc_mcux_init_hal_ep(dev, xfer); + if (mcux_ep != NULL) { + mcux_ep->update_addr = 1U; + } + } + } + + if ((xfer->buf != NULL) && (transfer->transferBuffer != NULL)) { + if (transfer->transferSofar > 0) { + net_buf_add(xfer->buf, transfer->transferSofar); + } + } + + transfer->setupPacket = NULL; + transfer->transferBuffer = NULL; + k_mem_slab_free(&mcux_uhc_transfer_pool, transfer); + uhc_xfer_return(dev, xfer, err); +} + +static usb_host_transfer_t *uhc_mcux_hal_init_transfer(const struct device *dev, + usb_host_pipe_handle mcux_ep_handle, + struct uhc_transfer *const xfer) +{ + usb_host_transfer_t *mcux_xfer; + + if (k_mem_slab_alloc(&mcux_uhc_transfer_pool, (void **)&mcux_xfer, K_NO_WAIT)) { + return NULL; + } + (void)uhc_mcux_hal_init_transfer_common(dev, mcux_xfer, mcux_ep_handle, xfer, + uhc_mcux_transfer_callback); + + return mcux_xfer; +} + +static int uhc_mcux_enqueue(const struct device *dev, struct uhc_transfer *const xfer) +{ + usb_host_pipe_t *mcux_ep; + struct uhc_mcux_data *priv = uhc_get_private(dev); + usb_status_t status; + usb_host_transfer_t *mcux_xfer; + + uhc_xfer_append(dev, xfer); + + /* firstly check and init the mcux endpoint handle */ + mcux_ep = uhc_mcux_init_hal_ep(dev, xfer); + if (mcux_ep == NULL) { + return -ENOMEM; + } + + /* Initialize MCUX hal transfer. */ + mcux_xfer = uhc_mcux_hal_init_transfer(dev, mcux_ep, xfer); + if (mcux_xfer == NULL) { + return -ENOMEM; + } + + if (mcux_ep->endpointAddress == 0 && mcux_ep->update_addr) { + mcux_ep->update_addr = 0; + /* update control endpoint address here */ + uhc_mcux_control(dev, kUSB_HostUpdateControlEndpointAddress, mcux_ep); + } + + uhc_mcux_lock(dev); + /* give the transfer to MCUX drv. */ + status = priv->mcux_if->controllerWritePipe(priv->mcux_host.controllerHandle, + mcux_ep, mcux_xfer); + uhc_mcux_unlock(dev); + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + return 0; +} + +static inline void uhc_mcux_get_hal_driver_id(struct uhc_mcux_data *priv, + const struct uhc_mcux_config *config) +{ + /* + * MCUX USB controller drivers use an ID to tell the HAL drivers + * which controller is being used. This part of the code converts + * the base address to the ID value. + */ + uintptr_t usb_base_addrs[] = USBFSH_BASE_ADDRS; + + /* get the right controller id */ + priv->controller_id = 0xFFu; /* invalid value */ + for (uint8_t i = 0; i < ARRAY_SIZE(usb_base_addrs); i++) { + if (usb_base_addrs[i] == config->base) { + priv->controller_id = kUSB_ControllerOhci0 + i; + break; + } + } +} + +static int uhc_mcux_driver_preinit(const struct device *dev) +{ + const struct uhc_mcux_config *config = dev->config; + struct uhc_data *data = dev->data; + struct uhc_mcux_data *priv = uhc_get_private(dev); + + uhc_mcux_get_hal_driver_id(priv, config); + if (priv->controller_id == 0xFFu) { + return -ENOMEM; + } + + k_mutex_init(&data->mutex); + pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + LOG_DBG("MCUX controller initialized"); + + return 0; +} + +static const struct uhc_api mcux_uhc_api = { + .lock = uhc_mcux_lock, + .unlock = uhc_mcux_unlock, + .init = uhc_mcux_init, + .enable = uhc_mcux_enable, + .disable = uhc_mcux_disable, + .shutdown = uhc_mcux_shutdown, + + .bus_reset = uhc_mcux_bus_reset, + .sof_enable = uhc_mcux_sof_enable, + .bus_suspend = uhc_mcux_bus_suspend, + .bus_resume = uhc_mcux_bus_resume, + + .ep_enqueue = uhc_mcux_enqueue, + .ep_dequeue = uhc_mcux_dequeue, +}; + +static const usb_host_controller_interface_t uhc_mcux_if = { + USB_HostOhciCreate, USB_HostOhciDestory, USB_HostOhciOpenPipe, USB_HostOhciClosePipe, + USB_HostOhciWritePipe, USB_HostOhciReadPipe, USB_HostOhciIoctl, +}; + +#define UHC_MCUX_OHCI_DEVICE_DEFINE(n) \ + static void uhc_irq_enable_func##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), uhc_mcux_isr, \ + DEVICE_DT_INST_GET(n), 0); \ + \ + irq_enable(DT_INST_IRQN(n)); \ + } \ + \ + static void uhc_irq_disable_func##n(const struct device *dev) \ + { \ + irq_disable(DT_INST_IRQN(n)); \ + } \ + \ + static K_KERNEL_STACK_DEFINE(drv_stack_##n, CONFIG_UHC_NXP_THREAD_STACK_SIZE); \ + \ + PINCTRL_DT_INST_DEFINE(n); \ + \ + static struct uhc_mcux_config priv_config_##n = { \ + .base = DT_INST_REG_ADDR(n), \ + .irq_enable_func = uhc_irq_enable_func##n, \ + .irq_disable_func = uhc_irq_disable_func##n, \ + .drv_stack = drv_stack_##n, \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + }; \ + \ + static struct uhc_mcux_data priv_data_##n = { \ + .mcux_if = &uhc_mcux_if, \ + }; \ + \ + static struct uhc_data uhc_data_##n = { \ + .mutex = Z_MUTEX_INITIALIZER(uhc_data_##n.mutex), \ + .priv = &priv_data_##n, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, uhc_mcux_driver_preinit, NULL, &uhc_data_##n, &priv_config_##n, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &mcux_uhc_api); + +DT_INST_FOREACH_STATUS_OKAY(UHC_MCUX_OHCI_DEVICE_DEFINE) diff --git a/modules/hal_nxp/CMakeLists.txt b/modules/hal_nxp/CMakeLists.txt index 81ea3f1a9d2..7f962faaf45 100644 --- a/modules/hal_nxp/CMakeLists.txt +++ b/modules/hal_nxp/CMakeLists.txt @@ -17,6 +17,7 @@ if(CONFIG_HAS_MCUX OR CONFIG_HAS_IMX_HAL OR CONFIG_HAS_NXP_S32_HAL) if(CONFIG_NOCACHE_MEMORY) zephyr_compile_definitions_ifdef(CONFIG_USB_DEVICE_DRIVER DATA_SECTION_IS_CACHEABLE=1) zephyr_compile_definitions_ifdef(CONFIG_UDC_DRIVER DATA_SECTION_IS_CACHEABLE=1) + zephyr_compile_definitions_ifdef(CONFIG_UHC_DRIVER DATA_SECTION_IS_CACHEABLE=1) endif() zephyr_compile_definitions_ifdef(CONFIG_ETH_NXP_IMX_NETC FSL_ETH_ENABLE_CACHE_CONTROL=1) diff --git a/modules/hal_nxp/mcux/mcux-sdk-ng/middleware/usb_host_config.h b/modules/hal_nxp/mcux/mcux-sdk-ng/middleware/usb_host_config.h new file mode 100644 index 00000000000..bf5f058abeb --- /dev/null +++ b/modules/hal_nxp/mcux/mcux-sdk-ng/middleware/usb_host_config.h @@ -0,0 +1,67 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __USB_HOST_CONFIG_H__ +#define __USB_HOST_CONFIG_H__ + +#include + +/****************************************************************************** + * Definitions + *****************************************************************************/ +/* EHCI instance count */ +#ifdef CONFIG_UHC_NXP_EHCI +#define USB_HOST_CONFIG_EHCI (2U) +#define USB_HOST_CONFIG_EHCI_FRAME_LIST_SIZE (1024U) +#define USB_HOST_CONFIG_EHCI_MAX_QH (8U) +#define USB_HOST_CONFIG_EHCI_MAX_QTD (8U) +#define USB_HOST_CONFIG_EHCI_MAX_ITD (0U) +#define USB_HOST_CONFIG_EHCI_MAX_SITD (0U) +#else +#define USB_HOST_CONFIG_EHCI (0U) +#endif /* CONFIG_USB_DC_NXP_EHCI */ + +#ifdef CONFIG_UHC_NXP_KHCI +#define USB_HOST_CONFIG_KHCI (1U) +#define USB_HOST_CONFIG_KHCI_DMA_ALIGN_BUFFER (64U) +#else +#define USB_HOST_CONFIG_KHCI (0U) +#endif /* CONFIG_UHC_NXP_KHCI */ + +#ifdef CONFIG_UHC_NXP_IP3516HS +#define USB_HOST_CONFIG_IP3516HS (1U) +#define USB_HOST_CONFIG_IP3516HS_MAX_PIPE (32U) +#define USB_HOST_CONFIG_IP3516HS_MAX_ATL (32U) +#define USB_HOST_CONFIG_IP3516HS_MAX_INT (32U) +#define USB_HOST_CONFIG_IP3516HS_MAX_ISO (0U) +#else +#define USB_HOST_CONFIG_IP3516HS (0U) +#endif /* CONFIG_UHC_NXP_IP3516HS */ + +#ifdef CONFIG_UHC_NXP_OHCI +#define USB_HOST_CONFIG_OHCI (1U) +#define USB_HOST_CONFIG_OHCI_MAX_ED (8U) +#define USB_HOST_CONFIG_OHCI_MAX_GTD (8U) +#define USB_HOST_CONFIG_OHCI_MAX_ITD (8U) +#else +#define USB_HOST_CONFIG_OHCI (0U) +#endif /* CONFIG_UHC_NXP_OHCI */ + +#define USB_HOST_CONFIG_MAX_HOST \ + (USB_HOST_CONFIG_KHCI + USB_HOST_CONFIG_EHCI + USB_HOST_CONFIG_IP3516HS + \ + USB_HOST_CONFIG_OHCI) + +#define USB_HOST_CONFIG_MAX_PIPES (16U) +#define USB_HOST_CONFIG_MAX_TRANSFERS (16U) +#define USB_HOST_CONFIG_INTERFACE_MAX_EP (4U) +#define USB_HOST_CONFIG_CONFIGURATION_MAX_INTERFACE (5U) +#define USB_HOST_CONFIG_MAX_POWER (250U) +#define USB_HOST_CONFIG_ENUMERATION_MAX_RETRIES (3U) +#define USB_HOST_CONFIG_ENUMERATION_MAX_STALL_RETRIES (1U) +#define USB_HOST_CONFIG_MAX_NAK (3000U) +#define USB_HOST_CONFIG_CLASS_AUTO_CLEAR_STALL (0U) + +#endif /* __USB_HOST_CONFIG_H__ */