drivers: udc: implement udc_mcux_ehci and udc_mcux_ip3511
udc_mcux_ehci is based on the MCUX USB controller driver (usb_device_ehci.c); udc_mcux_ip3511 is based on the MCUX USB controller driver (usb_device_lpcip3511.c); add related Kconfig and CMake; include the usb_phy.h path in modules/hal_nxp/usb/CMakeLists.txt because udc_mcux.c use it; add related macros to usb_device_config.h; update CMakeLists for udc_mcux_ehci and udc_mcux_ip3511. Signed-off-by: Mark Wang <yichang.wang@nxp.com>
This commit is contained in:
parent
a4e411bd13
commit
b6b43c3ed7
8 changed files with 1899 additions and 1 deletions
|
@ -13,3 +13,5 @@ zephyr_library_sources_ifdef(CONFIG_UDC_SKELETON udc_skeleton.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_UDC_VIRTUAL udc_virtual.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_UDC_STM32 udc_stm32.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_UDC_IT82XX2 udc_it82xx2.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_UDC_NXP_EHCI udc_mcux_ehci.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_UDC_NXP_IP3511 udc_mcux_ip3511.c)
|
||||
|
|
|
@ -54,5 +54,6 @@ source "drivers/usb/udc/Kconfig.skeleton"
|
|||
source "drivers/usb/udc/Kconfig.virtual"
|
||||
source "drivers/usb/udc/Kconfig.stm32"
|
||||
source "drivers/usb/udc/Kconfig.it82xx2"
|
||||
source "drivers/usb/udc/Kconfig.mcux"
|
||||
|
||||
endif # UDC_DRIVER
|
||||
|
|
17
drivers/usb/udc/Kconfig.mcux
Normal file
17
drivers/usb/udc/Kconfig.mcux
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Copyright 2024 NXP
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config UDC_NXP_EHCI
|
||||
bool "NXP MCUX USB EHCI Device controller driver"
|
||||
default y
|
||||
depends on DT_HAS_NXP_EHCI_ENABLED
|
||||
select NOCACHE_MEMORY if HAS_MCUX_CACHE && CPU_HAS_DCACHE
|
||||
help
|
||||
NXP MCUX USB Device Controller Driver for EHCI.
|
||||
|
||||
config UDC_NXP_IP3511
|
||||
bool "NXP MCUX USB IP3511 Device controller driver"
|
||||
default y
|
||||
depends on DT_HAS_NXP_LPCIP3511_ENABLED
|
||||
help
|
||||
NXP MCUX USB Device Controller Driver for KHCI.
|
929
drivers/usb/udc/udc_mcux_ehci.c
Normal file
929
drivers/usb/udc/udc_mcux_ehci.c
Normal file
|
@ -0,0 +1,929 @@
|
|||
/*
|
||||
* Copyright 2024 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#define DT_DRV_COMPAT nxp_ehci
|
||||
|
||||
#include <soc.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/drivers/usb/udc.h>
|
||||
#include <zephyr/drivers/pinctrl.h>
|
||||
|
||||
#include "udc_common.h"
|
||||
#include "usb.h"
|
||||
#include "usb_device_config.h"
|
||||
#include "usb_device_mcux_drv_port.h"
|
||||
#include "usb_device_ehci.h"
|
||||
#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED
|
||||
#include "usb_phy.h"
|
||||
#endif
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(udc_mcux, CONFIG_UDC_DRIVER_LOG_LEVEL);
|
||||
|
||||
/*
|
||||
* There is no real advantage to change control endpoint size
|
||||
* but we can use it for testing UDC driver API and higher layers.
|
||||
*/
|
||||
#define USB_MCUX_MPS0 UDC_MPS0_64
|
||||
#define USB_MCUX_EP0_SIZE 64
|
||||
|
||||
#define PRV_DATA_HANDLE(_handle) CONTAINER_OF(_handle, struct udc_mcux_data, mcux_device)
|
||||
|
||||
struct udc_mcux_config {
|
||||
const usb_device_controller_interface_struct_t *mcux_if;
|
||||
void (*irq_enable_func)(const struct device *dev);
|
||||
void (*irq_disable_func)(const struct device *dev);
|
||||
size_t num_of_eps;
|
||||
struct udc_ep_config *ep_cfg_in;
|
||||
struct udc_ep_config *ep_cfg_out;
|
||||
uintptr_t base;
|
||||
const struct pinctrl_dev_config *pincfg;
|
||||
#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED
|
||||
usb_phy_config_struct_t *phy_config;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct udc_mcux_data {
|
||||
const struct device *dev;
|
||||
usb_device_struct_t mcux_device;
|
||||
uint8_t controller_id; /* 0xFF is invalid value */
|
||||
};
|
||||
|
||||
/* TODO: implement the cache maintenance
|
||||
* solution1: Use the non-cached buf to do memcpy before/after giving buffer to usb controller.
|
||||
* solution2: Use cache API to flush/invalid cache. but it needs the given buffer is
|
||||
* cache line size aligned and the buffer range cover multiple of cache line size block.
|
||||
* Need to change the usb stack to implement it, will try to implement it later.
|
||||
*/
|
||||
#if defined(CONFIG_NOCACHE_MEMORY)
|
||||
K_HEAP_DEFINE_NOCACHE(mcux_packet_alloc_pool, USB_DEVICE_CONFIG_ENDPOINTS * 2u * 1024u);
|
||||
|
||||
/* allocate non-cached buffer for usb */
|
||||
static void *udc_mcux_nocache_alloc(uint32_t size)
|
||||
{
|
||||
void *p = (void *)k_heap_alloc(&mcux_packet_alloc_pool, size, K_NO_WAIT);
|
||||
|
||||
if (p != NULL) {
|
||||
(void)memset(p, 0, size);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/* free the allocated non-cached buffer */
|
||||
static void udc_mcux_nocache_free(void *p)
|
||||
{
|
||||
if (p == NULL) {
|
||||
return;
|
||||
}
|
||||
k_heap_free(&mcux_packet_alloc_pool, p);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int udc_mcux_control(const struct device *dev, usb_device_control_type_t command,
|
||||
void *param)
|
||||
{
|
||||
const struct udc_mcux_config *config = dev->config;
|
||||
const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if;
|
||||
struct udc_mcux_data *priv = udc_get_private(dev);
|
||||
usb_status_t status;
|
||||
|
||||
status = mcux_if->deviceControl(priv->mcux_device.controllerHandle,
|
||||
command, param);
|
||||
|
||||
if (status != kStatus_USB_Success) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If ep is busy, return busy. Otherwise feed the buf to controller */
|
||||
static int udc_mcux_ep_feed(const struct device *dev,
|
||||
struct udc_ep_config *const cfg,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
const struct udc_mcux_config *config = dev->config;
|
||||
const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if;
|
||||
struct udc_mcux_data *priv = udc_get_private(dev);
|
||||
usb_status_t status = kStatus_USB_Success;
|
||||
uint8_t *data;
|
||||
uint32_t len;
|
||||
unsigned int key;
|
||||
usb_device_endpoint_status_struct_t ep_status;
|
||||
|
||||
ep_status.endpointAddress = cfg->addr;
|
||||
udc_mcux_control(dev, kUSB_DeviceControlGetEndpointStatus, &ep_status);
|
||||
if (ep_status.endpointStatus == kUSB_DeviceEndpointStateStalled) {
|
||||
return -EACCES; /* stalled */
|
||||
}
|
||||
|
||||
key = irq_lock();
|
||||
if (!udc_ep_is_busy(dev, cfg->addr)) {
|
||||
udc_ep_set_busy(dev, cfg->addr, true);
|
||||
irq_unlock(key);
|
||||
|
||||
if (USB_EP_DIR_IS_OUT(cfg->addr)) {
|
||||
len = net_buf_tailroom(buf);
|
||||
#if defined(CONFIG_NOCACHE_MEMORY)
|
||||
data = (len == 0 ? NULL : udc_mcux_nocache_alloc(len));
|
||||
#else
|
||||
data = net_buf_tail(buf);
|
||||
#endif
|
||||
status = mcux_if->deviceRecv(priv->mcux_device.controllerHandle,
|
||||
cfg->addr, data, len);
|
||||
} else {
|
||||
len = buf->len;
|
||||
#if defined(CONFIG_NOCACHE_MEMORY)
|
||||
data = (len == 0 ? NULL : udc_mcux_nocache_alloc(len));
|
||||
memcpy(data, buf->data, len);
|
||||
#else
|
||||
data = buf->data;
|
||||
#endif
|
||||
status = mcux_if->deviceSend(priv->mcux_device.controllerHandle,
|
||||
cfg->addr, data, len);
|
||||
}
|
||||
|
||||
key = irq_lock();
|
||||
if (status != kStatus_USB_Success) {
|
||||
udc_ep_set_busy(dev, cfg->addr, false);
|
||||
}
|
||||
irq_unlock(key);
|
||||
} else {
|
||||
irq_unlock(key);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return (status == kStatus_USB_Success ? 0 : -EIO);
|
||||
}
|
||||
|
||||
/* return success if the ep is busy or stalled. */
|
||||
static int udc_mcux_ep_try_feed(const struct device *dev,
|
||||
struct udc_ep_config *const cfg)
|
||||
{
|
||||
struct net_buf *feed_buf;
|
||||
|
||||
feed_buf = udc_buf_peek(dev, cfg->addr);
|
||||
if (feed_buf) {
|
||||
int ret = udc_mcux_ep_feed(dev, cfg, feed_buf);
|
||||
|
||||
return ((ret == -EBUSY || ret == -EACCES || ret == 0) ? 0 : -EIO);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate buffer and initiate a new control OUT transfer.
|
||||
*/
|
||||
static int udc_mcux_ctrl_feed_dout(const struct device *dev,
|
||||
const size_t length)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT);
|
||||
int ret;
|
||||
|
||||
buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length);
|
||||
if (buf == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
net_buf_put(&cfg->fifo, buf);
|
||||
|
||||
ret = udc_mcux_ep_feed(dev, cfg, buf);
|
||||
|
||||
if (ret) {
|
||||
net_buf_unref(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udc_mcux_handler_setup(const struct device *dev, struct usb_setup_packet *setup)
|
||||
{
|
||||
int err;
|
||||
struct net_buf *buf;
|
||||
|
||||
LOG_DBG("setup packet");
|
||||
buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT,
|
||||
sizeof(struct usb_setup_packet));
|
||||
if (buf == NULL) {
|
||||
LOG_ERR("Failed to allocate for setup");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
udc_ep_buf_set_setup(buf);
|
||||
memcpy(buf->data, setup, 8);
|
||||
net_buf_add(buf, 8);
|
||||
|
||||
if (setup->RequestType.type == USB_REQTYPE_TYPE_STANDARD &&
|
||||
setup->RequestType.direction == USB_REQTYPE_DIR_TO_DEVICE &&
|
||||
setup->bRequest == USB_SREQ_SET_ADDRESS &&
|
||||
setup->wLength == 0) {
|
||||
udc_mcux_control(dev, kUSB_DeviceControlPreSetDeviceAddress,
|
||||
&setup->wValue);
|
||||
}
|
||||
|
||||
/* Update to next stage of control transfer */
|
||||
udc_ctrl_update_stage(dev, buf);
|
||||
|
||||
if (!buf->len) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (udc_ctrl_stage_is_data_out(dev)) {
|
||||
/* Allocate and feed buffer for data OUT stage */
|
||||
LOG_DBG("s:%p|feed for -out-", buf);
|
||||
err = udc_mcux_ctrl_feed_dout(dev, udc_data_stage_length(buf));
|
||||
if (err == -ENOMEM) {
|
||||
err = udc_submit_ep_event(dev, buf, err);
|
||||
}
|
||||
} else if (udc_ctrl_stage_is_data_in(dev)) {
|
||||
err = udc_ctrl_submit_s_in_status(dev);
|
||||
} else {
|
||||
err = udc_ctrl_submit_s_status(dev);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int udc_mcux_handler_ctrl_out(const struct device *dev, struct net_buf *buf,
|
||||
uint8_t *mcux_buf, uint16_t mcux_len)
|
||||
{
|
||||
int err = 0;
|
||||
uint32_t len;
|
||||
|
||||
len = MIN(net_buf_tailroom(buf), mcux_len);
|
||||
#if defined(CONFIG_NOCACHE_MEMORY)
|
||||
memcpy(net_buf_tail(buf), mcux_buf, len);
|
||||
udc_mcux_nocache_free(mcux_buf);
|
||||
#endif
|
||||
net_buf_add(buf, len);
|
||||
if (udc_ctrl_stage_is_status_out(dev)) {
|
||||
/* Update to next stage of control transfer */
|
||||
udc_ctrl_update_stage(dev, buf);
|
||||
/* Status stage finished, notify upper layer */
|
||||
err = udc_ctrl_submit_status(dev, buf);
|
||||
} else {
|
||||
/* Update to next stage of control transfer */
|
||||
udc_ctrl_update_stage(dev, buf);
|
||||
}
|
||||
|
||||
if (udc_ctrl_stage_is_status_in(dev)) {
|
||||
err = udc_ctrl_submit_s_out_status(dev, buf);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int udc_mcux_handler_ctrl_in(const struct device *dev, struct net_buf *buf,
|
||||
uint8_t *mcux_buf, uint16_t mcux_len)
|
||||
{
|
||||
int err = 0;
|
||||
uint32_t len;
|
||||
|
||||
len = MIN(buf->len, mcux_len);
|
||||
buf->data += len;
|
||||
buf->len -= len;
|
||||
#if defined(CONFIG_NOCACHE_MEMORY)
|
||||
udc_mcux_nocache_free(mcux_buf);
|
||||
#endif
|
||||
|
||||
if (udc_ctrl_stage_is_status_in(dev) ||
|
||||
udc_ctrl_stage_is_no_data(dev)) {
|
||||
/* Status stage finished, notify upper layer */
|
||||
err = udc_ctrl_submit_status(dev, buf);
|
||||
}
|
||||
|
||||
/* Update to next stage of control transfer */
|
||||
udc_ctrl_update_stage(dev, buf);
|
||||
|
||||
if (udc_ctrl_stage_is_status_out(dev)) {
|
||||
/*
|
||||
* IN transfer finished, release buffer,
|
||||
* control OUT buffer should be already fed.
|
||||
*/
|
||||
net_buf_unref(buf);
|
||||
err = udc_mcux_ctrl_feed_dout(dev, 0u);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int udc_mcux_handler_non_ctrl_in(const struct device *dev, uint8_t ep,
|
||||
struct net_buf *buf, uint8_t *mcux_buf, uint16_t mcux_len)
|
||||
{
|
||||
int err;
|
||||
uint32_t len;
|
||||
|
||||
len = MIN(buf->len, mcux_len);
|
||||
buf->data += len;
|
||||
buf->len -= len;
|
||||
|
||||
#if defined(CONFIG_NOCACHE_MEMORY)
|
||||
udc_mcux_nocache_free(mcux_buf);
|
||||
#endif
|
||||
err = udc_submit_ep_event(dev, buf, 0);
|
||||
udc_mcux_ep_try_feed(dev, udc_get_ep_cfg(dev, ep));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int udc_mcux_handler_non_ctrl_out(const struct device *dev, uint8_t ep,
|
||||
struct net_buf *buf, uint8_t *mcux_buf, uint16_t mcux_len)
|
||||
{
|
||||
int err;
|
||||
uint32_t len;
|
||||
|
||||
len = MIN(net_buf_tailroom(buf), mcux_len);
|
||||
#if defined(CONFIG_NOCACHE_MEMORY)
|
||||
memcpy(net_buf_tail(buf), mcux_buf, len);
|
||||
#endif
|
||||
net_buf_add(buf, len);
|
||||
|
||||
#if defined(CONFIG_NOCACHE_MEMORY)
|
||||
udc_mcux_nocache_free(mcux_buf);
|
||||
#endif
|
||||
err = udc_submit_ep_event(dev, buf, 0);
|
||||
udc_mcux_ep_try_feed(dev, udc_get_ep_cfg(dev, ep));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int udc_mcux_handler_out(const struct device *dev, uint8_t ep,
|
||||
uint8_t *mcux_buf, uint16_t mcux_len)
|
||||
{
|
||||
int err;
|
||||
struct net_buf *buf;
|
||||
unsigned int key;
|
||||
|
||||
buf = udc_buf_get(dev, ep);
|
||||
|
||||
key = irq_lock();
|
||||
udc_ep_set_busy(dev, ep, false);
|
||||
irq_unlock(key);
|
||||
|
||||
if (buf == NULL) {
|
||||
udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
if (ep == USB_CONTROL_EP_OUT) {
|
||||
err = udc_mcux_handler_ctrl_out(dev, buf, mcux_buf, mcux_len);
|
||||
} else {
|
||||
err = udc_mcux_handler_non_ctrl_out(dev, ep, buf, mcux_buf, mcux_len);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* return true - zlp is feed; false - no zlp */
|
||||
static bool udc_mcux_handler_zlt(const struct device *dev, uint8_t ep, struct net_buf *buf,
|
||||
uint16_t mcux_len)
|
||||
{
|
||||
const struct udc_mcux_config *config = dev->config;
|
||||
const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if;
|
||||
struct udc_mcux_data *priv = udc_get_private(dev);
|
||||
|
||||
/* The whole transfer is already done by MCUX controller driver. */
|
||||
if (mcux_len >= buf->len) {
|
||||
if (udc_ep_buf_has_zlp(buf)) {
|
||||
usb_status_t status;
|
||||
|
||||
udc_ep_buf_clear_zlp(buf);
|
||||
status = mcux_if->deviceRecv(priv->mcux_device.controllerHandle,
|
||||
ep, NULL, 0);
|
||||
if (status != kStatus_USB_Success) {
|
||||
udc_submit_event(dev, UDC_EVT_ERROR, -EIO);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int udc_mcux_handler_in(const struct device *dev, uint8_t ep,
|
||||
uint8_t *mcux_buf, uint16_t mcux_len)
|
||||
{
|
||||
int err;
|
||||
struct net_buf *buf;
|
||||
unsigned int key;
|
||||
|
||||
buf = udc_buf_peek(dev, ep);
|
||||
if (buf == NULL) {
|
||||
udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
if (udc_mcux_handler_zlt(dev, ep, buf, mcux_len)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf = udc_buf_get(dev, ep);
|
||||
|
||||
key = irq_lock();
|
||||
udc_ep_set_busy(dev, ep, false);
|
||||
irq_unlock(key);
|
||||
|
||||
if (buf == NULL) {
|
||||
udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
if (ep == USB_CONTROL_EP_IN) {
|
||||
err = udc_mcux_handler_ctrl_in(dev, buf, mcux_buf, mcux_len);
|
||||
} else {
|
||||
err = udc_mcux_handler_non_ctrl_in(dev, ep, buf, mcux_buf, mcux_len);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* NXP MCUX controller driver notify transfers/status through this interface */
|
||||
usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg)
|
||||
{
|
||||
usb_device_callback_message_struct_t *mcux_msg = msg;
|
||||
uint8_t ep;
|
||||
usb_device_notification_t mcux_notify;
|
||||
struct udc_mcux_data *priv;
|
||||
const struct device *dev;
|
||||
usb_status_t mcux_status = kStatus_USB_Success;
|
||||
int err = 0;
|
||||
|
||||
if ((NULL == msg) || (NULL == handle)) {
|
||||
return kStatus_USB_InvalidHandle;
|
||||
}
|
||||
|
||||
mcux_notify = (usb_device_notification_t)mcux_msg->code;
|
||||
priv = (struct udc_mcux_data *)(PRV_DATA_HANDLE(handle));
|
||||
dev = priv->dev;
|
||||
|
||||
switch (mcux_notify) {
|
||||
case kUSB_DeviceNotifyBusReset:
|
||||
struct udc_ep_config *cfg;
|
||||
|
||||
udc_mcux_control(dev, kUSB_DeviceControlSetDefaultStatus, NULL);
|
||||
cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT);
|
||||
if (cfg->stat.enabled) {
|
||||
udc_ep_disable_internal(dev, USB_CONTROL_EP_OUT);
|
||||
}
|
||||
cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN);
|
||||
if (cfg->stat.enabled) {
|
||||
udc_ep_disable_internal(dev, USB_CONTROL_EP_IN);
|
||||
}
|
||||
if (udc_ep_enable_internal(dev, USB_CONTROL_EP_OUT,
|
||||
USB_EP_TYPE_CONTROL,
|
||||
USB_MCUX_EP0_SIZE, 0)) {
|
||||
LOG_ERR("Failed to enable control endpoint");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (udc_ep_enable_internal(dev, USB_CONTROL_EP_IN,
|
||||
USB_EP_TYPE_CONTROL,
|
||||
USB_MCUX_EP0_SIZE, 0)) {
|
||||
LOG_ERR("Failed to enable control endpoint");
|
||||
return -EIO;
|
||||
}
|
||||
udc_submit_event(dev, UDC_EVT_RESET, 0);
|
||||
break;
|
||||
case kUSB_DeviceNotifyError:
|
||||
udc_submit_event(dev, UDC_EVT_ERROR, -EIO);
|
||||
break;
|
||||
case kUSB_DeviceNotifySuspend:
|
||||
udc_set_suspended(dev, true);
|
||||
udc_submit_event(dev, UDC_EVT_SUSPEND, 0);
|
||||
break;
|
||||
case kUSB_DeviceNotifyResume:
|
||||
udc_set_suspended(dev, false);
|
||||
udc_submit_event(dev, UDC_EVT_RESUME, 0);
|
||||
break;
|
||||
case kUSB_DeviceNotifyLPMSleep:
|
||||
break;
|
||||
case kUSB_DeviceNotifyDetach:
|
||||
udc_submit_event(dev, UDC_EVT_VBUS_REMOVED, 0);
|
||||
break;
|
||||
case kUSB_DeviceNotifyAttach:
|
||||
udc_submit_event(dev, UDC_EVT_VBUS_READY, 0);
|
||||
break;
|
||||
default:
|
||||
ep = mcux_msg->code;
|
||||
if (mcux_msg->isSetup) {
|
||||
struct usb_setup_packet *setup =
|
||||
(struct usb_setup_packet *)mcux_msg->buffer;
|
||||
|
||||
err = udc_mcux_handler_setup(dev, setup);
|
||||
} else if (USB_EP_DIR_IS_IN(ep)) {
|
||||
err = udc_mcux_handler_in(dev, ep, mcux_msg->buffer, mcux_msg->length);
|
||||
} else {
|
||||
err = udc_mcux_handler_out(dev, ep, mcux_msg->buffer, mcux_msg->length);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (unlikely(err)) {
|
||||
udc_submit_event(dev, UDC_EVT_ERROR, err);
|
||||
mcux_status = kStatus_USB_Error;
|
||||
}
|
||||
return mcux_status;
|
||||
}
|
||||
|
||||
static void udc_mcux_isr(const struct device *dev)
|
||||
{
|
||||
struct udc_mcux_data *priv = udc_get_private(dev);
|
||||
|
||||
USB_DeviceEhciIsrFunction((void *)(&priv->mcux_device));
|
||||
}
|
||||
|
||||
/* Return actual USB device speed */
|
||||
static enum udc_bus_speed udc_mcux_device_speed(const struct device *dev)
|
||||
{
|
||||
int err;
|
||||
uint8_t mcux_speed;
|
||||
|
||||
err = udc_mcux_control(dev, kUSB_DeviceControlGetSpeed, &mcux_speed);
|
||||
if (err) {
|
||||
/*
|
||||
* In the current version of all NXP USB device drivers,
|
||||
* no error is returned if the parameter is correct.
|
||||
*/
|
||||
return UDC_BUS_SPEED_FS;
|
||||
}
|
||||
|
||||
switch (mcux_speed) {
|
||||
case USB_SPEED_HIGH:
|
||||
return UDC_BUS_SPEED_HS;
|
||||
case USB_SPEED_LOW:
|
||||
__ASSERT(false, "Low speed mode not supported");
|
||||
__fallthrough;
|
||||
case USB_SPEED_FULL:
|
||||
__fallthrough;
|
||||
default:
|
||||
return UDC_BUS_SPEED_FS;
|
||||
}
|
||||
}
|
||||
|
||||
static int udc_mcux_ep_enqueue(const struct device *dev,
|
||||
struct udc_ep_config *const cfg,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
udc_buf_put(cfg, buf);
|
||||
if (cfg->stat.halted) {
|
||||
LOG_DBG("ep 0x%02x halted", cfg->addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return udc_mcux_ep_try_feed(dev, cfg);
|
||||
}
|
||||
|
||||
static int udc_mcux_ep_dequeue(const struct device *dev,
|
||||
struct udc_ep_config *const cfg)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
unsigned int key;
|
||||
|
||||
cfg->stat.halted = false;
|
||||
buf = udc_buf_get_all(dev, cfg->addr);
|
||||
if (buf) {
|
||||
udc_submit_ep_event(dev, buf, -ECONNABORTED);
|
||||
}
|
||||
|
||||
key = irq_lock();
|
||||
udc_ep_set_busy(dev, cfg->addr, false);
|
||||
irq_unlock(key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udc_mcux_ep_set_halt(const struct device *dev,
|
||||
struct udc_ep_config *const cfg)
|
||||
{
|
||||
return udc_mcux_control(dev, kUSB_DeviceControlEndpointStall, &cfg->addr);
|
||||
}
|
||||
|
||||
static int udc_mcux_ep_clear_halt(const struct device *dev,
|
||||
struct udc_ep_config *const cfg)
|
||||
{
|
||||
(void)udc_mcux_control(dev, kUSB_DeviceControlEndpointUnstall, &cfg->addr);
|
||||
/* transfer is enqueued after stalled */
|
||||
return udc_mcux_ep_try_feed(dev, cfg);
|
||||
}
|
||||
|
||||
static int udc_mcux_ep_enable(const struct device *dev,
|
||||
struct udc_ep_config *const cfg)
|
||||
{
|
||||
usb_device_endpoint_init_struct_t ep_init;
|
||||
|
||||
LOG_DBG("Enable ep 0x%02x", cfg->addr);
|
||||
|
||||
ep_init.zlt = 0U;
|
||||
ep_init.interval = cfg->interval;
|
||||
ep_init.endpointAddress = cfg->addr;
|
||||
ep_init.maxPacketSize = cfg->mps;
|
||||
|
||||
switch (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) {
|
||||
case USB_EP_TYPE_CONTROL:
|
||||
ep_init.transferType = USB_ENDPOINT_CONTROL;
|
||||
break;
|
||||
case USB_EP_TYPE_BULK:
|
||||
ep_init.transferType = USB_ENDPOINT_BULK;
|
||||
break;
|
||||
case USB_EP_TYPE_INTERRUPT:
|
||||
ep_init.transferType = USB_ENDPOINT_INTERRUPT;
|
||||
break;
|
||||
case USB_EP_TYPE_ISO:
|
||||
ep_init.transferType = USB_ENDPOINT_ISOCHRONOUS;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return udc_mcux_control(dev, kUSB_DeviceControlEndpointInit, &ep_init);
|
||||
}
|
||||
|
||||
static int udc_mcux_ep_disable(const struct device *dev,
|
||||
struct udc_ep_config *const cfg)
|
||||
{
|
||||
LOG_DBG("Disable ep 0x%02x", cfg->addr);
|
||||
|
||||
return udc_mcux_control(dev, kUSB_DeviceControlEndpointDeinit, &cfg->addr);
|
||||
}
|
||||
|
||||
static int udc_mcux_host_wakeup(const struct device *dev)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int udc_mcux_set_address(const struct device *dev, const uint8_t addr)
|
||||
{
|
||||
uint8_t temp_addr = addr;
|
||||
|
||||
return udc_mcux_control(dev, kUSB_DeviceControlSetDeviceAddress, &temp_addr);
|
||||
}
|
||||
|
||||
static int udc_mcux_enable(const struct device *dev)
|
||||
{
|
||||
return udc_mcux_control(dev, kUSB_DeviceControlRun, NULL);
|
||||
}
|
||||
|
||||
static int udc_mcux_disable(const struct device *dev)
|
||||
{
|
||||
return udc_mcux_control(dev, kUSB_DeviceControlStop, NULL);
|
||||
}
|
||||
|
||||
static int udc_mcux_init(const struct device *dev)
|
||||
{
|
||||
const struct udc_mcux_config *config = dev->config;
|
||||
const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if;
|
||||
struct udc_mcux_data *priv = udc_get_private(dev);
|
||||
usb_status_t status;
|
||||
|
||||
if (priv->controller_id == 0xFFu) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED
|
||||
if (config->phy_config != NULL) {
|
||||
USB_EhciPhyInit(priv->controller_id, 0u,
|
||||
(usb_phy_config_struct_t *)&config->phy_config);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Init MCUX USB device driver. */
|
||||
status = mcux_if->deviceInit(priv->controller_id,
|
||||
&priv->mcux_device, &(priv->mcux_device.controllerHandle));
|
||||
if (status != kStatus_USB_Success) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* enable USB interrupt */
|
||||
config->irq_enable_func(dev);
|
||||
|
||||
LOG_DBG("Initialized USB controller %x", (uint32_t)config->base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udc_mcux_shutdown(const struct device *dev)
|
||||
{
|
||||
const struct udc_mcux_config *config = dev->config;
|
||||
const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if;
|
||||
struct udc_mcux_data *priv = udc_get_private(dev);
|
||||
usb_status_t status;
|
||||
|
||||
/* Disable interrupt */
|
||||
config->irq_disable_func(dev);
|
||||
|
||||
/* De-init MCUX USB device driver. */
|
||||
status = mcux_if->deviceDeinit(priv->mcux_device.controllerHandle);
|
||||
if (status != kStatus_USB_Success) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udc_mcux_lock(const struct device *dev)
|
||||
{
|
||||
return udc_lock_internal(dev, K_FOREVER);
|
||||
}
|
||||
|
||||
static int udc_mcux_unlock(const struct device *dev)
|
||||
{
|
||||
return udc_unlock_internal(dev);
|
||||
}
|
||||
|
||||
static inline void udc_mcux_get_hal_driver_id(struct udc_mcux_data *priv,
|
||||
const struct udc_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.
|
||||
*/
|
||||
#ifdef 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 udc_mcux_driver_preinit(const struct device *dev)
|
||||
{
|
||||
const struct udc_mcux_config *config = dev->config;
|
||||
struct udc_data *data = dev->data;
|
||||
struct udc_mcux_data *priv = data->priv;
|
||||
int err;
|
||||
|
||||
udc_mcux_get_hal_driver_id(priv, config);
|
||||
if (priv->controller_id == 0xFFu) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
k_mutex_init(&data->mutex);
|
||||
|
||||
for (int i = 0; i < config->num_of_eps; i++) {
|
||||
config->ep_cfg_out[i].caps.out = 1;
|
||||
if (i == 0) {
|
||||
config->ep_cfg_out[i].caps.control = 1;
|
||||
config->ep_cfg_out[i].caps.mps = 64;
|
||||
} else {
|
||||
config->ep_cfg_out[i].caps.bulk = 1;
|
||||
config->ep_cfg_out[i].caps.interrupt = 1;
|
||||
config->ep_cfg_out[i].caps.iso = 1;
|
||||
config->ep_cfg_out[i].caps.mps = 1024;
|
||||
}
|
||||
|
||||
config->ep_cfg_out[i].addr = USB_EP_DIR_OUT | i;
|
||||
err = udc_register_ep(dev, &config->ep_cfg_out[i]);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to register endpoint");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < config->num_of_eps; i++) {
|
||||
config->ep_cfg_in[i].caps.in = 1;
|
||||
if (i == 0) {
|
||||
config->ep_cfg_in[i].caps.control = 1;
|
||||
config->ep_cfg_in[i].caps.mps = 64;
|
||||
} else {
|
||||
config->ep_cfg_in[i].caps.bulk = 1;
|
||||
config->ep_cfg_in[i].caps.interrupt = 1;
|
||||
config->ep_cfg_in[i].caps.iso = 1;
|
||||
config->ep_cfg_in[i].caps.mps = 1024;
|
||||
}
|
||||
|
||||
config->ep_cfg_in[i].addr = USB_EP_DIR_IN | i;
|
||||
err = udc_register_ep(dev, &config->ep_cfg_in[i]);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to register endpoint");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* Requires udc_mcux_host_wakeup() implementation */
|
||||
data->caps.rwup = false;
|
||||
data->caps.mps0 = USB_MCUX_MPS0;
|
||||
data->caps.hs = true;
|
||||
priv->dev = dev;
|
||||
|
||||
pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udc_api udc_mcux_api = {
|
||||
.device_speed = udc_mcux_device_speed,
|
||||
.ep_enqueue = udc_mcux_ep_enqueue,
|
||||
.ep_dequeue = udc_mcux_ep_dequeue,
|
||||
.ep_set_halt = udc_mcux_ep_set_halt,
|
||||
.ep_clear_halt = udc_mcux_ep_clear_halt,
|
||||
.ep_try_config = NULL,
|
||||
.ep_enable = udc_mcux_ep_enable,
|
||||
.ep_disable = udc_mcux_ep_disable,
|
||||
.host_wakeup = udc_mcux_host_wakeup,
|
||||
.set_address = udc_mcux_set_address,
|
||||
.enable = udc_mcux_enable,
|
||||
.disable = udc_mcux_disable,
|
||||
.init = udc_mcux_init,
|
||||
.shutdown = udc_mcux_shutdown,
|
||||
.lock = udc_mcux_lock,
|
||||
.unlock = udc_mcux_unlock,
|
||||
};
|
||||
|
||||
/* EHCI device driver interface */
|
||||
static const usb_device_controller_interface_struct_t udc_mcux_if = {
|
||||
USB_DeviceEhciInit, USB_DeviceEhciDeinit, USB_DeviceEhciSend,
|
||||
USB_DeviceEhciRecv, USB_DeviceEhciCancel, USB_DeviceEhciControl
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED
|
||||
#define UDC_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 UDC_MCUX_PHY_DEFINE_OR(n) \
|
||||
COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), \
|
||||
(UDC_MCUX_PHY_DEFINE(n)), ())
|
||||
|
||||
#define UDC_MCUX_PHY_CFG_PTR_OR_NULL(n) \
|
||||
.phy_config = COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), \
|
||||
(&phy_config_##n), (NULL))
|
||||
#else
|
||||
#define UDC_MCUX_PHY_DEFINE_OR(n)
|
||||
#define UDC_MCUX_PHY_CFG_PTR_OR_NULL(n)
|
||||
#endif
|
||||
|
||||
#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); \
|
||||
\
|
||||
irq_enable(DT_INST_IRQN(n)); \
|
||||
} \
|
||||
\
|
||||
static void udc_irq_disable_func##n(const struct device *dev) \
|
||||
{ \
|
||||
irq_disable(DT_INST_IRQN(n)); \
|
||||
} \
|
||||
\
|
||||
static struct udc_ep_config \
|
||||
ep_cfg_out##n[DT_INST_PROP(n, num_bidir_endpoints)]; \
|
||||
static struct udc_ep_config \
|
||||
ep_cfg_in##n[DT_INST_PROP(n, num_bidir_endpoints)]; \
|
||||
\
|
||||
PINCTRL_DT_INST_DEFINE(n); \
|
||||
\
|
||||
static struct udc_mcux_config priv_config_##n = { \
|
||||
.base = DT_INST_REG_ADDR(n), \
|
||||
.irq_enable_func = udc_irq_enable_func##n, \
|
||||
.irq_disable_func = udc_irq_disable_func##n, \
|
||||
.num_of_eps = DT_INST_PROP(n, num_bidir_endpoints), \
|
||||
.ep_cfg_in = ep_cfg_in##n, \
|
||||
.ep_cfg_out = ep_cfg_out##n, \
|
||||
.mcux_if = &udc_mcux_if, \
|
||||
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
|
||||
UDC_MCUX_PHY_CFG_PTR_OR_NULL(n), \
|
||||
}; \
|
||||
\
|
||||
static struct udc_mcux_data priv_data_##n = { \
|
||||
}; \
|
||||
\
|
||||
static struct udc_data udc_data_##n = { \
|
||||
.mutex = Z_MUTEX_INITIALIZER(udc_data_##n.mutex), \
|
||||
.priv = &priv_data_##n, \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, udc_mcux_driver_preinit, NULL, \
|
||||
&udc_data_##n, &priv_config_##n, \
|
||||
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
|
||||
&udc_mcux_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(USB_MCUX_EHCI_DEVICE_DEFINE)
|
890
drivers/usb/udc/udc_mcux_ip3511.c
Normal file
890
drivers/usb/udc/udc_mcux_ip3511.c
Normal file
|
@ -0,0 +1,890 @@
|
|||
/*
|
||||
* Copyright 2024 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#define DT_DRV_COMPAT nxp_lpcip3511
|
||||
|
||||
#include <soc.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/drivers/usb/udc.h>
|
||||
#include <zephyr/drivers/pinctrl.h>
|
||||
|
||||
#include "udc_common.h"
|
||||
#include "usb.h"
|
||||
#include "usb_device_config.h"
|
||||
#include "usb_device_mcux_drv_port.h"
|
||||
#include "usb_device_lpcip3511.h"
|
||||
#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED
|
||||
#include "usb_phy.h"
|
||||
#endif
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(udc_mcux, CONFIG_UDC_DRIVER_LOG_LEVEL);
|
||||
|
||||
/*
|
||||
* There is no real advantage to change control endpoint size
|
||||
* but we can use it for testing UDC driver API and higher layers.
|
||||
*/
|
||||
#define USB_MCUX_MPS0 UDC_MPS0_64
|
||||
#define USB_MCUX_EP0_SIZE 64
|
||||
|
||||
#define PRV_DATA_HANDLE(_handle) CONTAINER_OF(_handle, struct udc_mcux_data, mcux_device)
|
||||
|
||||
struct udc_mcux_config {
|
||||
const usb_device_controller_interface_struct_t *mcux_if;
|
||||
void (*irq_enable_func)(const struct device *dev);
|
||||
void (*irq_disable_func)(const struct device *dev);
|
||||
size_t num_of_eps;
|
||||
struct udc_ep_config *ep_cfg_in;
|
||||
struct udc_ep_config *ep_cfg_out;
|
||||
uintptr_t base;
|
||||
const struct pinctrl_dev_config *pincfg;
|
||||
#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED
|
||||
usb_phy_config_struct_t *phy_config;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct udc_mcux_data {
|
||||
const struct device *dev;
|
||||
usb_device_struct_t mcux_device;
|
||||
uint8_t controller_id; /* 0xFF is invalid value */
|
||||
};
|
||||
|
||||
static int udc_mcux_control(const struct device *dev, usb_device_control_type_t command,
|
||||
void *param)
|
||||
{
|
||||
const struct udc_mcux_config *config = dev->config;
|
||||
const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if;
|
||||
struct udc_mcux_data *priv = udc_get_private(dev);
|
||||
usb_status_t status;
|
||||
|
||||
status = mcux_if->deviceControl(priv->mcux_device.controllerHandle,
|
||||
command, param);
|
||||
|
||||
if (status != kStatus_USB_Success) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If ep is busy, return busy. Otherwise feed the buf to controller */
|
||||
static int udc_mcux_ep_feed(const struct device *dev,
|
||||
struct udc_ep_config *const cfg,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
const struct udc_mcux_config *config = dev->config;
|
||||
const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if;
|
||||
struct udc_mcux_data *priv = udc_get_private(dev);
|
||||
usb_status_t status = kStatus_USB_Success;
|
||||
uint8_t *data;
|
||||
uint32_t len;
|
||||
unsigned int key;
|
||||
usb_device_endpoint_status_struct_t ep_status;
|
||||
|
||||
ep_status.endpointAddress = cfg->addr;
|
||||
udc_mcux_control(dev, kUSB_DeviceControlGetEndpointStatus, &ep_status);
|
||||
if (ep_status.endpointStatus == kUSB_DeviceEndpointStateStalled) {
|
||||
return -EACCES; /* stalled */
|
||||
}
|
||||
|
||||
key = irq_lock();
|
||||
if (!udc_ep_is_busy(dev, cfg->addr)) {
|
||||
udc_ep_set_busy(dev, cfg->addr, true);
|
||||
irq_unlock(key);
|
||||
|
||||
if (USB_EP_DIR_IS_OUT(cfg->addr)) {
|
||||
len = net_buf_tailroom(buf);
|
||||
data = net_buf_tail(buf);
|
||||
status = mcux_if->deviceRecv(priv->mcux_device.controllerHandle,
|
||||
cfg->addr, data, len);
|
||||
} else {
|
||||
len = buf->len;
|
||||
data = buf->data;
|
||||
status = mcux_if->deviceSend(priv->mcux_device.controllerHandle,
|
||||
cfg->addr, data, len);
|
||||
}
|
||||
|
||||
key = irq_lock();
|
||||
if (status != kStatus_USB_Success) {
|
||||
udc_ep_set_busy(dev, cfg->addr, false);
|
||||
}
|
||||
irq_unlock(key);
|
||||
} else {
|
||||
irq_unlock(key);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return (status == kStatus_USB_Success ? 0 : -EIO);
|
||||
}
|
||||
|
||||
/* return success if the ep is busy or stalled. */
|
||||
static int udc_mcux_ep_try_feed(const struct device *dev,
|
||||
struct udc_ep_config *const cfg)
|
||||
{
|
||||
struct net_buf *feed_buf;
|
||||
|
||||
feed_buf = udc_buf_peek(dev, cfg->addr);
|
||||
if (feed_buf) {
|
||||
int ret = udc_mcux_ep_feed(dev, cfg, feed_buf);
|
||||
|
||||
return ((ret == -EBUSY || ret == -EACCES || ret == 0) ? 0 : -EIO);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate buffer and initiate a new control OUT transfer.
|
||||
*/
|
||||
static int udc_mcux_ctrl_feed_dout(const struct device *dev,
|
||||
const size_t length)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT);
|
||||
int ret;
|
||||
|
||||
buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length);
|
||||
if (buf == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
net_buf_put(&cfg->fifo, buf);
|
||||
|
||||
ret = udc_mcux_ep_feed(dev, cfg, buf);
|
||||
|
||||
if (ret) {
|
||||
net_buf_unref(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udc_mcux_handler_setup(const struct device *dev, struct usb_setup_packet *setup)
|
||||
{
|
||||
int err;
|
||||
struct net_buf *buf;
|
||||
|
||||
LOG_DBG("setup packet");
|
||||
buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT,
|
||||
sizeof(struct usb_setup_packet));
|
||||
if (buf == NULL) {
|
||||
LOG_ERR("Failed to allocate for setup");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
udc_ep_buf_set_setup(buf);
|
||||
memcpy(buf->data, setup, 8);
|
||||
net_buf_add(buf, 8);
|
||||
|
||||
if (setup->RequestType.type == USB_REQTYPE_TYPE_STANDARD &&
|
||||
setup->RequestType.direction == USB_REQTYPE_DIR_TO_DEVICE &&
|
||||
setup->bRequest == USB_SREQ_SET_ADDRESS &&
|
||||
setup->wLength == 0) {
|
||||
udc_mcux_control(dev, kUSB_DeviceControlPreSetDeviceAddress,
|
||||
&setup->wValue);
|
||||
}
|
||||
|
||||
/* Update to next stage of control transfer */
|
||||
udc_ctrl_update_stage(dev, buf);
|
||||
|
||||
if (!buf->len) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (udc_ctrl_stage_is_data_out(dev)) {
|
||||
/* Allocate and feed buffer for data OUT stage */
|
||||
LOG_DBG("s:%p|feed for -out-", buf);
|
||||
err = udc_mcux_ctrl_feed_dout(dev, udc_data_stage_length(buf));
|
||||
if (err == -ENOMEM) {
|
||||
err = udc_submit_ep_event(dev, buf, err);
|
||||
}
|
||||
} else if (udc_ctrl_stage_is_data_in(dev)) {
|
||||
err = udc_ctrl_submit_s_in_status(dev);
|
||||
} else {
|
||||
err = udc_ctrl_submit_s_status(dev);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int udc_mcux_handler_ctrl_out(const struct device *dev, struct net_buf *buf,
|
||||
uint8_t *mcux_buf, uint16_t mcux_len)
|
||||
{
|
||||
int err = 0;
|
||||
uint32_t len;
|
||||
|
||||
len = MIN(net_buf_tailroom(buf), mcux_len);
|
||||
net_buf_add(buf, len);
|
||||
if (udc_ctrl_stage_is_status_out(dev)) {
|
||||
/* Update to next stage of control transfer */
|
||||
udc_ctrl_update_stage(dev, buf);
|
||||
/* Status stage finished, notify upper layer */
|
||||
err = udc_ctrl_submit_status(dev, buf);
|
||||
} else {
|
||||
/* Update to next stage of control transfer */
|
||||
udc_ctrl_update_stage(dev, buf);
|
||||
}
|
||||
|
||||
if (udc_ctrl_stage_is_status_in(dev)) {
|
||||
err = udc_ctrl_submit_s_out_status(dev, buf);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int udc_mcux_handler_ctrl_in(const struct device *dev, struct net_buf *buf,
|
||||
uint8_t *mcux_buf, uint16_t mcux_len)
|
||||
{
|
||||
int err = 0;
|
||||
uint32_t len;
|
||||
|
||||
len = MIN(buf->len, mcux_len);
|
||||
buf->data += len;
|
||||
buf->len -= len;
|
||||
|
||||
if (udc_ctrl_stage_is_status_in(dev) ||
|
||||
udc_ctrl_stage_is_no_data(dev)) {
|
||||
/* Status stage finished, notify upper layer */
|
||||
err = udc_ctrl_submit_status(dev, buf);
|
||||
}
|
||||
|
||||
/* Update to next stage of control transfer */
|
||||
udc_ctrl_update_stage(dev, buf);
|
||||
|
||||
if (udc_ctrl_stage_is_status_out(dev)) {
|
||||
/*
|
||||
* IN transfer finished, release buffer,
|
||||
* control OUT buffer should be already fed.
|
||||
*/
|
||||
net_buf_unref(buf);
|
||||
err = udc_mcux_ctrl_feed_dout(dev, 0u);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int udc_mcux_handler_non_ctrl_in(const struct device *dev, uint8_t ep,
|
||||
struct net_buf *buf, uint8_t *mcux_buf, uint16_t mcux_len)
|
||||
{
|
||||
int err;
|
||||
uint32_t len;
|
||||
|
||||
len = MIN(buf->len, mcux_len);
|
||||
buf->data += len;
|
||||
buf->len -= len;
|
||||
|
||||
err = udc_submit_ep_event(dev, buf, 0);
|
||||
udc_mcux_ep_try_feed(dev, udc_get_ep_cfg(dev, ep));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int udc_mcux_handler_non_ctrl_out(const struct device *dev, uint8_t ep,
|
||||
struct net_buf *buf, uint8_t *mcux_buf, uint16_t mcux_len)
|
||||
{
|
||||
int err;
|
||||
uint32_t len;
|
||||
|
||||
len = MIN(net_buf_tailroom(buf), mcux_len);
|
||||
net_buf_add(buf, len);
|
||||
|
||||
err = udc_submit_ep_event(dev, buf, 0);
|
||||
udc_mcux_ep_try_feed(dev, udc_get_ep_cfg(dev, ep));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int udc_mcux_handler_out(const struct device *dev, uint8_t ep,
|
||||
uint8_t *mcux_buf, uint16_t mcux_len)
|
||||
{
|
||||
int err;
|
||||
struct net_buf *buf;
|
||||
unsigned int key;
|
||||
|
||||
buf = udc_buf_get(dev, ep);
|
||||
|
||||
key = irq_lock();
|
||||
udc_ep_set_busy(dev, ep, false);
|
||||
irq_unlock(key);
|
||||
|
||||
if (buf == NULL) {
|
||||
udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
if (ep == USB_CONTROL_EP_OUT) {
|
||||
err = udc_mcux_handler_ctrl_out(dev, buf, mcux_buf, mcux_len);
|
||||
} else {
|
||||
err = udc_mcux_handler_non_ctrl_out(dev, ep, buf, mcux_buf, mcux_len);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* return true - zlp is feed; false - no zlp */
|
||||
static bool udc_mcux_handler_zlt(const struct device *dev, uint8_t ep, struct net_buf *buf,
|
||||
uint16_t mcux_len)
|
||||
{
|
||||
const struct udc_mcux_config *config = dev->config;
|
||||
const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if;
|
||||
struct udc_mcux_data *priv = udc_get_private(dev);
|
||||
|
||||
/* The whole transfer is already done by MCUX controller driver. */
|
||||
if (mcux_len >= buf->len) {
|
||||
if (udc_ep_buf_has_zlp(buf)) {
|
||||
usb_status_t status;
|
||||
|
||||
udc_ep_buf_clear_zlp(buf);
|
||||
status = mcux_if->deviceRecv(priv->mcux_device.controllerHandle,
|
||||
ep, NULL, 0);
|
||||
if (status != kStatus_USB_Success) {
|
||||
udc_submit_event(dev, UDC_EVT_ERROR, -EIO);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int udc_mcux_handler_in(const struct device *dev, uint8_t ep,
|
||||
uint8_t *mcux_buf, uint16_t mcux_len)
|
||||
{
|
||||
int err;
|
||||
struct net_buf *buf;
|
||||
unsigned int key;
|
||||
|
||||
buf = udc_buf_peek(dev, ep);
|
||||
if (buf == NULL) {
|
||||
udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
if (udc_mcux_handler_zlt(dev, ep, buf, mcux_len)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf = udc_buf_get(dev, ep);
|
||||
|
||||
key = irq_lock();
|
||||
udc_ep_set_busy(dev, ep, false);
|
||||
irq_unlock(key);
|
||||
|
||||
if (buf == NULL) {
|
||||
udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
if (ep == USB_CONTROL_EP_IN) {
|
||||
err = udc_mcux_handler_ctrl_in(dev, buf, mcux_buf, mcux_len);
|
||||
} else {
|
||||
err = udc_mcux_handler_non_ctrl_in(dev, ep, buf, mcux_buf, mcux_len);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* NXP MCUX controller driver notify transfers/status through this interface */
|
||||
usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg)
|
||||
{
|
||||
usb_device_callback_message_struct_t *mcux_msg = msg;
|
||||
uint8_t ep;
|
||||
usb_device_notification_t mcux_notify;
|
||||
struct udc_mcux_data *priv;
|
||||
const struct device *dev;
|
||||
usb_status_t mcux_status = kStatus_USB_Success;
|
||||
int err = 0;
|
||||
|
||||
if ((NULL == msg) || (NULL == handle)) {
|
||||
return kStatus_USB_InvalidHandle;
|
||||
}
|
||||
|
||||
mcux_notify = (usb_device_notification_t)mcux_msg->code;
|
||||
priv = (struct udc_mcux_data *)(PRV_DATA_HANDLE(handle));
|
||||
dev = priv->dev;
|
||||
|
||||
switch (mcux_notify) {
|
||||
case kUSB_DeviceNotifyBusReset:
|
||||
struct udc_ep_config *cfg;
|
||||
|
||||
udc_mcux_control(dev, kUSB_DeviceControlSetDefaultStatus, NULL);
|
||||
cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT);
|
||||
if (cfg->stat.enabled) {
|
||||
udc_ep_disable_internal(dev, USB_CONTROL_EP_OUT);
|
||||
}
|
||||
cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN);
|
||||
if (cfg->stat.enabled) {
|
||||
udc_ep_disable_internal(dev, USB_CONTROL_EP_IN);
|
||||
}
|
||||
if (udc_ep_enable_internal(dev, USB_CONTROL_EP_OUT,
|
||||
USB_EP_TYPE_CONTROL,
|
||||
USB_MCUX_EP0_SIZE, 0)) {
|
||||
LOG_ERR("Failed to enable control endpoint");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (udc_ep_enable_internal(dev, USB_CONTROL_EP_IN,
|
||||
USB_EP_TYPE_CONTROL,
|
||||
USB_MCUX_EP0_SIZE, 0)) {
|
||||
LOG_ERR("Failed to enable control endpoint");
|
||||
return -EIO;
|
||||
}
|
||||
udc_submit_event(dev, UDC_EVT_RESET, 0);
|
||||
break;
|
||||
case kUSB_DeviceNotifyError:
|
||||
udc_submit_event(dev, UDC_EVT_ERROR, -EIO);
|
||||
break;
|
||||
case kUSB_DeviceNotifySuspend:
|
||||
udc_set_suspended(dev, true);
|
||||
udc_submit_event(dev, UDC_EVT_SUSPEND, 0);
|
||||
break;
|
||||
case kUSB_DeviceNotifyResume:
|
||||
udc_set_suspended(dev, false);
|
||||
udc_submit_event(dev, UDC_EVT_RESUME, 0);
|
||||
break;
|
||||
case kUSB_DeviceNotifyLPMSleep:
|
||||
break;
|
||||
case kUSB_DeviceNotifyDetach:
|
||||
udc_submit_event(dev, UDC_EVT_VBUS_REMOVED, 0);
|
||||
break;
|
||||
case kUSB_DeviceNotifyAttach:
|
||||
udc_submit_event(dev, UDC_EVT_VBUS_READY, 0);
|
||||
break;
|
||||
default:
|
||||
ep = mcux_msg->code;
|
||||
if (mcux_msg->isSetup) {
|
||||
struct usb_setup_packet *setup =
|
||||
(struct usb_setup_packet *)mcux_msg->buffer;
|
||||
|
||||
err = udc_mcux_handler_setup(dev, setup);
|
||||
} else if (USB_EP_DIR_IS_IN(ep)) {
|
||||
err = udc_mcux_handler_in(dev, ep, mcux_msg->buffer, mcux_msg->length);
|
||||
} else {
|
||||
err = udc_mcux_handler_out(dev, ep, mcux_msg->buffer, mcux_msg->length);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (unlikely(err)) {
|
||||
udc_submit_event(dev, UDC_EVT_ERROR, err);
|
||||
mcux_status = kStatus_USB_Error;
|
||||
}
|
||||
return mcux_status;
|
||||
}
|
||||
|
||||
static void udc_mcux_isr(const struct device *dev)
|
||||
{
|
||||
struct udc_mcux_data *priv = udc_get_private(dev);
|
||||
|
||||
USB_DeviceLpcIp3511IsrFunction((void *)(&priv->mcux_device));
|
||||
}
|
||||
|
||||
/* Return actual USB device speed */
|
||||
static enum udc_bus_speed udc_mcux_device_speed(const struct device *dev)
|
||||
{
|
||||
int err;
|
||||
uint8_t mcux_speed;
|
||||
|
||||
err = udc_mcux_control(dev, kUSB_DeviceControlGetSpeed, &mcux_speed);
|
||||
if (err) {
|
||||
/*
|
||||
* In the current version of all NXP USB device drivers,
|
||||
* no error is returned if the parameter is correct.
|
||||
*/
|
||||
return UDC_BUS_SPEED_FS;
|
||||
}
|
||||
|
||||
switch (mcux_speed) {
|
||||
case USB_SPEED_HIGH:
|
||||
return UDC_BUS_SPEED_HS;
|
||||
case USB_SPEED_LOW:
|
||||
__ASSERT(false, "Low speed mode not supported");
|
||||
__fallthrough;
|
||||
case USB_SPEED_FULL:
|
||||
__fallthrough;
|
||||
default:
|
||||
return UDC_BUS_SPEED_FS;
|
||||
}
|
||||
}
|
||||
|
||||
static int udc_mcux_ep_enqueue(const struct device *dev,
|
||||
struct udc_ep_config *const cfg,
|
||||
struct net_buf *const buf)
|
||||
{
|
||||
udc_buf_put(cfg, buf);
|
||||
if (cfg->stat.halted) {
|
||||
LOG_DBG("ep 0x%02x halted", cfg->addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return udc_mcux_ep_try_feed(dev, cfg);
|
||||
}
|
||||
|
||||
static int udc_mcux_ep_dequeue(const struct device *dev,
|
||||
struct udc_ep_config *const cfg)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
unsigned int key;
|
||||
|
||||
cfg->stat.halted = false;
|
||||
buf = udc_buf_get_all(dev, cfg->addr);
|
||||
if (buf) {
|
||||
udc_submit_ep_event(dev, buf, -ECONNABORTED);
|
||||
}
|
||||
|
||||
key = irq_lock();
|
||||
udc_ep_set_busy(dev, cfg->addr, false);
|
||||
irq_unlock(key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udc_mcux_ep_set_halt(const struct device *dev,
|
||||
struct udc_ep_config *const cfg)
|
||||
{
|
||||
return udc_mcux_control(dev, kUSB_DeviceControlEndpointStall, &cfg->addr);
|
||||
}
|
||||
|
||||
static int udc_mcux_ep_clear_halt(const struct device *dev,
|
||||
struct udc_ep_config *const cfg)
|
||||
{
|
||||
(void)udc_mcux_control(dev, kUSB_DeviceControlEndpointUnstall, &cfg->addr);
|
||||
/* transfer is enqueued after stalled */
|
||||
return udc_mcux_ep_try_feed(dev, cfg);
|
||||
}
|
||||
|
||||
static int udc_mcux_ep_enable(const struct device *dev,
|
||||
struct udc_ep_config *const cfg)
|
||||
{
|
||||
usb_device_endpoint_init_struct_t ep_init;
|
||||
|
||||
LOG_DBG("Enable ep 0x%02x", cfg->addr);
|
||||
|
||||
ep_init.zlt = 0U;
|
||||
ep_init.interval = cfg->interval;
|
||||
ep_init.endpointAddress = cfg->addr;
|
||||
ep_init.maxPacketSize = cfg->mps;
|
||||
|
||||
switch (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) {
|
||||
case USB_EP_TYPE_CONTROL:
|
||||
ep_init.transferType = USB_ENDPOINT_CONTROL;
|
||||
break;
|
||||
case USB_EP_TYPE_BULK:
|
||||
ep_init.transferType = USB_ENDPOINT_BULK;
|
||||
break;
|
||||
case USB_EP_TYPE_INTERRUPT:
|
||||
ep_init.transferType = USB_ENDPOINT_INTERRUPT;
|
||||
break;
|
||||
case USB_EP_TYPE_ISO:
|
||||
ep_init.transferType = USB_ENDPOINT_ISOCHRONOUS;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return udc_mcux_control(dev, kUSB_DeviceControlEndpointInit, &ep_init);
|
||||
}
|
||||
|
||||
static int udc_mcux_ep_disable(const struct device *dev,
|
||||
struct udc_ep_config *const cfg)
|
||||
{
|
||||
LOG_DBG("Disable ep 0x%02x", cfg->addr);
|
||||
|
||||
return udc_mcux_control(dev, kUSB_DeviceControlEndpointDeinit, &cfg->addr);
|
||||
}
|
||||
|
||||
static int udc_mcux_host_wakeup(const struct device *dev)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int udc_mcux_set_address(const struct device *dev, const uint8_t addr)
|
||||
{
|
||||
uint8_t temp_addr = addr;
|
||||
|
||||
return udc_mcux_control(dev, kUSB_DeviceControlSetDeviceAddress, &temp_addr);
|
||||
}
|
||||
|
||||
static int udc_mcux_enable(const struct device *dev)
|
||||
{
|
||||
return udc_mcux_control(dev, kUSB_DeviceControlRun, NULL);
|
||||
}
|
||||
|
||||
static int udc_mcux_disable(const struct device *dev)
|
||||
{
|
||||
return udc_mcux_control(dev, kUSB_DeviceControlStop, NULL);
|
||||
}
|
||||
|
||||
static int udc_mcux_init(const struct device *dev)
|
||||
{
|
||||
const struct udc_mcux_config *config = dev->config;
|
||||
const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if;
|
||||
struct udc_mcux_data *priv = udc_get_private(dev);
|
||||
usb_status_t status;
|
||||
|
||||
if (priv->controller_id == 0xFFu) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED
|
||||
if (config->phy_config != NULL) {
|
||||
USB_EhciPhyInit(priv->controller_id, 0u,
|
||||
(usb_phy_config_struct_t *)&config->phy_config);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Init MCUX USB device driver. */
|
||||
status = mcux_if->deviceInit(priv->controller_id,
|
||||
&priv->mcux_device, &(priv->mcux_device.controllerHandle));
|
||||
if (status != kStatus_USB_Success) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* enable USB interrupt */
|
||||
config->irq_enable_func(dev);
|
||||
|
||||
LOG_DBG("Initialized USB controller %x", (uint32_t)config->base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udc_mcux_shutdown(const struct device *dev)
|
||||
{
|
||||
const struct udc_mcux_config *config = dev->config;
|
||||
const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if;
|
||||
struct udc_mcux_data *priv = udc_get_private(dev);
|
||||
usb_status_t status;
|
||||
|
||||
/* Disable interrupt */
|
||||
config->irq_disable_func(dev);
|
||||
|
||||
/* De-init MCUX USB device driver. */
|
||||
status = mcux_if->deviceDeinit(priv->mcux_device.controllerHandle);
|
||||
if (status != kStatus_USB_Success) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udc_mcux_lock(const struct device *dev)
|
||||
{
|
||||
return udc_lock_internal(dev, K_FOREVER);
|
||||
}
|
||||
|
||||
static int udc_mcux_unlock(const struct device *dev)
|
||||
{
|
||||
return udc_unlock_internal(dev);
|
||||
}
|
||||
|
||||
static inline void udc_mcux_get_hal_driver_id(struct udc_mcux_data *priv,
|
||||
const struct udc_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.
|
||||
*/
|
||||
#ifdef USB_BASE_ADDRS
|
||||
uintptr_t ip3511_fs_base[] = USB_BASE_ADDRS;
|
||||
#endif
|
||||
#ifdef USBHSD_BASE_ADDRS
|
||||
uintptr_t ip3511_hs_base[] = USBHSD_BASE_ADDRS;
|
||||
#endif
|
||||
|
||||
/* get the right controller id */
|
||||
priv->controller_id = 0xFFu; /* invalid value */
|
||||
#ifdef USB_BASE_ADDRS
|
||||
for (uint8_t i = 0; i < ARRAY_SIZE(ip3511_fs_base); i++) {
|
||||
if (ip3511_fs_base[i] == config->base) {
|
||||
priv->controller_id = kUSB_ControllerLpcIp3511Fs0 + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USBHSD_BASE_ADDRS
|
||||
if (priv->controller_id == 0xFF) {
|
||||
for (uint8_t i = 0; i < ARRAY_SIZE(ip3511_hs_base); i++) {
|
||||
if (ip3511_hs_base[i] == config->base) {
|
||||
priv->controller_id = kUSB_ControllerLpcIp3511Hs0 + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int udc_mcux_driver_preinit(const struct device *dev)
|
||||
{
|
||||
const struct udc_mcux_config *config = dev->config;
|
||||
struct udc_data *data = dev->data;
|
||||
struct udc_mcux_data *priv = data->priv;
|
||||
int err;
|
||||
|
||||
udc_mcux_get_hal_driver_id(priv, config);
|
||||
if (priv->controller_id == 0xFFu) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
k_mutex_init(&data->mutex);
|
||||
|
||||
for (int i = 0; i < config->num_of_eps; i++) {
|
||||
config->ep_cfg_out[i].caps.out = 1;
|
||||
if (i == 0) {
|
||||
config->ep_cfg_out[i].caps.control = 1;
|
||||
config->ep_cfg_out[i].caps.mps = 64;
|
||||
} else {
|
||||
config->ep_cfg_out[i].caps.bulk = 1;
|
||||
config->ep_cfg_out[i].caps.interrupt = 1;
|
||||
config->ep_cfg_out[i].caps.iso = 1;
|
||||
config->ep_cfg_out[i].caps.mps = 1024;
|
||||
}
|
||||
|
||||
config->ep_cfg_out[i].addr = USB_EP_DIR_OUT | i;
|
||||
err = udc_register_ep(dev, &config->ep_cfg_out[i]);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to register endpoint");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < config->num_of_eps; i++) {
|
||||
config->ep_cfg_in[i].caps.in = 1;
|
||||
if (i == 0) {
|
||||
config->ep_cfg_in[i].caps.control = 1;
|
||||
config->ep_cfg_in[i].caps.mps = 64;
|
||||
} else {
|
||||
config->ep_cfg_in[i].caps.bulk = 1;
|
||||
config->ep_cfg_in[i].caps.interrupt = 1;
|
||||
config->ep_cfg_in[i].caps.iso = 1;
|
||||
config->ep_cfg_in[i].caps.mps = 1024;
|
||||
}
|
||||
|
||||
config->ep_cfg_in[i].addr = USB_EP_DIR_IN | i;
|
||||
err = udc_register_ep(dev, &config->ep_cfg_in[i]);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to register endpoint");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* Requires udc_mcux_host_wakeup() implementation */
|
||||
data->caps.rwup = false;
|
||||
data->caps.mps0 = USB_MCUX_MPS0;
|
||||
if ((priv->controller_id == kUSB_ControllerLpcIp3511Hs0) ||
|
||||
(priv->controller_id == kUSB_ControllerLpcIp3511Hs1)) {
|
||||
data->caps.hs = true;
|
||||
}
|
||||
priv->dev = dev;
|
||||
|
||||
pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udc_api udc_mcux_api = {
|
||||
.device_speed = udc_mcux_device_speed,
|
||||
.ep_enqueue = udc_mcux_ep_enqueue,
|
||||
.ep_dequeue = udc_mcux_ep_dequeue,
|
||||
.ep_set_halt = udc_mcux_ep_set_halt,
|
||||
.ep_clear_halt = udc_mcux_ep_clear_halt,
|
||||
.ep_try_config = NULL,
|
||||
.ep_enable = udc_mcux_ep_enable,
|
||||
.ep_disable = udc_mcux_ep_disable,
|
||||
.host_wakeup = udc_mcux_host_wakeup,
|
||||
.set_address = udc_mcux_set_address,
|
||||
.enable = udc_mcux_enable,
|
||||
.disable = udc_mcux_disable,
|
||||
.init = udc_mcux_init,
|
||||
.shutdown = udc_mcux_shutdown,
|
||||
.lock = udc_mcux_lock,
|
||||
.unlock = udc_mcux_unlock,
|
||||
};
|
||||
|
||||
/* IP3511 device driver interface */
|
||||
static const usb_device_controller_interface_struct_t udc_mcux_if = {
|
||||
USB_DeviceLpc3511IpInit, USB_DeviceLpc3511IpDeinit, USB_DeviceLpc3511IpSend,
|
||||
USB_DeviceLpc3511IpRecv, USB_DeviceLpc3511IpCancel, USB_DeviceLpc3511IpControl
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED
|
||||
#define UDC_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 UDC_MCUX_PHY_DEFINE_OR(n) \
|
||||
COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), \
|
||||
(UDC_MCUX_PHY_DEFINE(n)), ())
|
||||
|
||||
#define UDC_MCUX_PHY_CFG_PTR_OR_NULL(n) \
|
||||
.phy_config = COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), \
|
||||
(&phy_config_##n), (NULL))
|
||||
#else
|
||||
#define UDC_MCUX_PHY_DEFINE_OR(n)
|
||||
#define UDC_MCUX_PHY_CFG_PTR_OR_NULL(n)
|
||||
#endif
|
||||
|
||||
#define USB_MCUX_IP3511_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); \
|
||||
\
|
||||
irq_enable(DT_INST_IRQN(n)); \
|
||||
} \
|
||||
\
|
||||
static void udc_irq_disable_func##n(const struct device *dev) \
|
||||
{ \
|
||||
irq_disable(DT_INST_IRQN(n)); \
|
||||
} \
|
||||
\
|
||||
static struct udc_ep_config \
|
||||
ep_cfg_out##n[DT_INST_PROP(n, num_bidir_endpoints)]; \
|
||||
static struct udc_ep_config \
|
||||
ep_cfg_in##n[DT_INST_PROP(n, num_bidir_endpoints)]; \
|
||||
\
|
||||
PINCTRL_DT_INST_DEFINE(n); \
|
||||
\
|
||||
static struct udc_mcux_config priv_config_##n = { \
|
||||
.base = DT_INST_REG_ADDR(n), \
|
||||
.irq_enable_func = udc_irq_enable_func##n, \
|
||||
.irq_disable_func = udc_irq_disable_func##n, \
|
||||
.num_of_eps = DT_INST_PROP(n, num_bidir_endpoints), \
|
||||
.ep_cfg_in = ep_cfg_in##n, \
|
||||
.ep_cfg_out = ep_cfg_out##n, \
|
||||
.mcux_if = &udc_mcux_if, \
|
||||
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
|
||||
UDC_MCUX_PHY_CFG_PTR_OR_NULL(n), \
|
||||
}; \
|
||||
\
|
||||
static struct udc_mcux_data priv_data_##n = { \
|
||||
}; \
|
||||
\
|
||||
static struct udc_data udc_data_##n = { \
|
||||
.mutex = Z_MUTEX_INITIALIZER(udc_data_##n.mutex), \
|
||||
.priv = &priv_data_##n, \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, udc_mcux_driver_preinit, NULL, \
|
||||
&udc_data_##n, &priv_config_##n, \
|
||||
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
|
||||
&udc_mcux_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(USB_MCUX_IP3511_DEVICE_DEFINE)
|
|
@ -1,5 +1,6 @@
|
|||
#
|
||||
# Copyright (c) 2021 Linaro, Limited
|
||||
# Copyright 2024 NXP
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
@ -7,6 +8,7 @@
|
|||
if(CONFIG_HAS_MCUX OR CONFIG_HAS_IMX_HAL OR CONFIG_HAS_NXP_S32_HAL)
|
||||
add_subdirectory(${ZEPHYR_CURRENT_MODULE_DIR} hal_nxp)
|
||||
add_subdirectory_ifdef(CONFIG_USB_DEVICE_DRIVER usb)
|
||||
add_subdirectory_ifdef(CONFIG_UDC_DRIVER usb)
|
||||
|
||||
zephyr_sources_ifdef(CONFIG_PWM_MCUX_CTIMER ${ZEPHYR_CURRENT_MODULE_DIR}/mcux/mcux-sdk/drivers/ctimer/fsl_ctimer.c)
|
||||
zephyr_include_directories_ifdef(CONFIG_PWM_MCUX_CTIMER
|
||||
|
@ -22,6 +24,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)
|
||||
endif()
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_BT_H4_NXP_CTLR bt_controller)
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#
|
||||
# Copyright (c) 2021, NXP
|
||||
# Copyright (c) 2021,2024 NXP
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
zephyr_include_directories(.)
|
||||
if(CONFIG_DT_HAS_NXP_USBPHY_ENABLED)
|
||||
zephyr_include_directories(${ZEPHYR_HAL_NXP_MODULE_DIR}/mcux/middleware/mcux-sdk-middleware-usb/phy)
|
||||
endif()
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
/******************************************************************************
|
||||
* Definitions
|
||||
*****************************************************************************/
|
||||
#ifdef CONFIG_USB_DEVICE_DRIVER
|
||||
/* EHCI instance count */
|
||||
#ifdef CONFIG_USB_DC_NXP_EHCI
|
||||
#define USB_DEVICE_CONFIG_EHCI (1U)
|
||||
|
@ -51,5 +52,57 @@ BUILD_ASSERT(NUM_INSTS <= 1, "Only one USB device supported");
|
|||
|
||||
/* Number of endpoints supported */
|
||||
#define USB_DEVICE_CONFIG_ENDPOINTS (DT_INST_PROP(0, num_bidir_endpoints))
|
||||
#else
|
||||
|
||||
#ifdef CONFIG_UDC_NXP_EHCI
|
||||
#define USB_DEVICE_CONFIG_EHCI (DT_NUM_INST_STATUS_OKAY(nxp_ehci))
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_UDC_NXP_IP3511
|
||||
|
||||
#if defined(USBHSD_BASE_ADDRS) && defined(USB_BASE_ADDRS)
|
||||
#define USB_DEVICE_CONFIG_LPCIP3511HS (1U)
|
||||
#define USB_DEVICE_CONFIG_LPCIP3511FS (1U)
|
||||
|
||||
#else
|
||||
|
||||
#ifdef USBHSD_BASE_ADDRS
|
||||
#define USB_DEVICE_CONFIG_LPCIP3511HS (DT_NUM_INST_STATUS_OKAY(nxp_lpcip3511))
|
||||
#else
|
||||
#define USB_DEVICE_CONFIG_LPCIP3511HS (0U)
|
||||
#endif
|
||||
|
||||
#ifdef USB_BASE_ADDRS
|
||||
#define USB_DEVICE_CONFIG_LPCIP3511FS (DT_NUM_INST_STATUS_OKAY(nxp_lpcip3511))
|
||||
#else
|
||||
#define USB_DEVICE_CONFIG_LPCIP3511FS (0U)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* calculte the num of endponts.
|
||||
* mcux ip3511 driver doesn't use USB_DEVICE_CONFIG_ENDPOINTS,
|
||||
* so use ehci endpoint number if ehci is enabled.
|
||||
*/
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(nxp_ehci)
|
||||
#undef DT_DRV_COMPAT
|
||||
#define DT_DRV_COMPAT nxp_ehci
|
||||
#elif DT_HAS_COMPAT_STATUS_OKAY(nxp_lpcip3511)
|
||||
#undef DT_DRV_COMPAT
|
||||
#define DT_DRV_COMPAT nxp_lpcip3511
|
||||
#endif
|
||||
|
||||
/* Number of endpoints supported */
|
||||
#define USB_DEVICE_CONFIG_ENDPOINTS (DT_INST_PROP(0, num_bidir_endpoints))
|
||||
|
||||
#define USB_DEVICE_CONFIG_SELF_POWER (1U)
|
||||
|
||||
#if ((defined(USB_DEVICE_CONFIG_EHCI)) && (USB_DEVICE_CONFIG_EHCI > 0U))
|
||||
/*! @brief How many the DTD are supported. */
|
||||
#define USB_DEVICE_CONFIG_EHCI_MAX_DTD (16U)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __USB_DEVICE_CONFIG_H__ */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue