diff --git a/drivers/usb/udc/CMakeLists.txt b/drivers/usb/udc/CMakeLists.txt index 883b8fc13d8..0fdc321a314 100644 --- a/drivers/usb/udc/CMakeLists.txt +++ b/drivers/usb/udc/CMakeLists.txt @@ -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_RPI_PICO udc_rpi_pico.c) zephyr_library_sources_ifdef(CONFIG_UDC_AMBIQ udc_ambiq.c) +zephyr_library_sources_ifdef(CONFIG_UDC_RENESAS_RA udc_renesas_ra.c) diff --git a/drivers/usb/udc/Kconfig b/drivers/usb/udc/Kconfig index a88c5a1ddd9..fd5e25cf113 100644 --- a/drivers/usb/udc/Kconfig +++ b/drivers/usb/udc/Kconfig @@ -66,5 +66,6 @@ source "drivers/usb/udc/Kconfig.mcux" source "drivers/usb/udc/Kconfig.numaker" source "drivers/usb/udc/Kconfig.rpi_pico" source "drivers/usb/udc/Kconfig.ambiq" +source "drivers/usb/udc/Kconfig.renesas_ra" endif # UDC_DRIVER diff --git a/drivers/usb/udc/Kconfig.renesas_ra b/drivers/usb/udc/Kconfig.renesas_ra new file mode 100644 index 00000000000..9b2a784e392 --- /dev/null +++ b/drivers/usb/udc/Kconfig.renesas_ra @@ -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 diff --git a/drivers/usb/udc/udc_renesas_ra.c b/drivers/usb/udc/udc_renesas_ra.c new file mode 100644 index 00000000000..a8b127a3510 --- /dev/null +++ b/drivers/usb/udc/udc_renesas_ra.c @@ -0,0 +1,754 @@ +/* + * Copyright (c) 2024 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "udc_common.h" + +#include +#include +#include +#include +#include "r_usb_device.h" + +#include +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) diff --git a/dts/bindings/phy/renesas,ra-usbphyc.yaml b/dts/bindings/phy/renesas,ra-usbphyc.yaml new file mode 100644 index 00000000000..45806d2e1d9 --- /dev/null +++ b/dts/bindings/phy/renesas,ra-usbphyc.yaml @@ -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 diff --git a/dts/bindings/usb/renesas/renesas,ra-udc.yaml b/dts/bindings/usb/renesas/renesas,ra-udc.yaml new file mode 100644 index 00000000000..495c368fcae --- /dev/null +++ b/dts/bindings/usb/renesas/renesas,ra-udc.yaml @@ -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" diff --git a/dts/bindings/usb/renesas/renesas,ra-usb.yaml b/dts/bindings/usb/renesas/renesas,ra-usb.yaml new file mode 100644 index 00000000000..ec31e250eb3 --- /dev/null +++ b/dts/bindings/usb/renesas/renesas,ra-usb.yaml @@ -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 diff --git a/modules/Kconfig.renesas_fsp b/modules/Kconfig.renesas_fsp index f8c85d90eb6..ec8e78b9aba 100644 --- a/modules/Kconfig.renesas_fsp +++ b/modules/Kconfig.renesas_fsp @@ -103,6 +103,11 @@ config USE_RA_FSP_ETHER help 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 if HAS_RENESAS_RZ_FSP