diff --git a/boards/nxp/frdm_k22f/frdm_k22f.dts b/boards/nxp/frdm_k22f/frdm_k22f.dts index b779a0b1495bb..75c3bd27858de 100644 --- a/boards/nxp/frdm_k22f/frdm_k22f.dts +++ b/boards/nxp/frdm_k22f/frdm_k22f.dts @@ -178,6 +178,10 @@ zephyr_udc0: &usbotg { num-bidir-endpoints = <8>; }; +zephyr_uhc0: &usbh { + status = "okay"; +}; + &gpioa { status = "okay"; }; diff --git a/boards/nxp/lpcxpresso55s28/lpcxpresso55s28-pinctrl.dtsi b/boards/nxp/lpcxpresso55s28/lpcxpresso55s28-pinctrl.dtsi index dbf467f3c166a..f363d52a886cd 100644 --- a/boards/nxp/lpcxpresso55s28/lpcxpresso55s28-pinctrl.dtsi +++ b/boards/nxp/lpcxpresso55s28/lpcxpresso55s28-pinctrl.dtsi @@ -46,4 +46,22 @@ }; }; + pinmux_usbhfs: pinmux_usbhfs { + group0 { + pinmux = , + , + ; + bias-pull-up; + slew-rate = "standard"; + }; + }; + + pinmux_usbhhs: pinmux_usbhhs { + group0 { + pinmux = , + ; + bias-pull-up; + slew-rate = "standard"; + }; + }; }; diff --git a/boards/nxp/lpcxpresso55s28/lpcxpresso55s28.dts b/boards/nxp/lpcxpresso55s28/lpcxpresso55s28.dts index a2407a2004ac8..d9d46ed50ed17 100644 --- a/boards/nxp/lpcxpresso55s28/lpcxpresso55s28.dts +++ b/boards/nxp/lpcxpresso55s28/lpcxpresso55s28.dts @@ -127,3 +127,23 @@ zephyr_udc0: &usbhs { status = "okay"; }; + +zephyr_uhc0: &usbhfs { + pinctrl-0 = <&pinmux_usbhfs>; + pinctrl-names = "default"; + status = "okay"; +}; + +zephyr_uhc1: &usbhhs { + pinctrl-0 = <&pinmux_usbhhs>; + pinctrl-names = "default"; + status = "okay"; + phy_handle = <&usbphy1>; +}; + +&usbphy1 { + status = "okay"; + tx-d-cal = <5>; + tx-cal-45-dp-ohms = <10>; + tx-cal-45-dm-ohms = <10>; +}; diff --git a/boards/nxp/lpcxpresso55s69/lpcxpresso55s69-pinctrl.dtsi b/boards/nxp/lpcxpresso55s69/lpcxpresso55s69-pinctrl.dtsi index 9699793638854..bbc196e7eab8c 100644 --- a/boards/nxp/lpcxpresso55s69/lpcxpresso55s69-pinctrl.dtsi +++ b/boards/nxp/lpcxpresso55s69/lpcxpresso55s69-pinctrl.dtsi @@ -2,7 +2,7 @@ * NOTE: File generated by gen_board_pinctrl.py * from LPCXpresso55S69.mex * - * Copyright 2022 NXP + * Copyright 2022,2024 NXP * SPDX-License-Identifier: Apache-2.0 */ @@ -108,4 +108,23 @@ slew-rate = "standard"; }; }; + + pinmux_usbhfs: pinmux_usbhfs { + group0 { + pinmux = , + , + ; + bias-pull-up; + slew-rate = "standard"; + }; + }; + + pinmux_usbhhs: pinmux_usbhhs { + group0 { + pinmux = , + ; + bias-pull-up; + slew-rate = "standard"; + }; + }; }; diff --git a/boards/nxp/lpcxpresso55s69/lpcxpresso55s69_lpc55s69_cpu0.dts b/boards/nxp/lpcxpresso55s69/lpcxpresso55s69_lpc55s69_cpu0.dts index b966c8545e647..44caca0d3270b 100644 --- a/boards/nxp/lpcxpresso55s69/lpcxpresso55s69_lpc55s69_cpu0.dts +++ b/boards/nxp/lpcxpresso55s69/lpcxpresso55s69_lpc55s69_cpu0.dts @@ -155,6 +155,19 @@ zephyr_udc0: &usbhs { phy-handle = <&usbphy1>; }; +zephyr_uhc0: &usbhfs { + pinctrl-0 = <&pinmux_usbhfs>; + pinctrl-names = "default"; + status = "okay"; +}; + +zephyr_uhc1: &usbhhs { + pinctrl-0 = <&pinmux_usbhhs>; + pinctrl-names = "default"; + status = "okay"; + phy_handle = <&usbphy1>; +}; + &usbphy1 { status = "okay"; tx-d-cal = <5>; diff --git a/boards/nxp/mimxrt1050_evk/mimxrt1050_evk.dtsi b/boards/nxp/mimxrt1050_evk/mimxrt1050_evk.dtsi index 4fdca2496d330..0886b2ac912b7 100644 --- a/boards/nxp/mimxrt1050_evk/mimxrt1050_evk.dtsi +++ b/boards/nxp/mimxrt1050_evk/mimxrt1050_evk.dtsi @@ -211,6 +211,30 @@ zephyr_udc0: &usb1 { status = "okay"; }; +zephyr_uhc0: &usbh1 { + status = "okay"; + phy_handle = <&usbphy1>; +}; + +zephyr_uhc1: &usbh2 { + status = "okay"; + phy_handle = <&usbphy2>; +}; + +&usbphy1 { + status = "okay"; + tx-d-cal = <12>; + tx-cal-45-dp-ohms = <6>; + tx-cal-45-dm-ohms = <6>; +}; + +&usbphy2 { + status = "okay"; + tx-d-cal = <12>; + tx-cal-45-dp-ohms = <6>; + tx-cal-45-dm-ohms = <6>; +}; + &usdhc1 { status = "okay"; pwr-gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>; diff --git a/boards/nxp/mimxrt1060_evk/mimxrt1060_evk.dtsi b/boards/nxp/mimxrt1060_evk/mimxrt1060_evk.dtsi index c9b7edc566745..4143a7b6430fa 100644 --- a/boards/nxp/mimxrt1060_evk/mimxrt1060_evk.dtsi +++ b/boards/nxp/mimxrt1060_evk/mimxrt1060_evk.dtsi @@ -188,6 +188,16 @@ zephyr_udc0: &usb1 { phy-handle = <&usbphy1>; }; +zephyr_uhc0: &usbh1 { + status = "okay"; + phy_handle = <&usbphy1>; +}; + +zephyr_uhc1: &usbh2 { + status = "okay"; + phy_handle = <&usbphy2>; +}; + &usbphy1 { status = "okay"; tx-d-cal = <12>; @@ -195,6 +205,13 @@ zephyr_udc0: &usb1 { tx-cal-45-dm-ohms = <6>; }; +&usbphy2 { + status = "okay"; + tx-d-cal = <12>; + tx-cal-45-dp-ohms = <6>; + tx-cal-45-dm-ohms = <6>; +}; + &csi { pinctrl-0 = <&pinmux_csi>; pinctrl-names = "default"; diff --git a/drivers/usb/udc/Kconfig.kinetis b/drivers/usb/udc/Kconfig.kinetis index e0ac6d3d9f53e..59965c80f31b3 100644 --- a/drivers/usb/udc/Kconfig.kinetis +++ b/drivers/usb/udc/Kconfig.kinetis @@ -6,6 +6,7 @@ config UDC_KINETIS imply UDC_WORKQUEUE default y depends on DT_HAS_NXP_KINETIS_USBD_ENABLED + select DYNAMIC_INTERRUPTS if UHC_NXP_KHCI help Kinetis USB device controller criver. diff --git a/drivers/usb/udc/Kconfig.mcux b/drivers/usb/udc/Kconfig.mcux index 7ebace0e7f7c4..0610aea2c0ef8 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 if UHC_NXP_EHCI imply UDC_BUF_FORCE_NOCACHE imply UDC_WORKQUEUE help diff --git a/drivers/usb/udc/udc_kinetis.c b/drivers/usb/udc/udc_kinetis.c index 19e1f865efa49..8858ee129c4f9 100644 --- a/drivers/usb/udc/udc_kinetis.c +++ b/drivers/usb/udc/udc_kinetis.c @@ -1154,14 +1154,19 @@ static const struct udc_api usbfsotg_api = { .unlock = usbfsotg_unlock, }; +#define USBFSOTG_IRQ_DEFINE_OR(n) \ + COND_CODE_1(CONFIG_UHC_NXP_KHCI, \ + (irq_connect_dynamic(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \ + (void (*)(const void *))usbfsotg_isr_handler, \ + DEVICE_DT_INST_GET(n), 0)), \ + (IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \ + usbfsotg_isr_handler, \ + DEVICE_DT_INST_GET(n), 0))) + #define USBFSOTG_DEVICE_DEFINE(n) \ static void udc_irq_enable_func##n(const struct device *dev) \ { \ - IRQ_CONNECT(DT_INST_IRQN(n), \ - DT_INST_IRQ(n, priority), \ - usbfsotg_isr_handler, \ - DEVICE_DT_INST_GET(n), 0); \ - \ + USBFSOTG_IRQ_DEFINE_OR(n); \ irq_enable(DT_INST_IRQN(n)); \ } \ \ diff --git a/drivers/usb/udc/udc_mcux_ehci.c b/drivers/usb/udc/udc_mcux_ehci.c index 0feda0bd4144a..fc95f7a91d7eb 100644 --- a/drivers/usb/udc/udc_mcux_ehci.c +++ b/drivers/usb/udc/udc_mcux_ehci.c @@ -868,16 +868,20 @@ static usb_phy_config_struct_t phy_config_##n = { \ COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), \ (&phy_config_##n), (NULL)) +#define UDC_MCUX_EHCI_IRQ_DEFINE_OR(n) \ + COND_CODE_1(CONFIG_UHC_NXP_EHCI, \ + (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_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), udc_mcux_isr, \ + DEVICE_DT_INST_GET(n), 0))) + #define USB_MCUX_EHCI_DEVICE_DEFINE(n) \ UDC_MCUX_PHY_DEFINE_OR(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); \ - \ + UDC_MCUX_EHCI_IRQ_DEFINE_OR(n); \ irq_enable(DT_INST_IRQN(n)); \ } \ \ diff --git a/drivers/usb/uhc/CMakeLists.txt b/drivers/usb/uhc/CMakeLists.txt index 7f38ffd314649..69f1cea8433b2 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 e6da1d4d3dffb..052e9437f6ae2 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 0000000000000..a9eb2525d5ca6 --- /dev/null +++ b/drivers/usb/uhc/Kconfig.mcux @@ -0,0 +1,47 @@ +# 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 if UDC_NXP_EHCI + 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 + select DYNAMIC_INTERRUPTS if UDC_KINETIS + 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 0000000000000..9fcf61c723cfe --- /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 0000000000000..d80ccdadcfcf0 --- /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 0000000000000..1a8021633eb94 --- /dev/null +++ b/drivers/usb/uhc/uhc_mcux_ehci.c @@ -0,0 +1,375 @@ +/* + * 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_IRQ_DEFINE_OR(n) \ + COND_CODE_1(CONFIG_UDC_NXP_EHCI, \ + (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_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), uhc_mcux_isr, \ + DEVICE_DT_INST_GET(n), 0))) + +#define UHC_MCUX_EHCI_DEVICE_DEFINE(n) \ + UHC_MCUX_PHY_DEFINE_OR(n); \ + \ + static void uhc_irq_enable_func##n(const struct device *dev) \ + { \ + UHC_MCUX_EHCI_IRQ_DEFINE_OR(n); \ + 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 0000000000000..0796daae2cc62 --- /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 0000000000000..c8e62a99e6385 --- /dev/null +++ b/drivers/usb/uhc/uhc_mcux_khci.c @@ -0,0 +1,298 @@ +/* + * 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_IRQ_DEFINE_OR(n) \ + COND_CODE_1(CONFIG_UDC_KINETIS, \ + (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_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \ + uhc_mcux_isr, \ + DEVICE_DT_INST_GET(n), 0))) + +#define UHC_MCUX_KHCI_DEVICE_DEFINE(n) \ + static void uhc_irq_enable_func##n(const struct device *dev) \ + { \ + UHC_MCUX_KHCI_IRQ_DEFINE_OR(n); \ + 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 0000000000000..a377170d6d403 --- /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/dts/arm/nxp/nxp_k2x.dtsi b/dts/arm/nxp/nxp_k2x.dtsi index 0df1010f8b54e..062d5d1611c99 100644 --- a/dts/arm/nxp/nxp_k2x.dtsi +++ b/dts/arm/nxp/nxp_k2x.dtsi @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 Prevas A/S + * Copyright 2024 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -375,6 +376,14 @@ status = "disabled"; }; + usbh: usbh@40072000 { + compatible = "nxp,uhc-khci"; + reg = <0x40072000 0x1000>; + interrupts = <53 1>; + interrupt-names = "usb_otg"; + status = "disabled"; + }; + rnga: random@40029000 { compatible = "nxp,kinetis-rnga"; reg = <0x40029000 0x1000>; diff --git a/dts/arm/nxp/nxp_lpc55S2x_common.dtsi b/dts/arm/nxp/nxp_lpc55S2x_common.dtsi index ca2670dbef80e..8a28db38bef2e 100644 --- a/dts/arm/nxp/nxp_lpc55S2x_common.dtsi +++ b/dts/arm/nxp/nxp_lpc55S2x_common.dtsi @@ -320,6 +320,27 @@ num-bidir-endpoints = <6>; status = "disabled"; }; + + usbhfs: usbhfs@A2000 { + compatible = "nxp,uhc-ohci"; + reg = <0xa2000 0x1000>; + interrupts = <28 1>; + maximum-speed = "full-speed"; + status = "disabled"; + }; + + usbhhs: usbhhs@A3000 { + compatible = "nxp,uhc-ip3516hs"; + reg = <0xa3000 0x1000>; + interrupts = <47 1>; + status = "disabled"; + }; + + usbphy1: usbphy@38000 { + compatible = "nxp,usbphy"; + reg = <0x38000 0x1000>; + status = "disabled"; + }; }; &nvic { diff --git a/dts/arm/nxp/nxp_lpc55S6x_common.dtsi b/dts/arm/nxp/nxp_lpc55S6x_common.dtsi index 07883ab825305..8ef73b8aca518 100644 --- a/dts/arm/nxp/nxp_lpc55S6x_common.dtsi +++ b/dts/arm/nxp/nxp_lpc55S6x_common.dtsi @@ -390,6 +390,21 @@ status = "disabled"; }; + usbhfs: usbhfs@A2000 { + compatible = "nxp,uhc-ohci"; + reg = <0xa2000 0x1000>; + interrupts = <28 1>; + maximum-speed = "full-speed"; + status = "disabled"; + }; + + usbhhs: usbhhs@A3000 { + compatible = "nxp,uhc-ip3516hs"; + reg = <0xa3000 0x1000>; + interrupts = <47 1>; + status = "disabled"; + }; + usbphy1: usbphy@38000 { compatible = "nxp,usbphy"; reg = <0x38000 0x1000>; diff --git a/dts/arm/nxp/nxp_rt10xx.dtsi b/dts/arm/nxp/nxp_rt10xx.dtsi index 80e5a1e7863e2..c2f1ea5caf0fc 100644 --- a/dts/arm/nxp/nxp_rt10xx.dtsi +++ b/dts/arm/nxp/nxp_rt10xx.dtsi @@ -1,5 +1,5 @@ /* - * Copyright 2017,2023 NXP + * Copyright 2017,2023,2024 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -846,6 +846,24 @@ status = "disabled"; }; + usbh1: usbh@402e0000 { + compatible = "nxp,uhc-ehci"; + reg = <0x402E0000 0x200>; + interrupts = <113 1>; + interrupt-names = "usb_otg"; + clocks = <&usbclk>; + status = "disabled"; + }; + + usbh2: usbh@402e0200 { + compatible = "nxp,uhc-ehci"; + reg = <0x402E0200 0x200>; + interrupts = <112 1>; + interrupt-names = "usb_otg"; + clocks = <&usbclk>; + status = "disabled"; + }; + usbphy1: usbphy@400d9000 { compatible = "nxp,usbphy"; reg = <0x400D9000 0x1000>; diff --git a/dts/bindings/usb/nxp,uhc-ehci.yaml b/dts/bindings/usb/nxp,uhc-ehci.yaml new file mode 100644 index 0000000000000..5f8657fe72279 --- /dev/null +++ b/dts/bindings/usb/nxp,uhc-ehci.yaml @@ -0,0 +1,12 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP EHCI USB host controller + +compatible: "nxp,uhc-ehci" + +include: [usb-controller.yaml] + +properties: + phy_handle: + type: phandle diff --git a/dts/bindings/usb/nxp,uhc-ip3516hs.yaml b/dts/bindings/usb/nxp,uhc-ip3516hs.yaml new file mode 100644 index 0000000000000..d09b5b9f056d8 --- /dev/null +++ b/dts/bindings/usb/nxp,uhc-ip3516hs.yaml @@ -0,0 +1,12 @@ +# # Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP IP3516HS USB host controller + +compatible: "nxp,uhc-ip3516hs" + +include: [usb-controller.yaml, pinctrl-device.yaml] + +properties: + phy_handle: + type: phandle diff --git a/dts/bindings/usb/nxp,uhc-khci.yaml b/dts/bindings/usb/nxp,uhc-khci.yaml new file mode 100644 index 0000000000000..9a3c6b3a04670 --- /dev/null +++ b/dts/bindings/usb/nxp,uhc-khci.yaml @@ -0,0 +1,8 @@ +# # Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP KHCI USB host controller + +compatible: "nxp,uhc-khci" + +include: [usb-controller.yaml] diff --git a/dts/bindings/usb/nxp,uhc-ohci.yaml b/dts/bindings/usb/nxp,uhc-ohci.yaml new file mode 100644 index 0000000000000..b39f2c359a9cc --- /dev/null +++ b/dts/bindings/usb/nxp,uhc-ohci.yaml @@ -0,0 +1,8 @@ +# # Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP OHCI USB host controller + +compatible: "nxp,uhc-ohci" + +include: [usb-controller.yaml, pinctrl-device.yaml] diff --git a/include/zephyr/arch/arm/mpu/nxp_mpu.h b/include/zephyr/arch/arm/mpu/nxp_mpu.h index b3a5479d220c4..48419a8419962 100644 --- a/include/zephyr/arch/arm/mpu/nxp_mpu.h +++ b/include/zephyr/arch/arm/mpu/nxp_mpu.h @@ -34,7 +34,7 @@ #define BM4_WE_SHIFT 24 #define BM4_RE_SHIFT 25 -#if CONFIG_USB_KINETIS || CONFIG_UDC_KINETIS +#if CONFIG_USB_KINETIS || CONFIG_UDC_KINETIS || CONFIG_UHC_NXP_KHCI #define BM4_PERMISSIONS ((1 << BM4_RE_SHIFT) | (1 << BM4_WE_SHIFT)) #else #define BM4_PERMISSIONS 0 diff --git a/modules/hal_nxp/CMakeLists.txt b/modules/hal_nxp/CMakeLists.txt index 81ea3f1a9d249..7f962faaf45a2 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/components/components.cmake b/modules/hal_nxp/mcux/mcux-sdk-ng/components/components.cmake index acc954f2fdf30..e52e5dfb6ca8b 100644 --- a/modules/hal_nxp/mcux/mcux-sdk-ng/components/components.cmake +++ b/modules/hal_nxp/mcux/mcux-sdk-ng/components/components.cmake @@ -20,7 +20,7 @@ if(${MCUX_DEVICE} MATCHES "RW61") endif() endif() -if(CONFIG_USB_DEVICE_DRIVER OR CONFIG_UDC_DRIVER OR CONFIG_BT) +if(CONFIG_USB_DEVICE_DRIVER OR CONFIG_UDC_DRIVER OR CONFIG_UHC_DRIVER OR CONFIG_BT) set(CONFIG_USE_component_osa_zephyr ON) endif() diff --git a/modules/hal_nxp/mcux/mcux-sdk-ng/middleware/middleware.cmake b/modules/hal_nxp/mcux/mcux-sdk-ng/middleware/middleware.cmake index f30bcd65a2d34..ca1e988a50c68 100644 --- a/modules/hal_nxp/mcux/mcux-sdk-ng/middleware/middleware.cmake +++ b/modules/hal_nxp/mcux/mcux-sdk-ng/middleware/middleware.cmake @@ -36,6 +36,22 @@ if(CONFIG_UDC_DRIVER) zephyr_include_directories(${MCUX_SDK_NG_DIR}/middleware/usb/include) endif() +if (CONFIG_UHC_DRIVER) + zephyr_include_directories(middleware) + + set(CONFIG_MCUX_COMPONENT_middleware.usb.common_header ON) + + set_variable_ifdef(CONFIG_DT_HAS_NXP_USBPHY_ENABLED CONFIG_MCUX_COMPONENT_middleware.usb.phy) + set_variable_ifdef(CONFIG_UHC_NXP_EHCI CONFIG_MCUX_COMPONENT_middleware.usb.host.ehci) + set_variable_ifdef(CONFIG_UHC_NXP_KHCI CONFIG_MCUX_COMPONENT_middleware.usb.host.khci) + set_variable_ifdef(CONFIG_UHC_NXP_OHCI CONFIG_MCUX_COMPONENT_middleware.usb.host.ohci) + set_variable_ifdef(CONFIG_UHC_NXP_IP3516HS CONFIG_MCUX_COMPONENT_middleware.usb.host.ip3516hs) + # For soc.c build pass + zephyr_include_directories(.) + zephyr_include_directories(${MCUX_SDK_NG_DIR}/middleware/usb/phy) + zephyr_include_directories(${MCUX_SDK_NG_DIR}/middleware/usb/include) +endif() + add_subdirectory(${MCUX_SDK_NG_DIR}/middleware/usb ${CMAKE_CURRENT_BINARY_DIR}/usb ) 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 0000000000000..bf5f058abebd5 --- /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__ */ diff --git a/soc/nxp/imxrt/imxrt10xx/soc.c b/soc/nxp/imxrt/imxrt10xx/soc.c index 852dcf60628f4..29529aa7b7a41 100644 --- a/soc/nxp/imxrt/imxrt10xx/soc.c +++ b/soc/nxp/imxrt/imxrt10xx/soc.c @@ -1,5 +1,5 @@ /* - * Copyright 2017-2023 NXP + * Copyright 2017-2024 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -245,8 +245,9 @@ __weak void clock_init(void) kIOMUXC_GPR_ENET2RefClkMode, true); #endif -#if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(usb1)) && \ - (CONFIG_USB_DC_NXP_EHCI || CONFIG_UDC_NXP_EHCI) +#if ((DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(usb1)) && \ + (CONFIG_USB_DC_NXP_EHCI || CONFIG_UDC_NXP_EHCI)) ||\ + (DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(usbh1)) && (CONFIG_UHC_NXP_EHCI))) CLOCK_EnableUsbhs0PhyPllClock(kCLOCK_Usb480M, DT_PROP_BY_PHANDLE(DT_NODELABEL(usb1), clocks, clock_frequency)); CLOCK_EnableUsbhs0Clock(kCLOCK_Usb480M, @@ -256,8 +257,9 @@ __weak void clock_init(void) #endif #endif -#if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(usb2)) && \ - (CONFIG_USB_DC_NXP_EHCI || CONFIG_UDC_NXP_EHCI) +#if ((DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(usb2)) && \ + (CONFIG_USB_DC_NXP_EHCI || CONFIG_UDC_NXP_EHCI)) ||\ + (DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(usbh2)) && (CONFIG_UHC_NXP_EHCI))) CLOCK_EnableUsbhs1PhyPllClock(kCLOCK_Usb480M, DT_PROP_BY_PHANDLE(DT_NODELABEL(usb2), clocks, clock_frequency)); CLOCK_EnableUsbhs1Clock(kCLOCK_Usb480M, diff --git a/soc/nxp/imxrt/imxrt6xx/cm33/CMakeLists.txt b/soc/nxp/imxrt/imxrt6xx/cm33/CMakeLists.txt index 8ea9e211c2b9b..014a1d4c74359 100644 --- a/soc/nxp/imxrt/imxrt6xx/cm33/CMakeLists.txt +++ b/soc/nxp/imxrt/imxrt6xx/cm33/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, NXP +# Copyright (c) 2020,2024 NXP # # SPDX-License-Identifier: Apache-2.0 # @@ -19,6 +19,7 @@ zephyr_library_include_directories( zephyr_compile_definitions_ifdef(CONFIG_USB_DEVICE_DRIVER USB_STACK_USE_DEDICATED_RAM=1) zephyr_compile_definitions_ifdef(CONFIG_UDC_DRIVER USB_STACK_USE_DEDICATED_RAM=1) +zephyr_compile_definitions_ifdef(CONFIG_UHC_DRIVER USB_STACK_USE_DEDICATED_RAM=1) if(CONFIG_FLASH_MCUX_FLEXSPI_XIP) zephyr_code_relocate(FILES flash_clock_setup.c LOCATION RAM) diff --git a/soc/nxp/kinetis/k2x/soc.c b/soc/nxp/kinetis/k2x/soc.c index 069e5a862412d..7d60fb2e1a3f1 100644 --- a/soc/nxp/kinetis/k2x/soc.c +++ b/soc/nxp/kinetis/k2x/soc.c @@ -3,6 +3,7 @@ * Copyright (c) 2016, Freescale Semiconductor, Inc. * Copyright (c) 2018 Prevas A/S * Copyright (c) 2019 Thomas Burdick + * Copyright 2024 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -102,7 +103,7 @@ __weak void clock_init(void) CLOCK_SetSimConfig(&simConfig); -#if CONFIG_USB_KINETIS || CONFIG_UDC_KINETIS +#if CONFIG_USB_KINETIS || CONFIG_UDC_KINETIS || CONFIG_UHC_NXP_KHCI CLOCK_EnableUsbfs0Clock(kCLOCK_UsbSrcPll0, CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC); #endif diff --git a/soc/nxp/lpc/lpc55xxx/CMakeLists.txt b/soc/nxp/lpc/lpc55xxx/CMakeLists.txt index 4dae5afa7eee5..e0d7cb005029d 100644 --- a/soc/nxp/lpc/lpc55xxx/CMakeLists.txt +++ b/soc/nxp/lpc/lpc55xxx/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2019, NXP +# Copyright (c) 2019,2024 NXP # # SPDX-License-Identifier: Apache-2.0 # @@ -16,9 +16,12 @@ zephyr_linker_sources_ifdef(CONFIG_USB_DEVICE_DRIVER SECTIONS usb.ld) zephyr_linker_sources_ifdef(CONFIG_UDC_DRIVER SECTIONS usb.ld) + zephyr_linker_sources_ifdef(CONFIG_UHC_DRIVER + SECTIONS usb.ld) zephyr_compile_definitions_ifdef(CONFIG_USB_DEVICE_DRIVER USB_STACK_USE_DEDICATED_RAM=1) zephyr_compile_definitions_ifdef(CONFIG_UDC_DRIVER USB_STACK_USE_DEDICATED_RAM=1) +zephyr_compile_definitions_ifdef(CONFIG_UHC_DRIVER USB_STACK_USE_DEDICATED_RAM=1) endif() # CMSIS SystemInit allows us to skip enabling clock to SRAM banks via diff --git a/soc/nxp/lpc/lpc55xxx/soc.c b/soc/nxp/lpc/lpc55xxx/soc.c index 2d353583336ca..3e3f85b9ce194 100644 --- a/soc/nxp/lpc/lpc55xxx/soc.c +++ b/soc/nxp/lpc/lpc55xxx/soc.c @@ -1,4 +1,4 @@ -/* Copyright 2017, 2019-2023 NXP +/* Copyright 2017, 2019-2024 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -26,7 +26,7 @@ #ifdef CONFIG_GPIO_MCUX_LPC #include #endif -#if CONFIG_USB_DC_NXP_LPCIP3511 || CONFIG_UDC_NXP_IP3511 +#if CONFIG_USB_DC_NXP_LPCIP3511 || CONFIG_UDC_NXP_IP3511 || CONFIG_UHC_NXP_IP3516HS #include "usb_phy.h" #include "usb.h" #endif @@ -287,6 +287,48 @@ __weak void clock_init(void) #endif /* CONFIG_USB_DC_NXP_LPCIP3511 */ +#if CONFIG_UHC_NXP_OHCI + +#if DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(usbhfs), nxp_uhc_ohci, okay) + /* set BOD VBAT level to 1.65V */ + POWER_SetBodVbatLevel(kPOWER_BodVbatLevel1650mv, kPOWER_BodHystLevel50mv, false); + NVIC_ClearPendingIRQ(USB0_IRQn); + NVIC_ClearPendingIRQ(USB0_NEEDCLK_IRQn); + /*< Turn on USB Phy */ +#if defined(CONFIG_SOC_LPC55S36) + POWER_DisablePD(kPDRUNCFG_PD_USBFSPHY); +#else + POWER_DisablePD(kPDRUNCFG_PD_USB0_PHY); +#endif + RESET_PeripheralReset(kUSB1H_RST_SHIFT_RSTn); + RESET_PeripheralReset(kUSB1D_RST_SHIFT_RSTn); + RESET_PeripheralReset(kUSB1_RST_SHIFT_RSTn); + RESET_PeripheralReset(kUSB1RAM_RST_SHIFT_RSTn); + CLOCK_EnableUsbfs0HostClock(kCLOCK_UsbfsSrcPll0, 48000000U); +#if defined(FSL_FEATURE_USBHSD_USB_RAM) && (FSL_FEATURE_USBHSD_USB_RAM) + memset((uint8_t *)FSL_FEATURE_USBHSD_USB_RAM_BASE_ADDRESS, 0, FSL_FEATURE_USBHSD_USB_RAM); +#endif +#endif + +#endif + +#if CONFIG_UHC_NXP_IP3516HS + +#if DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(usbhhs), nxp_uhc_ip3516hs, okay) + /*< Turn on USB Phy */ +#if !defined(CONFIG_SOC_LPC55S36) + POWER_DisablePD(kPDRUNCFG_PD_USB1_PHY); +#endif + CLOCK_EnableUsbhs0PhyPllClock(kCLOCK_UsbPhySrcExt, CLK_CLK_IN); + CLOCK_EnableUsbhs0HostClock(kCLOCK_UsbSrcUnused, 0U); + USB_EhciPhyInit(kUSB_ControllerLpcIp3511Hs0, CLK_CLK_IN, NULL); +#if defined(FSL_FEATURE_USBHSD_USB_RAM) && (FSL_FEATURE_USBHSD_USB_RAM) + memset((uint8_t *)FSL_FEATURE_USBHSD_USB_RAM_BASE_ADDRESS, 0, FSL_FEATURE_USBHSD_USB_RAM); +#endif +#endif + +#endif + DT_FOREACH_STATUS_OKAY(nxp_lpc_ctimer, CTIMER_CLOCK_SETUP) DT_FOREACH_STATUS_OKAY(nxp_ctimer_pwm, CTIMER_CLOCK_SETUP) diff --git a/west.yml b/west.yml index e25b25630e309..a7bf8430de612 100644 --- a/west.yml +++ b/west.yml @@ -210,7 +210,7 @@ manifest: groups: - hal - name: hal_nxp - revision: 73d478f7afff753c9cf60248e0c0fff4ac572ee0 + revision: cea5006f1829520e40db1b550a9f49cc69ceba7d path: modules/hal/nxp groups: - hal