drivers: udc: add support for Renesas RA USB device
First commit to support UDC on Renesas RA USBHS module Signed-off-by: The Nguyen <the.nguyen.yf@renesas.com>
This commit is contained in:
parent
f9cea63608
commit
2cfd6065dd
8 changed files with 842 additions and 0 deletions
|
@ -19,3 +19,4 @@ zephyr_library_sources_ifdef(CONFIG_UDC_NXP_IP3511 udc_mcux_ip3511.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_UDC_NUMAKER udc_numaker.c)
|
zephyr_library_sources_ifdef(CONFIG_UDC_NUMAKER udc_numaker.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_UDC_RPI_PICO udc_rpi_pico.c)
|
zephyr_library_sources_ifdef(CONFIG_UDC_RPI_PICO udc_rpi_pico.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_UDC_AMBIQ udc_ambiq.c)
|
zephyr_library_sources_ifdef(CONFIG_UDC_AMBIQ udc_ambiq.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_UDC_RENESAS_RA udc_renesas_ra.c)
|
||||||
|
|
|
@ -66,5 +66,6 @@ source "drivers/usb/udc/Kconfig.mcux"
|
||||||
source "drivers/usb/udc/Kconfig.numaker"
|
source "drivers/usb/udc/Kconfig.numaker"
|
||||||
source "drivers/usb/udc/Kconfig.rpi_pico"
|
source "drivers/usb/udc/Kconfig.rpi_pico"
|
||||||
source "drivers/usb/udc/Kconfig.ambiq"
|
source "drivers/usb/udc/Kconfig.ambiq"
|
||||||
|
source "drivers/usb/udc/Kconfig.renesas_ra"
|
||||||
|
|
||||||
endif # UDC_DRIVER
|
endif # UDC_DRIVER
|
||||||
|
|
34
drivers/usb/udc/Kconfig.renesas_ra
Normal file
34
drivers/usb/udc/Kconfig.renesas_ra
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# Copyright (c) 2024 Renesas Electronics Corporation
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
config UDC_RENESAS_RA
|
||||||
|
bool "Renesas RA family UDC driver"
|
||||||
|
default y
|
||||||
|
depends on DT_HAS_RENESAS_RA_UDC_ENABLED
|
||||||
|
select USE_RA_FSP_USB_DEVICE
|
||||||
|
select PINCTRL
|
||||||
|
help
|
||||||
|
Enable Renesas RA family UDC driver.
|
||||||
|
|
||||||
|
if UDC_RENESAS_RA
|
||||||
|
|
||||||
|
config UDC_RENESAS_RA_STACK_SIZE
|
||||||
|
int "UDC controller driver internal thread stack size"
|
||||||
|
default 512
|
||||||
|
help
|
||||||
|
Renesas RA device controller driver internal thread stack size.
|
||||||
|
|
||||||
|
config UDC_RENESAS_RA_THREAD_PRIORITY
|
||||||
|
int "Renesas RA family UDC driver thread priority"
|
||||||
|
default 8
|
||||||
|
help
|
||||||
|
Renesas RA device controller driver thread priority.
|
||||||
|
|
||||||
|
config UDC_RENESAS_RA_MAX_QMESSAGES
|
||||||
|
int "Renesas RA family UDC driver maximum number of ISR event messages"
|
||||||
|
range 4 64
|
||||||
|
default 8
|
||||||
|
help
|
||||||
|
Maximum number of messages for handling of Renesas RA USBD ISR events.
|
||||||
|
|
||||||
|
endif
|
754
drivers/usb/udc/udc_renesas_ra.c
Normal file
754
drivers/usb/udc/udc_renesas_ra.c
Normal file
|
@ -0,0 +1,754 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Renesas Electronics Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "udc_common.h"
|
||||||
|
|
||||||
|
#include <soc.h>
|
||||||
|
#include <zephyr/drivers/pinctrl.h>
|
||||||
|
#include <zephyr/drivers/clock_control/renesas_ra_cgc.h>
|
||||||
|
#include <zephyr/drivers/usb/udc.h>
|
||||||
|
#include "r_usb_device.h"
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(udc_renesas_ra, CONFIG_UDC_DRIVER_LOG_LEVEL);
|
||||||
|
|
||||||
|
struct udc_renesas_ra_config {
|
||||||
|
const struct pinctrl_dev_config *pcfg;
|
||||||
|
size_t num_of_eps;
|
||||||
|
struct udc_ep_config *ep_cfg_in;
|
||||||
|
struct udc_ep_config *ep_cfg_out;
|
||||||
|
void (*make_thread)(const struct device *dev);
|
||||||
|
int speed_idx;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct udc_renesas_ra_data {
|
||||||
|
struct k_thread thread_data;
|
||||||
|
struct st_usbd_instance_ctrl udc;
|
||||||
|
struct st_usbd_cfg udc_cfg;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum udc_renesas_ra_event_type {
|
||||||
|
/* An event generated by the HAL driver */
|
||||||
|
UDC_RENESAS_RA_EVT_HAL,
|
||||||
|
/* Shim driver event to trigger next transfer */
|
||||||
|
UDC_RENESAS_RA_EVT_XFER,
|
||||||
|
/* Let controller perform status stage */
|
||||||
|
UDC_RENESAS_RA_EVT_STATUS,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct udc_renesas_ra_evt {
|
||||||
|
enum udc_renesas_ra_event_type type;
|
||||||
|
usbd_event_t hal_evt;
|
||||||
|
uint8_t ep;
|
||||||
|
};
|
||||||
|
|
||||||
|
K_MSGQ_DEFINE(drv_msgq, sizeof(struct udc_renesas_ra_evt), CONFIG_UDC_RENESAS_RA_MAX_QMESSAGES,
|
||||||
|
sizeof(uint32_t));
|
||||||
|
|
||||||
|
extern void usb_device_isr(void);
|
||||||
|
|
||||||
|
static void udc_renesas_ra_event_handler(usbd_callback_arg_t *p_args)
|
||||||
|
{
|
||||||
|
const struct device *dev = p_args->p_context;
|
||||||
|
struct udc_renesas_ra_evt evt;
|
||||||
|
|
||||||
|
switch (p_args->event.event_id) {
|
||||||
|
case USBD_EVENT_BUS_RESET:
|
||||||
|
udc_submit_event(dev, UDC_EVT_RESET, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USBD_EVENT_VBUS_RDY:
|
||||||
|
udc_submit_event(dev, UDC_EVT_VBUS_READY, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USBD_EVENT_VBUS_REMOVED:
|
||||||
|
udc_submit_event(dev, UDC_EVT_VBUS_REMOVED, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USBD_EVENT_SUSPEND:
|
||||||
|
udc_submit_event(dev, UDC_EVT_SUSPEND, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USBD_EVENT_RESUME:
|
||||||
|
udc_submit_event(dev, UDC_EVT_RESUME, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USBD_EVENT_SOF:
|
||||||
|
udc_submit_event(dev, UDC_EVT_SOF, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
evt.type = UDC_RENESAS_RA_EVT_HAL;
|
||||||
|
evt.hal_evt = p_args->event;
|
||||||
|
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void udc_renesas_ra_interrupt_handler(void *arg)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(arg);
|
||||||
|
usb_device_isr();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void udc_event_xfer_next(const struct device *dev, const uint8_t ep)
|
||||||
|
{
|
||||||
|
struct udc_renesas_ra_data *data = udc_get_private(dev);
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
if (udc_ep_is_busy(dev, ep)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = udc_buf_peek(dev, ep);
|
||||||
|
if (buf != NULL) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (USB_EP_DIR_IS_IN(ep)) {
|
||||||
|
err = R_USBD_XferStart(&data->udc, ep, buf->data, buf->len);
|
||||||
|
} else {
|
||||||
|
err = R_USBD_XferStart(&data->udc, ep, buf->data, buf->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err != FSP_SUCCESS) {
|
||||||
|
LOG_ERR("ep 0x%02x error", ep);
|
||||||
|
udc_submit_ep_event(dev, buf, -ECONNREFUSED);
|
||||||
|
} else {
|
||||||
|
udc_ep_set_busy(dev, ep, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
struct udc_renesas_ra_data *data = udc_get_private(dev);
|
||||||
|
|
||||||
|
buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length);
|
||||||
|
if (buf == NULL) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_fifo_put(&cfg->fifo, buf);
|
||||||
|
|
||||||
|
if (FSP_SUCCESS != R_USBD_XferStart(&data->udc, cfg->addr, buf->data, buf->size)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udc_event_xfer_setup(const struct device *dev, struct udc_renesas_ra_evt *evt)
|
||||||
|
{
|
||||||
|
struct net_buf *buf;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
struct usb_setup_packet *setup_packet =
|
||||||
|
(struct usb_setup_packet *)&evt->hal_evt.setup_received;
|
||||||
|
|
||||||
|
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);
|
||||||
|
net_buf_add_mem(buf, setup_packet, sizeof(struct usb_setup_packet));
|
||||||
|
|
||||||
|
/* 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_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 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update to next stage of control transfer */
|
||||||
|
udc_ctrl_update_stage(dev, buf);
|
||||||
|
|
||||||
|
if (udc_ctrl_stage_is_status_out(dev)) {
|
||||||
|
/* IN transfer finished, perform status stage OUT and release buffer */
|
||||||
|
usbd_ctrl_feed_dout(dev, 0);
|
||||||
|
net_buf_unref(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void udc_event_status_in(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct udc_renesas_ra_data *data = udc_get_private(dev);
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
buf = udc_buf_get(dev, USB_CONTROL_EP_IN);
|
||||||
|
if (unlikely(buf == NULL)) {
|
||||||
|
LOG_DBG("ep 0x%02x queue is empty", USB_CONTROL_EP_IN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform status stage IN */
|
||||||
|
R_USBD_XferStart(&data->udc, USB_CONTROL_EP_IN, NULL, 0);
|
||||||
|
|
||||||
|
udc_event_xfer_ctrl_in(dev, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void udc_event_xfer_ctrl_out(const struct device *dev, struct net_buf *const buf,
|
||||||
|
uint32_t len)
|
||||||
|
{
|
||||||
|
net_buf_add(buf, len);
|
||||||
|
|
||||||
|
if (udc_ctrl_stage_is_status_out(dev)) {
|
||||||
|
/* Status stage finished, notify upper layer */
|
||||||
|
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_in(dev)) {
|
||||||
|
udc_ctrl_submit_s_out_status(dev, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void udc_event_xfer_complete(const struct device *dev, struct udc_renesas_ra_evt *evt)
|
||||||
|
{
|
||||||
|
struct net_buf *buf;
|
||||||
|
struct udc_renesas_ra_data *data = udc_get_private(dev);
|
||||||
|
|
||||||
|
uint8_t ep = evt->hal_evt.xfer_complete.ep_addr;
|
||||||
|
usbd_xfer_result_t result = evt->hal_evt.xfer_complete.result;
|
||||||
|
uint32_t len = evt->hal_evt.xfer_complete.len;
|
||||||
|
|
||||||
|
udc_ep_set_busy(dev, ep, false);
|
||||||
|
|
||||||
|
buf = udc_buf_peek(dev, ep);
|
||||||
|
if (buf == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != USBD_XFER_RESULT_SUCCESS) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (USB_EP_DIR_IS_IN(ep) && udc_ep_buf_has_zlp(buf)) {
|
||||||
|
/* Send ZLP, notification about transfer complete should come again */
|
||||||
|
udc_ep_buf_clear_zlp(buf);
|
||||||
|
if (FSP_SUCCESS != R_USBD_XferStart(&data->udc, ep, NULL, 0)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = udc_buf_get(dev, ep);
|
||||||
|
|
||||||
|
if (ep == USB_CONTROL_EP_IN) {
|
||||||
|
udc_event_xfer_ctrl_in(dev, buf);
|
||||||
|
} else if (ep == USB_CONTROL_EP_OUT) {
|
||||||
|
udc_event_xfer_ctrl_out(dev, buf, len);
|
||||||
|
} else {
|
||||||
|
if (USB_EP_DIR_IS_OUT(ep)) {
|
||||||
|
net_buf_add(buf, len);
|
||||||
|
}
|
||||||
|
udc_submit_ep_event(dev, buf, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
error:
|
||||||
|
udc_submit_ep_event(dev, buf, -EIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ALWAYS_INLINE void renesas_ra_thread_handler(void *const arg)
|
||||||
|
{
|
||||||
|
const struct device *dev = (const struct device *)arg;
|
||||||
|
|
||||||
|
LOG_DBG("Driver %p thread started", dev);
|
||||||
|
while (true) {
|
||||||
|
struct udc_renesas_ra_evt evt;
|
||||||
|
|
||||||
|
k_msgq_get(&drv_msgq, &evt, K_FOREVER);
|
||||||
|
switch (evt.type) {
|
||||||
|
case UDC_RENESAS_RA_EVT_HAL: {
|
||||||
|
switch (evt.hal_evt.event_id) {
|
||||||
|
case USBD_EVENT_SETUP_RECEIVED:
|
||||||
|
udc_event_xfer_setup(dev, &evt);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USBD_EVENT_XFER_COMPLETE:
|
||||||
|
udc_event_xfer_complete(dev, &evt);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case UDC_RENESAS_RA_EVT_XFER:
|
||||||
|
udc_event_xfer_next(dev, evt.ep);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UDC_RENESAS_RA_EVT_STATUS:
|
||||||
|
udc_event_status_in(dev);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udc_renesas_ra_ep_enqueue(const struct device *dev, struct udc_ep_config *const cfg,
|
||||||
|
struct net_buf *buf)
|
||||||
|
{
|
||||||
|
struct udc_renesas_ra_evt evt = {};
|
||||||
|
|
||||||
|
LOG_DBG("%p enqueue %p", dev, buf);
|
||||||
|
|
||||||
|
udc_buf_put(cfg, buf);
|
||||||
|
|
||||||
|
evt.ep = cfg->addr;
|
||||||
|
if (cfg->addr == USB_CONTROL_EP_IN && buf->len == 0) {
|
||||||
|
evt.type = UDC_RENESAS_RA_EVT_STATUS;
|
||||||
|
} else {
|
||||||
|
evt.type = UDC_RENESAS_RA_EVT_XFER;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
|
||||||
|
|
||||||
|
if (cfg->stat.halted) {
|
||||||
|
LOG_DBG("ep 0x%02x halted", cfg->addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udc_renesas_ra_ep_dequeue(const struct device *dev, struct udc_ep_config *const cfg)
|
||||||
|
{
|
||||||
|
struct udc_renesas_ra_data *data = udc_get_private(dev);
|
||||||
|
unsigned int lock_key;
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
lock_key = irq_lock();
|
||||||
|
|
||||||
|
buf = udc_buf_get_all(dev, cfg->addr);
|
||||||
|
if (buf != NULL) {
|
||||||
|
udc_submit_ep_event(dev, buf, -ECONNABORTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FSP_SUCCESS != R_USBD_XferAbort(&data->udc, cfg->addr)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
udc_ep_set_busy(dev, cfg->addr, false);
|
||||||
|
|
||||||
|
irq_unlock(lock_key);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udc_renesas_ra_ep_enable(const struct device *dev, struct udc_ep_config *const cfg)
|
||||||
|
{
|
||||||
|
struct udc_renesas_ra_data *data = udc_get_private(dev);
|
||||||
|
usbd_desc_endpoint_t ep_desc;
|
||||||
|
|
||||||
|
if (USB_EP_GET_IDX(cfg->addr) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ep_desc.bLength = sizeof(struct usb_ep_descriptor);
|
||||||
|
ep_desc.bDescriptorType = USB_DESC_ENDPOINT;
|
||||||
|
ep_desc.bEndpointAddress = cfg->addr;
|
||||||
|
ep_desc.bmAttributes = cfg->attributes;
|
||||||
|
ep_desc.wMaxPacketSize = cfg->mps;
|
||||||
|
ep_desc.bInterval = cfg->interval;
|
||||||
|
|
||||||
|
if (FSP_SUCCESS != R_USBD_EdptOpen(&data->udc, &ep_desc)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Enable ep 0x%02x", cfg->addr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udc_renesas_ra_ep_disable(const struct device *dev, struct udc_ep_config *const cfg)
|
||||||
|
{
|
||||||
|
struct udc_renesas_ra_data *data = udc_get_private(dev);
|
||||||
|
|
||||||
|
if (USB_EP_GET_IDX(cfg->addr) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FSP_SUCCESS != R_USBD_EdptClose(&data->udc, cfg->addr)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Disable ep 0x%02x", cfg->addr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udc_renesas_ra_ep_set_halt(const struct device *dev, struct udc_ep_config *const cfg)
|
||||||
|
{
|
||||||
|
struct udc_renesas_ra_data *data = udc_get_private(dev);
|
||||||
|
|
||||||
|
LOG_DBG("Set halt ep 0x%02x", cfg->addr);
|
||||||
|
|
||||||
|
if (FSP_SUCCESS != R_USBD_EdptStall(&data->udc, cfg->addr)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg->stat.halted = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udc_renesas_ra_ep_clear_halt(const struct device *dev, struct udc_ep_config *const cfg)
|
||||||
|
{
|
||||||
|
struct udc_renesas_ra_data *data = udc_get_private(dev);
|
||||||
|
|
||||||
|
LOG_DBG("Clear halt ep 0x%02x", cfg->addr);
|
||||||
|
|
||||||
|
if (FSP_SUCCESS != R_USBD_EdptClearStall(&data->udc, cfg->addr)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg->stat.halted = false;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udc_renesas_ra_set_address(const struct device *dev, const uint8_t addr)
|
||||||
|
{
|
||||||
|
/* The USB controller will automatically perform a response to the SET_ADRRESS request. */
|
||||||
|
LOG_DBG("Set new address %u for %p", addr, dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udc_renesas_ra_host_wakeup(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct udc_renesas_ra_data *data = udc_get_private(dev);
|
||||||
|
|
||||||
|
if (FSP_SUCCESS != R_USBD_RemoteWakeup(&data->udc)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Remote wakeup from %p", dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum udc_bus_speed udc_renesas_ra_device_speed(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct udc_data *data = dev->data;
|
||||||
|
|
||||||
|
return data->caps.hs ? UDC_BUS_SPEED_HS : UDC_BUS_SPEED_FS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udc_renesas_ra_enable(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct udc_renesas_ra_data *data = udc_get_private(dev);
|
||||||
|
|
||||||
|
if (FSP_SUCCESS != R_USBD_Connect(&data->udc)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Enable device %p", dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udc_renesas_ra_disable(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct udc_renesas_ra_data *data = udc_get_private(dev);
|
||||||
|
|
||||||
|
if (FSP_SUCCESS != R_USBD_Disconnect(&data->udc)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Enable device %p", dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udc_renesas_ra_init(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct udc_renesas_ra_data *data = udc_get_private(dev);
|
||||||
|
|
||||||
|
#if !USBHS_PHY_CLOCK_SOURCE_IS_XTAL
|
||||||
|
if (data->udc_cfg.usb_speed == USBD_SPEED_HS) {
|
||||||
|
LOG_ERR("High-speed operation is not supported in case PHY clock source is not "
|
||||||
|
"XTAL");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t uclk_src = RA_CGC_CLK_SRC(DT_CLOCKS_CTLR(DT_NODELABEL(uclk)));
|
||||||
|
uint32_t uclk_div = DT_PROP_OR(DT_NODELABEL(uclk), div, 1);
|
||||||
|
uint32_t u60clk_src = RA_CGC_CLK_SRC(DT_CLOCKS_CTLR(DT_NODELABEL(u60clk)));
|
||||||
|
uint32_t u60clk_div = DT_PROP_OR(DT_NODELABEL(u60clk), div, 1);
|
||||||
|
|
||||||
|
if (uclk_src == BSP_CLOCKS_CLOCK_DISABLED || u60clk_src == BSP_CLOCKS_CLOCK_DISABLED) {
|
||||||
|
LOG_ERR("PHY clock is not working");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t uclk_clock_rate = R_BSP_SourceClockHzGet(uclk_src) / uclk_div;
|
||||||
|
uint32_t u60clk_clock_rate = R_BSP_SourceClockHzGet(u60clk_src) / u60clk_div;
|
||||||
|
|
||||||
|
if (uclk_clock_rate != 48000000) {
|
||||||
|
LOG_ERR("Setting for uclk should be 48Mhz");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (u60clk_clock_rate != 60000000) {
|
||||||
|
LOG_ERR("Setting for u60clk should be 60Mhz");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!(data->udc_cfg.usb_speed == USBD_SPEED_HS ||
|
||||||
|
data->udc_cfg.usb_speed == USBD_SPEED_FS)) {
|
||||||
|
LOG_ERR("USB device mode support full-speed and high-speed only");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FSP_SUCCESS != R_USBD_Open(&data->udc, &data->udc_cfg)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_enable(data->udc_cfg.hs_irq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udc_renesas_ra_shutdown(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct udc_renesas_ra_data *data = udc_get_private(dev);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FSP_SUCCESS != R_USBD_Close(&data->udc)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udc_renesas_ra_driver_preinit(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct udc_renesas_ra_config *config = dev->config;
|
||||||
|
struct udc_data *data = dev->data;
|
||||||
|
uint16_t mps = 1023;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
|
||||||
|
if (err < 0) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_mutex_init(&data->mutex);
|
||||||
|
|
||||||
|
data->caps.rwup = true;
|
||||||
|
data->caps.mps0 = UDC_MPS0_64;
|
||||||
|
if (config->speed_idx == UDC_BUS_SPEED_HS) {
|
||||||
|
data->caps.hs = true;
|
||||||
|
mps = 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = mps;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = mps;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config->make_thread(dev);
|
||||||
|
LOG_INF("Device %p (max. speed %d)", dev, config->speed_idx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udc_renesas_ra_lock(const struct device *dev)
|
||||||
|
{
|
||||||
|
return udc_lock_internal(dev, K_FOREVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udc_renesas_ra_unlock(const struct device *dev)
|
||||||
|
{
|
||||||
|
return udc_unlock_internal(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct udc_api udc_renesas_ra_api = {
|
||||||
|
.lock = udc_renesas_ra_lock,
|
||||||
|
.unlock = udc_renesas_ra_unlock,
|
||||||
|
.device_speed = udc_renesas_ra_device_speed,
|
||||||
|
.init = udc_renesas_ra_init,
|
||||||
|
.enable = udc_renesas_ra_enable,
|
||||||
|
.disable = udc_renesas_ra_disable,
|
||||||
|
.shutdown = udc_renesas_ra_shutdown,
|
||||||
|
.set_address = udc_renesas_ra_set_address,
|
||||||
|
.host_wakeup = udc_renesas_ra_host_wakeup,
|
||||||
|
.ep_enable = udc_renesas_ra_ep_enable,
|
||||||
|
.ep_disable = udc_renesas_ra_ep_disable,
|
||||||
|
.ep_set_halt = udc_renesas_ra_ep_set_halt,
|
||||||
|
.ep_clear_halt = udc_renesas_ra_ep_clear_halt,
|
||||||
|
.ep_enqueue = udc_renesas_ra_ep_enqueue,
|
||||||
|
.ep_dequeue = udc_renesas_ra_ep_dequeue,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT renesas_ra_udc
|
||||||
|
|
||||||
|
#define USB_MODULE_NUMBER(n) ((DT_REG_ADDR(DT_INST_PARENT(n))) == R_USB_HS0_BASE ? 1 : 0)
|
||||||
|
|
||||||
|
#define RENESAS_RA_USB_IRQ_CONFIG_FUNC(n) \
|
||||||
|
static int udc_renesas_ra_irq_config_func_##n(const struct device *dev) \
|
||||||
|
{ \
|
||||||
|
struct udc_renesas_ra_data *data = udc_get_private(dev); \
|
||||||
|
\
|
||||||
|
data->udc_cfg.hs_irq = DT_IRQ_BY_NAME(DT_INST_PARENT(n), usbhs_ir, irq); \
|
||||||
|
data->udc_cfg.hsirq_d0 = DT_IRQ_BY_NAME(DT_INST_PARENT(n), usbhs_d0, irq); \
|
||||||
|
data->udc_cfg.hsirq_d1 = DT_IRQ_BY_NAME(DT_INST_PARENT(n), usbhs_d1, irq); \
|
||||||
|
data->udc_cfg.hsipl = DT_IRQ_BY_NAME(DT_INST_PARENT(n), usbhs_ir, priority); \
|
||||||
|
data->udc_cfg.hsipl_d0 = DT_IRQ_BY_NAME(DT_INST_PARENT(n), usbhs_d0, priority); \
|
||||||
|
data->udc_cfg.hsipl_d1 = DT_IRQ_BY_NAME(DT_INST_PARENT(n), usbhs_d1, priority); \
|
||||||
|
\
|
||||||
|
R_ICU->IELSR[DT_IRQ_BY_NAME(DT_NODELABEL(usbhs), usbhs_ir, irq)] = \
|
||||||
|
ELC_EVENT_USBHS_USB_INT_RESUME; \
|
||||||
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_NODELABEL(usbhs), usbhs_ir, irq), \
|
||||||
|
DT_IRQ_BY_NAME(DT_NODELABEL(usbhs), usbhs_ir, priority), \
|
||||||
|
udc_renesas_ra_interrupt_handler, DEVICE_DT_INST_GET(n), 0); \
|
||||||
|
return 0; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define UDC_RENESAS_RA_DEVICE_DEFINE(n) \
|
||||||
|
PINCTRL_DT_DEFINE(DT_INST_PARENT(n)); \
|
||||||
|
K_THREAD_STACK_DEFINE(udc_renesas_ra_stack_##n, CONFIG_UDC_RENESAS_RA_STACK_SIZE); \
|
||||||
|
RENESAS_RA_USB_IRQ_CONFIG_FUNC(n); \
|
||||||
|
\
|
||||||
|
static void udc_renesas_ra_thread_##n(void *dev, void *arg1, void *arg2) \
|
||||||
|
{ \
|
||||||
|
renesas_ra_thread_handler(dev); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static void udc_renesas_ra_make_thread_##n(const struct device *dev) \
|
||||||
|
{ \
|
||||||
|
struct udc_renesas_ra_data *priv = udc_get_private(dev); \
|
||||||
|
\
|
||||||
|
k_thread_create(&priv->thread_data, udc_renesas_ra_stack_##n, \
|
||||||
|
K_THREAD_STACK_SIZEOF(udc_renesas_ra_stack_##n), \
|
||||||
|
udc_renesas_ra_thread_##n, (void *)dev, NULL, NULL, \
|
||||||
|
K_PRIO_COOP(CONFIG_UDC_RENESAS_RA_THREAD_PRIORITY), K_ESSENTIAL, \
|
||||||
|
K_NO_WAIT); \
|
||||||
|
k_thread_name_set(&priv->thread_data, dev->name); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static struct udc_ep_config ep_cfg_in##n[DT_PROP(DT_INST_PARENT(n), num_bidir_endpoints)]; \
|
||||||
|
static struct udc_ep_config \
|
||||||
|
ep_cfg_out##n[DT_PROP(DT_INST_PARENT(n), num_bidir_endpoints)]; \
|
||||||
|
\
|
||||||
|
static const struct udc_renesas_ra_config udc_renesas_ra_config_##n = { \
|
||||||
|
.pcfg = PINCTRL_DT_DEV_CONFIG_GET(DT_INST_PARENT(n)), \
|
||||||
|
.num_of_eps = DT_PROP(DT_INST_PARENT(n), num_bidir_endpoints), \
|
||||||
|
.ep_cfg_in = ep_cfg_in##n, \
|
||||||
|
.ep_cfg_out = ep_cfg_out##n, \
|
||||||
|
.make_thread = udc_renesas_ra_make_thread_##n, \
|
||||||
|
.speed_idx = DT_ENUM_IDX_OR(DT_INST_PARENT(n), maximum_speed, UDC_BUS_SPEED_HS), \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
static struct udc_renesas_ra_data udc_priv_##n = { \
|
||||||
|
.udc_cfg = { \
|
||||||
|
.module_number = USB_MODULE_NUMBER(n), \
|
||||||
|
.usb_speed = DT_ENUM_IDX_OR(DT_INST_PARENT(n), maximum_speed, \
|
||||||
|
UDC_BUS_SPEED_HS), \
|
||||||
|
.p_context = DEVICE_DT_INST_GET(n), \
|
||||||
|
.p_callback = udc_renesas_ra_event_handler, \
|
||||||
|
}}; \
|
||||||
|
\
|
||||||
|
static struct udc_data udc_data_##n = { \
|
||||||
|
.mutex = Z_MUTEX_INITIALIZER(udc_data_##n.mutex), \
|
||||||
|
.priv = &udc_priv_##n, \
|
||||||
|
}; \
|
||||||
|
int udc_renesas_ra_driver_preinit##n(const struct device *dev) \
|
||||||
|
{ \
|
||||||
|
udc_renesas_ra_irq_config_func_##n(dev); \
|
||||||
|
return udc_renesas_ra_driver_preinit(dev); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
DEVICE_DT_INST_DEFINE(n, udc_renesas_ra_driver_preinit##n, NULL, &udc_data_##n, \
|
||||||
|
&udc_renesas_ra_config_##n, POST_KERNEL, \
|
||||||
|
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &udc_renesas_ra_api);
|
||||||
|
|
||||||
|
DT_INST_FOREACH_STATUS_OKAY(UDC_RENESAS_RA_DEVICE_DEFINE)
|
25
dts/bindings/phy/renesas,ra-usbphyc.yaml
Normal file
25
dts/bindings/phy/renesas,ra-usbphyc.yaml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# Copyright (c) 2024 Renesas Electronics Corporation
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
description: Renesas RA USBHS internal PHY controller
|
||||||
|
|
||||||
|
compatible: "renesas,ra-usbphyc"
|
||||||
|
|
||||||
|
include: phy-controller.yaml
|
||||||
|
|
||||||
|
properties:
|
||||||
|
clock:
|
||||||
|
type: phandle
|
||||||
|
description: |
|
||||||
|
Clock source for PHY clock in case internal clock is using
|
||||||
|
|
||||||
|
phys-clock-src:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- "internal"
|
||||||
|
- "xtal"
|
||||||
|
description: |
|
||||||
|
Select clock source for PHY clock as XTAL or use internal clock
|
||||||
|
|
||||||
|
"#phy-cells":
|
||||||
|
const: 0
|
6
dts/bindings/usb/renesas/renesas,ra-udc.yaml
Normal file
6
dts/bindings/usb/renesas/renesas,ra-udc.yaml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Copyright (c) 2024 Renesas Electronics Corporation
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
description: Renesas RA USB device controller
|
||||||
|
|
||||||
|
compatible: "renesas,ra-udc"
|
16
dts/bindings/usb/renesas/renesas,ra-usb.yaml
Normal file
16
dts/bindings/usb/renesas/renesas,ra-usb.yaml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# Copyright (c) 2024 Renesas Electronics Corporation
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
description: Renesas RA USB controller
|
||||||
|
|
||||||
|
compatible: "renesas,ra-usb"
|
||||||
|
|
||||||
|
include: [pinctrl-device.yaml, usb-ep.yaml]
|
||||||
|
|
||||||
|
properties:
|
||||||
|
reg:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
phys:
|
||||||
|
type: phandle
|
||||||
|
description: PHY provider specifier
|
|
@ -103,6 +103,11 @@ config USE_RA_FSP_ETHER
|
||||||
help
|
help
|
||||||
Enable RA FSP Ethernet driver
|
Enable RA FSP Ethernet driver
|
||||||
|
|
||||||
|
config USE_RA_FSP_USB_DEVICE
|
||||||
|
bool
|
||||||
|
help
|
||||||
|
Enable RA FSP USB Device Controller driver
|
||||||
|
|
||||||
endif # HAS_RENESAS_RA_FSP
|
endif # HAS_RENESAS_RA_FSP
|
||||||
|
|
||||||
if HAS_RENESAS_RZ_FSP
|
if HAS_RENESAS_RZ_FSP
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue