blob: 815631dbd6dc9c625ecb52297bed31c5b6c8a123 [file] [log] [blame]
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file udc_nrf.c
* @brief Nordic USB device controller (UDC) driver
*
* The driver implements the interface between the nRF USBD peripheral
* driver from nrfx package and UDC API.
*/
#include <string.h>
#include <stdio.h>
#include <soc.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/usb/udc.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/nrf_clock_control.h>
#include <nrfx_usbd.h>
#include <nrfx_power.h>
#include "udc_common.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(udc_nrf, CONFIG_UDC_DRIVER_LOG_LEVEL);
enum udc_nrf_event_type {
/* An event generated by the HAL driver */
UDC_NRF_EVT_HAL,
/* Shim driver event to trigger next transfer */
UDC_NRF_EVT_XFER,
/* Let controller perform status stage */
UDC_NRF_EVT_STATUS_IN,
};
struct udc_nrf_evt {
enum udc_nrf_event_type type;
union {
nrfx_usbd_evt_t hal_evt;
uint8_t ep;
};
};
K_MSGQ_DEFINE(drv_msgq, sizeof(struct udc_nrf_evt),
CONFIG_UDC_NRF_MAX_QMESSAGES, sizeof(uint32_t));
static K_KERNEL_STACK_DEFINE(drv_stack, CONFIG_UDC_NRF_THREAD_STACK_SIZE);
static struct k_thread drv_stack_data;
/* USB device controller access from devicetree */
#define DT_DRV_COMPAT nordic_nrf_usbd
#define CFG_EPIN_CNT DT_INST_PROP(0, num_in_endpoints)
#define CFG_EPOUT_CNT DT_INST_PROP(0, num_out_endpoints)
#define CFG_EP_ISOIN_CNT DT_INST_PROP(0, num_isoin_endpoints)
#define CFG_EP_ISOOUT_CNT DT_INST_PROP(0, num_isoout_endpoints)
static struct udc_ep_config ep_cfg_out[CFG_EPOUT_CNT + CFG_EP_ISOOUT_CNT + 1];
static struct udc_ep_config ep_cfg_in[CFG_EPIN_CNT + CFG_EP_ISOIN_CNT + 1];
static bool udc_nrf_setup_rcvd;
const static struct device *udc_nrf_dev;
struct udc_nrf_config {
clock_control_subsys_t clock;
nrfx_power_config_t pwr;
nrfx_power_usbevt_config_t evt;
};
static struct onoff_manager *hfxo_mgr;
static struct onoff_client hfxo_cli;
static void udc_nrf_clear_control_out(const struct device *dev)
{
if (nrfx_usbd_last_setup_dir_get() == USB_CONTROL_EP_OUT &&
udc_nrf_setup_rcvd) {
/* Allow data chunk on EP0 OUT */
nrfx_usbd_setup_data_clear();
udc_nrf_setup_rcvd = false;
LOG_INF("Allow data OUT");
}
}
static void udc_event_xfer_in_next(const struct device *dev, const uint8_t ep)
{
struct net_buf *buf;
if (nrfx_usbd_ep_is_busy(ep)) {
return;
}
buf = udc_buf_peek(dev, ep, false);
if (buf != NULL) {
nrfx_usbd_transfer_t xfer = {
.p_data = {.tx = buf->data},
.size = buf->len,
.flags = udc_ep_buf_has_zlp(buf) ?
NRFX_USBD_TRANSFER_ZLP_FLAG : 0,
};
nrfx_err_t err;
err = nrfx_usbd_ep_transfer(ep, &xfer);
if (err != NRFX_SUCCESS) {
LOG_ERR("ep 0x%02x nrfx error: %x", ep, err);
/* REVISE: remove from endpoint queue? ASSERT? */
udc_submit_event(dev, UDC_EVT_EP_REQUEST,
-ECONNREFUSED, buf);
}
}
}
static void udc_event_xfer_ctrl_in(const struct device *dev,
struct net_buf *const buf)
{
if (udc_ctrl_stage_is_status_in(dev) ||
udc_ctrl_stage_is_no_data(dev)) {
/* Status stage finished, notify upper layer */
udc_ctrl_submit_status(dev, buf);
}
if (udc_ctrl_stage_is_data_in(dev)) {
/*
* s-in-[status] finished, release buffer.
* Since the controller supports auto-status we cannot use
* if (udc_ctrl_stage_is_status_out()) after state update.
*/
net_buf_unref(buf);
}
/* Update to next stage of control transfer */
udc_ctrl_update_stage(dev, buf);
nrfx_usbd_setup_clear();
}
static void udc_event_fake_status_in(const struct device *dev)
{
struct net_buf *buf;
buf = udc_buf_get(dev, USB_CONTROL_EP_IN, true);
if (unlikely(buf == NULL)) {
LOG_DBG("ep 0x%02x queue is empty", USB_CONTROL_EP_IN);
return;
}
LOG_DBG("Fake status IN %p", buf);
udc_event_xfer_ctrl_in(dev, buf);
}
static void udc_event_xfer_in(const struct device *dev,
nrfx_usbd_evt_t const *const event)
{
uint8_t ep = event->data.eptransfer.ep;
struct net_buf *buf;
switch (event->data.eptransfer.status) {
case NRFX_USBD_EP_OK:
buf = udc_buf_get(dev, ep, true);
if (buf == NULL) {
LOG_ERR("ep 0x%02x queue is empty", ep);
__ASSERT_NO_MSG(false);
return;
}
if (ep == USB_CONTROL_EP_IN) {
return udc_event_xfer_ctrl_in(dev, buf);
}
udc_submit_event(dev, UDC_EVT_EP_REQUEST, 0, buf);
break;
case NRFX_USBD_EP_ABORTED:
LOG_WRN("aborted IN ep 0x%02x", ep);
buf = udc_buf_get_all(dev, ep);
if (buf == NULL) {
LOG_DBG("ep 0x%02x queue is empty", ep);
return;
}
udc_submit_event(dev, UDC_EVT_EP_REQUEST,
-ECONNABORTED, buf);
break;
default:
LOG_ERR("Unexpected event (nrfx_usbd): %d, ep 0x%02x",
event->data.eptransfer.status, ep);
udc_submit_event(dev, UDC_EVT_EP_REQUEST, -EIO, NULL);
break;
}
}
static void udc_event_xfer_ctrl_out(const struct device *dev,
struct net_buf *const buf)
{
/*
* In case s-in-status, controller supports auto-status therefore we
* do not have to call udc_ctrl_stage_is_status_out().
*/
/* Update to next stage of control transfer */
udc_ctrl_update_stage(dev, buf);
if (udc_ctrl_stage_is_status_in(dev)) {
udc_ctrl_submit_s_out_status(dev, buf);
}
}
static void udc_event_xfer_out_next(const struct device *dev, const uint8_t ep)
{
struct net_buf *buf;
buf = udc_buf_peek(dev, ep, true);
if (buf != NULL) {
nrfx_usbd_transfer_t xfer = {
.p_data = {.rx = buf->data},
.size = buf->size,
.flags = 0,
};
nrfx_err_t err;
err = nrfx_usbd_ep_transfer(ep, &xfer);
if (err != NRFX_SUCCESS) {
LOG_ERR("ep 0x%02x nrfx error: %x", ep, err);
/* REVISE: remove from endpoint queue? ASSERT? */
udc_submit_event(dev, UDC_EVT_EP_REQUEST,
-ECONNREFUSED, buf);
}
} else {
LOG_DBG("ep 0x%02x waiting, queue is empty", ep);
}
}
static void udc_event_xfer_out(const struct device *dev,
nrfx_usbd_evt_t const *const event)
{
uint8_t ep = event->data.eptransfer.ep;
nrfx_usbd_ep_status_t err_code;
struct net_buf *buf;
size_t len;
switch (event->data.eptransfer.status) {
case NRFX_USBD_EP_WAITING:
udc_event_xfer_out_next(dev, ep);
break;
case NRFX_USBD_EP_OK:
err_code = nrfx_usbd_ep_status_get(ep, &len);
if (err_code != NRFX_USBD_EP_OK) {
LOG_ERR("OUT transfer failed %d", err_code);
}
buf = udc_buf_get(dev, ep, true);
if (buf == NULL) {
LOG_ERR("ep 0x%02x ok, queue is empty", ep);
return;
}
net_buf_add(buf, len);
if (ep == USB_CONTROL_EP_OUT) {
udc_event_xfer_ctrl_out(dev, buf);
} else {
udc_submit_event(dev, UDC_EVT_EP_REQUEST, 0, buf);
}
break;
default:
LOG_ERR("Unexpected event (nrfx_usbd): %d, ep 0x%02x",
event->data.eptransfer.status, ep);
udc_submit_event(dev, UDC_EVT_EP_REQUEST, -EIO, NULL);
break;
}
}
static int usbd_ctrl_feed_dout(const struct device *dev,
const size_t length)
{
struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT);
struct net_buf *buf;
buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length);
if (buf == NULL) {
return -ENOMEM;
}
net_buf_put(&cfg->fifo, buf);
udc_nrf_clear_control_out(dev);
return 0;
}
static int udc_event_xfer_setup(const struct device *dev)
{
struct net_buf *buf;
int err;
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 -ENOMEM;
}
udc_ep_buf_set_setup(buf);
nrfx_usbd_setup_get((nrfx_usbd_setup_t *)buf->data);
net_buf_add(buf, sizeof(nrfx_usbd_setup_t));
udc_nrf_setup_rcvd = true;
/* Update to next stage of control transfer */
udc_ctrl_update_stage(dev, buf);
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 = usbd_ctrl_feed_dout(dev, udc_data_stage_length(buf));
if (err == -ENOMEM) {
err = udc_submit_event(dev, UDC_EVT_EP_REQUEST, err, buf);
}
} 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 void udc_nrf_thread(const struct device *dev)
{
while (true) {
bool start_xfer = false;
struct udc_nrf_evt evt;
uint8_t ep;
k_msgq_get(&drv_msgq, &evt, K_FOREVER);
switch (evt.type) {
case UDC_NRF_EVT_HAL:
ep = evt.hal_evt.data.eptransfer.ep;
switch (evt.hal_evt.type) {
case NRFX_USBD_EVT_EPTRANSFER:
if (USB_EP_DIR_IS_IN(ep)) {
udc_event_xfer_in(dev, &evt.hal_evt);
start_xfer = true;
} else {
udc_event_xfer_out(dev, &evt.hal_evt);
}
break;
case NRFX_USBD_EVT_SETUP:
udc_event_xfer_setup(dev);
break;
default:
break;
}
break;
case UDC_NRF_EVT_XFER:
start_xfer = true;
ep = evt.ep;
break;
case UDC_NRF_EVT_STATUS_IN:
udc_event_fake_status_in(dev);
break;
}
if (start_xfer) {
struct udc_ep_config *cfg = udc_get_ep_cfg(dev, ep);
if (USB_EP_DIR_IS_IN(ep)) {
udc_event_xfer_in_next(dev, ep);
} else if (cfg->stat.pending) {
udc_event_xfer_out_next(dev, ep);
}
}
}
}
static void udc_sof_check_iso_out(const struct device *dev)
{
const uint8_t iso_out_addr = 0x08;
struct udc_nrf_evt evt = {
.type = UDC_NRF_EVT_XFER,
.ep = iso_out_addr,
};
struct udc_ep_config *ep_cfg;
ep_cfg = udc_get_ep_cfg(dev, iso_out_addr);
if (ep_cfg == NULL) {
return;
}
if (ep_cfg->stat.enabled && !k_fifo_is_empty(&ep_cfg->fifo)) {
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
}
}
static void usbd_event_handler(nrfx_usbd_evt_t const *const hal_evt)
{
switch (hal_evt->type) {
case NRFX_USBD_EVT_SUSPEND:
LOG_INF("SUSPEND state detected");
nrfx_usbd_suspend();
udc_set_suspended(udc_nrf_dev, true);
udc_submit_event(udc_nrf_dev, UDC_EVT_SUSPEND, 0, NULL);
break;
case NRFX_USBD_EVT_RESUME:
LOG_INF("RESUMING from suspend");
udc_set_suspended(udc_nrf_dev, false);
udc_submit_event(udc_nrf_dev, UDC_EVT_RESUME, 0, NULL);
break;
case NRFX_USBD_EVT_WUREQ:
LOG_INF("Remote wakeup initiated");
break;
case NRFX_USBD_EVT_RESET:
LOG_INF("Reset");
udc_submit_event(udc_nrf_dev, UDC_EVT_RESET, 0, NULL);
break;
case NRFX_USBD_EVT_SOF:
udc_submit_event(udc_nrf_dev, UDC_EVT_SOF, 0, NULL);
udc_sof_check_iso_out(udc_nrf_dev);
break;
case NRFX_USBD_EVT_EPTRANSFER:
case NRFX_USBD_EVT_SETUP:
struct udc_nrf_evt evt = {
.type = UDC_NRF_EVT_HAL,
.hal_evt = *hal_evt,
};
/* Forward these two to the thread since mutually exclusive
* access to the controller is necessary.
*/
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
break;
default:
break;
}
}
static void udc_nrf_power_handler(nrfx_power_usb_evt_t pwr_evt)
{
switch (pwr_evt) {
case NRFX_POWER_USB_EVT_DETECTED:
LOG_DBG("POWER event detected");
break;
case NRFX_POWER_USB_EVT_READY:
LOG_INF("POWER event ready");
udc_submit_event(udc_nrf_dev, UDC_EVT_VBUS_READY, 0, NULL);
nrfx_usbd_start(true);
break;
case NRFX_POWER_USB_EVT_REMOVED:
LOG_INF("POWER event removed");
udc_submit_event(udc_nrf_dev, UDC_EVT_VBUS_REMOVED, 0, NULL);
break;
default:
LOG_ERR("Unknown power event %d", pwr_evt);
}
}
static void udc_nrf_fake_status_in(const struct device *dev)
{
struct udc_nrf_evt evt = {
.type = UDC_NRF_EVT_STATUS_IN,
.ep = USB_CONTROL_EP_IN,
};
if (nrfx_usbd_last_setup_dir_get() == USB_CONTROL_EP_OUT) {
/* Let controller perform status IN stage */
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
}
}
static int udc_nrf_ep_enqueue(const struct device *dev,
struct udc_ep_config *cfg,
struct net_buf *buf)
{
struct udc_nrf_evt evt = {
.type = UDC_NRF_EVT_XFER,
.ep = cfg->addr,
};
udc_buf_put(cfg, buf);
if (cfg->addr == USB_CONTROL_EP_IN && buf->len == 0) {
udc_nrf_fake_status_in(dev);
return 0;
}
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
return 0;
}
static int udc_nrf_ep_dequeue(const struct device *dev,
struct udc_ep_config *cfg)
{
nrfx_usbd_ep_abort(cfg->addr);
if (USB_EP_DIR_IS_OUT(cfg->addr)) {
struct net_buf *buf;
/* HAL driver does not generate event for an OUT endpoint */
buf = udc_buf_get_all(dev, cfg->addr);
if (buf) {
udc_submit_event(dev, UDC_EVT_EP_REQUEST,
-ECONNABORTED, buf);
} else {
LOG_INF("ep 0x%02x queue is empty", cfg->addr);
}
}
return 0;
}
static int udc_nrf_ep_enable(const struct device *dev,
struct udc_ep_config *cfg)
{
uint16_t mps;
__ASSERT_NO_MSG(cfg);
mps = (cfg->mps == 0) ? cfg->caps.mps : cfg->mps;
nrfx_usbd_ep_max_packet_size_set(cfg->addr, mps);
nrfx_usbd_ep_enable(cfg->addr);
if (!NRF_USBD_EPISO_CHECK(cfg->addr)) {
/* ISO transactions for full-speed device do not support
* toggle sequencing and should only send DATA0 PID.
*/
nrfx_usbd_ep_dtoggle_clear(cfg->addr);
nrfx_usbd_ep_stall_clear(cfg->addr);
}
LOG_DBG("Enable ep 0x%02x", cfg->addr);
return 0;
}
static int udc_nrf_ep_disable(const struct device *dev,
struct udc_ep_config *cfg)
{
__ASSERT_NO_MSG(cfg);
nrfx_usbd_ep_disable(cfg->addr);
LOG_DBG("Disable ep 0x%02x", cfg->addr);
return 0;
}
static int udc_nrf_ep_set_halt(const struct device *dev,
struct udc_ep_config *cfg)
{
LOG_DBG("Halt ep 0x%02x", cfg->addr);
if (cfg->addr == USB_CONTROL_EP_OUT ||
cfg->addr == USB_CONTROL_EP_IN) {
nrfx_usbd_setup_stall();
} else {
nrfx_usbd_ep_stall(cfg->addr);
}
return 0;
}
static int udc_nrf_ep_clear_halt(const struct device *dev,
struct udc_ep_config *cfg)
{
LOG_DBG("Clear halt ep 0x%02x", cfg->addr);
nrfx_usbd_ep_dtoggle_clear(cfg->addr);
nrfx_usbd_ep_stall_clear(cfg->addr);
return 0;
}
static int udc_nrf_ep_flush(const struct device *dev,
struct udc_ep_config *cfg)
{
LOG_INF("Flush ep 0x%02x", cfg->addr);
/*
* nrfx_usbd_ep_disable() in udc_nrf_ep_disable()
* flushes endpoint buffer as well.
*/
nrfx_usbd_ep_abort(cfg->addr);
return 0;
}
static int udc_nrf_set_address(const struct device *dev, const uint8_t addr)
{
/**
* Nothing to do here. The USBD HW already takes care of initiating
* STATUS stage. Just double check the address for sanity.
*/
if (addr != (uint8_t)NRF_USBD->USBADDR) {
LOG_WRN("USB Address incorrect 0x%02x", addr);
}
return 0;
}
static int udc_nrf_host_wakeup(const struct device *dev)
{
bool res = nrfx_usbd_wakeup_req();
LOG_DBG("Host wakeup request");
if (!res) {
return -EAGAIN;
}
return 0;
}
static int udc_nrf_enable(const struct device *dev)
{
int ret;
nrfx_usbd_enable();
sys_notify_init_spinwait(&hfxo_cli.notify);
ret = onoff_request(hfxo_mgr, &hfxo_cli);
if (ret < 0) {
LOG_ERR("Failed to start HFXO %d", ret);
return ret;
}
return 0;
}
static int udc_nrf_disable(const struct device *dev)
{
int ret;
nrfx_usbd_disable();
ret = onoff_cancel_or_release(hfxo_mgr, &hfxo_cli);
if (ret < 0) {
LOG_ERR("Failed to stop HFXO %d", ret);
return ret;
}
return 0;
}
static int udc_nrf_init(const struct device *dev)
{
const struct udc_nrf_config *cfg = dev->config;
int ret;
hfxo_mgr = z_nrf_clock_control_get_onoff(cfg->clock);
#ifdef CONFIG_HAS_HW_NRF_USBREG
/* Use CLOCK/POWER priority for compatibility with other series where
* USB events are handled by CLOCK interrupt handler.
*/
IRQ_CONNECT(USBREGULATOR_IRQn,
DT_IRQ(DT_INST(0, nordic_nrf_clock), priority),
nrfx_isr, nrfx_usbreg_irq_handler, 0);
irq_enable(USBREGULATOR_IRQn);
#endif
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
nrfx_isr, nrfx_usbd_irq_handler, 0);
(void)nrfx_power_init(&cfg->pwr);
nrfx_power_usbevt_init(&cfg->evt);
ret = nrfx_usbd_init(usbd_event_handler);
if (ret != NRFX_SUCCESS) {
LOG_ERR("nRF USBD driver initialization failed");
return -EIO;
}
nrfx_power_usbevt_enable();
if (udc_ep_enable_internal(dev, USB_CONTROL_EP_OUT,
USB_EP_TYPE_CONTROL, 64, 0)) {
LOG_ERR("Failed to enable control endpoint");
return -EIO;
}
if (udc_ep_enable_internal(dev, USB_CONTROL_EP_IN,
USB_EP_TYPE_CONTROL, 64, 0)) {
LOG_ERR("Failed to enable control endpoint");
return -EIO;
}
LOG_INF("Initialized");
return 0;
}
static int udc_nrf_shutdown(const struct device *dev)
{
LOG_INF("shutdown");
if (udc_ep_disable_internal(dev, USB_CONTROL_EP_OUT)) {
LOG_ERR("Failed to disable control endpoint");
return -EIO;
}
if (udc_ep_disable_internal(dev, USB_CONTROL_EP_IN)) {
LOG_ERR("Failed to disable control endpoint");
return -EIO;
}
nrfx_power_usbevt_disable();
nrfx_usbd_uninit();
nrfx_power_usbevt_uninit();
#ifdef CONFIG_HAS_HW_NRF_USBREG
irq_disable(USBREGULATOR_IRQn);
#endif
return 0;
}
static int udc_nrf_driver_init(const struct device *dev)
{
struct udc_data *data = dev->data;
int err;
LOG_INF("Preinit");
udc_nrf_dev = dev;
k_mutex_init(&data->mutex);
k_thread_create(&drv_stack_data, drv_stack,
K_KERNEL_STACK_SIZEOF(drv_stack),
(k_thread_entry_t)udc_nrf_thread,
(void *)dev, NULL, NULL,
K_PRIO_COOP(8), 0, K_NO_WAIT);
k_thread_name_set(&drv_stack_data, "udc_nrfx");
for (int i = 0; i < ARRAY_SIZE(ep_cfg_out); i++) {
ep_cfg_out[i].caps.out = 1;
if (i == 0) {
ep_cfg_out[i].caps.control = 1;
ep_cfg_out[i].caps.mps = NRFX_USBD_EPSIZE;
} else if (i < (CFG_EPOUT_CNT + 1)) {
ep_cfg_out[i].caps.bulk = 1;
ep_cfg_out[i].caps.interrupt = 1;
ep_cfg_out[i].caps.mps = NRFX_USBD_EPSIZE;
} else {
ep_cfg_out[i].caps.iso = 1;
ep_cfg_out[i].caps.mps = NRFX_USBD_ISOSIZE / 2;
}
ep_cfg_out[i].addr = USB_EP_DIR_OUT | i;
err = udc_register_ep(dev, &ep_cfg_out[i]);
if (err != 0) {
LOG_ERR("Failed to register endpoint");
return err;
}
}
for (int i = 0; i < ARRAY_SIZE(ep_cfg_in); i++) {
ep_cfg_in[i].caps.in = 1;
if (i == 0) {
ep_cfg_in[i].caps.control = 1;
ep_cfg_in[i].caps.mps = NRFX_USBD_EPSIZE;
} else if (i < (CFG_EPIN_CNT + 1)) {
ep_cfg_in[i].caps.bulk = 1;
ep_cfg_in[i].caps.interrupt = 1;
ep_cfg_in[i].caps.mps = NRFX_USBD_EPSIZE;
} else {
ep_cfg_in[i].caps.iso = 1;
ep_cfg_in[i].caps.mps = NRFX_USBD_ISOSIZE / 2;
}
ep_cfg_in[i].addr = USB_EP_DIR_IN | i;
err = udc_register_ep(dev, &ep_cfg_in[i]);
if (err != 0) {
LOG_ERR("Failed to register endpoint");
return err;
}
}
data->caps.rwup = true;
data->caps.out_ack = true;
return 0;
}
static int udc_nrf_lock(const struct device *dev)
{
return udc_lock_internal(dev, K_FOREVER);
}
static int udc_nrf_unlock(const struct device *dev)
{
return udc_unlock_internal(dev);
}
static const struct udc_nrf_config udc_nrf_cfg = {
.clock = COND_CODE_1(NRF_CLOCK_HAS_HFCLK192M,
(CLOCK_CONTROL_NRF_SUBSYS_HF192M),
(CLOCK_CONTROL_NRF_SUBSYS_HF)),
.pwr = {
.dcdcen = IS_ENABLED(CONFIG_SOC_DCDC_NRF52X) ||
IS_ENABLED(CONFIG_SOC_DCDC_NRF53X_APP),
#if NRFX_POWER_SUPPORTS_DCDCEN_VDDH
.dcdcenhv = IS_ENABLED(CONFIG_SOC_DCDC_NRF52X_HV) ||
IS_ENABLED(CONFIG_SOC_DCDC_NRF53X_HV),
#endif
},
.evt = {
.handler = udc_nrf_power_handler
},
};
static struct udc_data udc_nrf_data = {
.mutex = Z_MUTEX_INITIALIZER(udc_nrf_data.mutex),
.priv = NULL,
};
static const struct udc_api udc_nrf_api = {
.lock = udc_nrf_lock,
.unlock = udc_nrf_unlock,
.init = udc_nrf_init,
.enable = udc_nrf_enable,
.disable = udc_nrf_disable,
.shutdown = udc_nrf_shutdown,
.set_address = udc_nrf_set_address,
.host_wakeup = udc_nrf_host_wakeup,
.ep_try_config = NULL,
.ep_enable = udc_nrf_ep_enable,
.ep_disable = udc_nrf_ep_disable,
.ep_set_halt = udc_nrf_ep_set_halt,
.ep_clear_halt = udc_nrf_ep_clear_halt,
.ep_flush = udc_nrf_ep_flush,
.ep_enqueue = udc_nrf_ep_enqueue,
.ep_dequeue = udc_nrf_ep_dequeue,
};
DEVICE_DT_INST_DEFINE(0, udc_nrf_driver_init, NULL,
&udc_nrf_data, &udc_nrf_cfg,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&udc_nrf_api);