diff --git a/boards/renesas/ek_ra6m3/ek_ra6m3-pinctrl.dtsi b/boards/renesas/ek_ra6m3/ek_ra6m3-pinctrl.dtsi index 553e8895419f5..441990d6154cd 100644 --- a/boards/renesas/ek_ra6m3/ek_ra6m3-pinctrl.dtsi +++ b/boards/renesas/ek_ra6m3/ek_ra6m3-pinctrl.dtsi @@ -33,7 +33,8 @@ usbhs_default: usbhs_default { group1 { - psels = ; /* VBUS */ + psels = , /* VBUS */ + ; /* VBUSEN */ drive-strength = "high"; }; }; @@ -64,8 +65,8 @@ usbfs_default: usbfs_default { group1 { - /* USB_VBUS */ - psels = ; + psels = , /* VBUS */ + ; /* VBUSEN */ drive-strength = "high"; }; }; diff --git a/boards/renesas/ek_ra6m3/ek_ra6m3.dts b/boards/renesas/ek_ra6m3/ek_ra6m3.dts index e83039c0e80b2..cbcdd7b124aca 100644 --- a/boards/renesas/ek_ra6m3/ek_ra6m3.dts +++ b/boards/renesas/ek_ra6m3/ek_ra6m3.dts @@ -199,6 +199,11 @@ pinctrl-0 = <&usbfs_default>; pinctrl-names = "default"; maximum-speed = "full-speed"; + status = "okay"; + + zephyr_uhc0: uhc { + status = "okay"; + }; }; &wdt { diff --git a/boards/renesas/ek_ra6m5/ek_ra6m5-pinctrl.dtsi b/boards/renesas/ek_ra6m5/ek_ra6m5-pinctrl.dtsi index 0a9f7c94453c3..80221604d5c72 100644 --- a/boards/renesas/ek_ra6m5/ek_ra6m5-pinctrl.dtsi +++ b/boards/renesas/ek_ra6m5/ek_ra6m5-pinctrl.dtsi @@ -51,7 +51,8 @@ usbhs_default: usbhs_default { group1 { - psels = ; /* USBHS-VBUS */ + psels = , /* VBUS */ + ; /* VBUSEN */ drive-strength = "high"; }; }; @@ -82,8 +83,8 @@ usbfs_default: usbfs_default { group1 { - /* USB_VBUS */ - psels = ; + psels = , /* VBUS */ + ; /* VBUSEN */ drive-strength = "high"; }; }; diff --git a/boards/renesas/ek_ra6m5/ek_ra6m5.dts b/boards/renesas/ek_ra6m5/ek_ra6m5.dts index 6e06fdf80e239..a57c510bb29a2 100644 --- a/boards/renesas/ek_ra6m5/ek_ra6m5.dts +++ b/boards/renesas/ek_ra6m5/ek_ra6m5.dts @@ -297,6 +297,11 @@ pinctrl-0 = <&usbfs_default>; pinctrl-names = "default"; maximum-speed = "full-speed"; + status = "okay"; + + zephyr_uhc0: uhc { + status = "okay"; + }; }; mikrobus_serial: &uart7 {}; diff --git a/boards/renesas/ek_ra8d1/ek_ra8d1-pinctrl.dtsi b/boards/renesas/ek_ra8d1/ek_ra8d1-pinctrl.dtsi index 10895f9cc7e99..b8c2c04003b34 100644 --- a/boards/renesas/ek_ra8d1/ek_ra8d1-pinctrl.dtsi +++ b/boards/renesas/ek_ra8d1/ek_ra8d1-pinctrl.dtsi @@ -121,7 +121,8 @@ usbhs_default: usbhs_default { group1 { - psels = ; /* USBHS-VBUS */ + psels = , /* VBUS */ + ; /* VBUSEN */ drive-strength = "high"; }; }; @@ -212,10 +213,10 @@ usbfs_default: usbfs_default { group1 { - /* USBP USBN */ - psels = , - , - ; + psels = , /* USBN */ + , /* USBP */ + , /* VBUS */ + ; /* VBUSEN */ drive-strength = "high"; }; }; diff --git a/boards/renesas/ek_ra8d1/ek_ra8d1.dts b/boards/renesas/ek_ra8d1/ek_ra8d1.dts index 8e434e957bb11..939e8add52cbb 100644 --- a/boards/renesas/ek_ra8d1/ek_ra8d1.dts +++ b/boards/renesas/ek_ra8d1/ek_ra8d1.dts @@ -471,6 +471,11 @@ dvp_20pin_interface: &ceu {}; pinctrl-0 = <&usbfs_default>; pinctrl-names = "default"; maximum-speed = "full-speed"; + status = "okay"; + + zephyr_uhc0: uhc { + status = "okay"; + }; }; &wdt { diff --git a/boards/renesas/ek_ra8m1/ek_ra8m1-pinctrl.dtsi b/boards/renesas/ek_ra8m1/ek_ra8m1-pinctrl.dtsi index c1ae8b36cdfba..0a7939d35b4d1 100644 --- a/boards/renesas/ek_ra8m1/ek_ra8m1-pinctrl.dtsi +++ b/boards/renesas/ek_ra8m1/ek_ra8m1-pinctrl.dtsi @@ -147,7 +147,8 @@ usbhs_default: usbhs_default { group1 { - psels = ; /* VBUS */ + psels = , /* VBUS */ + ; /* VBUSEN */ drive-strength = "high"; }; }; @@ -172,10 +173,10 @@ usbfs_default: usbfs_default { group1 { - /* USBP USBN */ - psels = , - , - ; + psels = , /* USBN */ + , /* USBP */ + , /* VBUS */ + ; /* VBUSEN */ drive-strength = "high"; }; }; diff --git a/boards/renesas/ek_ra8m1/ek_ra8m1.dts b/boards/renesas/ek_ra8m1/ek_ra8m1.dts index 0112cae43269e..34aa6d892ab0f 100644 --- a/boards/renesas/ek_ra8m1/ek_ra8m1.dts +++ b/boards/renesas/ek_ra8m1/ek_ra8m1.dts @@ -445,6 +445,11 @@ pmod_sd_shield: &sdhc0 {}; pinctrl-0 = <&usbfs_default>; pinctrl-names = "default"; maximum-speed = "full-speed"; + status = "okay"; + + zephyr_uhc0: uhc { + status = "okay"; + }; }; &wdt { diff --git a/boards/renesas/mck_ra8t1/mck_ra8t1-pinctrl.dtsi b/boards/renesas/mck_ra8t1/mck_ra8t1-pinctrl.dtsi index 779356b7c2316..23fc8bfefe492 100644 --- a/boards/renesas/mck_ra8t1/mck_ra8t1-pinctrl.dtsi +++ b/boards/renesas/mck_ra8t1/mck_ra8t1-pinctrl.dtsi @@ -126,10 +126,10 @@ usbfs_default: usbfs_default { group1 { - /* USBP USBN */ - psels = , - , - ; + psels = , /* USBN */ + , /* USBP */ + , /* VBUS */ + ; /* VBUSEN */ drive-strength = "high"; }; }; diff --git a/drivers/usb/uhc/CMakeLists.txt b/drivers/usb/uhc/CMakeLists.txt index 69f1cea8433b2..2f062b075f343 100644 --- a/drivers/usb/uhc/CMakeLists.txt +++ b/drivers/usb/uhc/CMakeLists.txt @@ -11,3 +11,4 @@ zephyr_library_sources_ifdef(CONFIG_UHC_NXP_EHCI uhc_mcux_common.c uhc_mcux_ehci zephyr_library_sources_ifdef(CONFIG_UHC_NXP_KHCI uhc_mcux_common.c uhc_mcux_khci.c) zephyr_library_sources_ifdef(CONFIG_UHC_NXP_OHCI uhc_mcux_common.c uhc_mcux_ohci.c) zephyr_library_sources_ifdef(CONFIG_UHC_NXP_IP3516HS uhc_mcux_common.c uhc_mcux_ip3516hs.c) +zephyr_library_sources_ifdef(CONFIG_UHC_RENESAS_RA uhc_renesas_ra.c) diff --git a/drivers/usb/uhc/Kconfig b/drivers/usb/uhc/Kconfig index 052e9437f6ae2..6087f41e7c4f0 100644 --- a/drivers/usb/uhc/Kconfig +++ b/drivers/usb/uhc/Kconfig @@ -39,5 +39,6 @@ source "subsys/logging/Kconfig.template.log_config" source "drivers/usb/uhc/Kconfig.max3421e" source "drivers/usb/uhc/Kconfig.virtual" source "drivers/usb/uhc/Kconfig.mcux" +source "drivers/usb/uhc/Kconfig.renesas_ra" endif # UHC_DRIVER diff --git a/drivers/usb/uhc/Kconfig.renesas_ra b/drivers/usb/uhc/Kconfig.renesas_ra new file mode 100644 index 0000000000000..1897f5a2faa4a --- /dev/null +++ b/drivers/usb/uhc/Kconfig.renesas_ra @@ -0,0 +1,40 @@ +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +DT_COMPAT_UHC_RENESAS_RA := renesas,ra-uhc + +config UHC_RENESAS_RA + bool "Renesas RA family UHC driver" + default y + depends on DT_HAS_RENESAS_RA_UHC_ENABLED + select USE_RA_FSP_USB_HOST + help + Enable Renesas RA family UHC driver. + +if UHC_RENESAS_RA + +config UHC_RENESAS_RA_STACK_SIZE + int "UHC controller driver internal thread stack size" + default 512 + help + Renesas RA device controller driver internal thread stack size. + +config UHC_RENESAS_RA_THREAD_PRIORITY + int "Renesas RA family UHC driver thread priority" + default 8 + help + Renesas RA device controller driver thread priority. + +config UHC_RENESAS_RA_MAX_MSGQ + int "Renesas RA family internal UHC driver msgq size" + default 10 + help + Renesas RA device controller driver internal msgq size. + +config UHC_RENESAS_RA_OSC_WAIT_RETRIES + int "Maximum retries for oscillator ready event" + default 3 + help + Specify the number of retries for oscillator ready event. + +endif # UHC_RENESAS_RA diff --git a/drivers/usb/uhc/uhc_renesas_ra.c b/drivers/usb/uhc/uhc_renesas_ra.c new file mode 100644 index 0000000000000..06320a89b5340 --- /dev/null +++ b/drivers/usb/uhc/uhc_renesas_ra.c @@ -0,0 +1,744 @@ +/* + * Copyright (c) 2024 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT renesas_ra_uhc + +#include +#include +#include "uhc_common.h" +#include "r_usb_host.h" + +LOG_MODULE_REGISTER(uhc_renesas_ra, CONFIG_UHC_DRIVER_LOG_LEVEL); + +#define UHC_RENESAS_RA_BUS_STATE_RESUME 1 +#define UHC_RENESAS_RA_BUS_STATE_SOF_ENABLE 2 +#define UHC_RENESA_RA_MAX_UDEV 5 + +enum uhc_renesas_ra_event_type { + /* An event generated by the HAL driver */ + UHC_RENESAS_RA_EVT_HAL, + /* Shim driver event to trigger next transfer */ + UHC_RENESAS_RA_EVT_XFER, +}; + +struct uhc_renesas_ra_evt { + enum uhc_renesas_ra_event_type type; + struct st_usbh_event hal_evt; +}; + +struct uhc_renesas_ra_data { + const struct device *dev; + struct uhc_transfer *last_xfer; + struct k_thread thread_data; + struct st_usbh_instance_ctrl uhc_ctrl; + struct st_usb_cfg uhc_cfg; + uint16_t xfer_len; + atomic_t bus_state; + atomic_t ep; +}; + +struct uhc_renesas_ra_config { + const struct pinctrl_dev_config *pcfg; + void (*make_thread)(const struct device *dev); +}; + +K_MSGQ_DEFINE(uhc_msgq, sizeof(struct uhc_renesas_ra_evt), CONFIG_UHC_RENESAS_RA_MAX_MSGQ, + sizeof(uint32_t)); + +extern void r_usbh_isr(void); + +static void uhc_renesas_ra_interrupt_handler(void *arg) +{ + ARG_UNUSED(arg); + r_usbh_isr(); +} + +static void uhc_renesas_ra_xfer_request(void) +{ + struct uhc_renesas_ra_evt event = {.type = UHC_RENESAS_RA_EVT_XFER}; + __maybe_unused int ret; + + ret = k_msgq_put(&uhc_msgq, &event, K_NO_WAIT); + __ASSERT_NO_MSG(ret == 0); +} + +static void uhc_renesas_ra_callback(usbh_callback_arg_t *p_args) +{ + struct uhc_renesas_ra_evt event = { + .type = UHC_RENESAS_RA_EVT_HAL, + .hal_evt = p_args->event, + }; + __maybe_unused int ret; + + ret = k_msgq_put(&uhc_msgq, &event, K_NO_WAIT); + __ASSERT_NO_MSG(ret == 0); +} + +static int uhc_renesas_ra_lock(const struct device *dev) +{ + struct uhc_data *data = dev->data; + + return k_mutex_lock(&data->mutex, K_FOREVER); +} + +static int uhc_renesas_ra_unlock(const struct device *dev) +{ + struct uhc_data *data = dev->data; + + return k_mutex_unlock(&data->mutex); +} + +static int uhc_renesas_ra_control_status_xfer(const struct device *dev, + struct uhc_transfer *const xfer) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + uint8_t inv_ep = (xfer->ep ^ USB_EP_DIR_MASK); + int err; + + err = R_USBH_XferStart(&priv->uhc_ctrl, xfer->udev->addr, inv_ep, NULL, 0); + if (err != FSP_SUCCESS) { + return -EIO; + } + + return 0; +} + +static int uhc_renesas_ra_data_send(const struct device *dev, struct uhc_transfer *const xfer) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + struct net_buf *buf = xfer->buf; + int err; + + err = R_USBH_XferStart(&priv->uhc_ctrl, xfer->udev->addr, xfer->ep, buf->data, buf->len); + if (err != FSP_SUCCESS) { + LOG_ERR("ep 0x%02x state data error", xfer->ep); + return -EIO; + } + + return 0; +} + +static int uhc_renesas_ra_data_receive(const struct device *dev, struct uhc_transfer *const xfer) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + struct net_buf *buf = xfer->buf; + size_t len = net_buf_tailroom(buf); + void *buffer_tail = net_buf_add(buf, len); + int err; + + err = R_USBH_XferStart(&priv->uhc_ctrl, xfer->udev->addr, xfer->ep, buffer_tail, len); + if (err != FSP_SUCCESS) { + net_buf_remove_mem(buf, len); + LOG_ERR("ep 0x%02x state status error", xfer->ep); + return -EIO; + } + + return 0; +} + +static int uhc_renesas_ra_data_xfer(const struct device *dev, struct uhc_transfer *const xfer) +{ + if (USB_EP_DIR_IS_IN(xfer->ep)) { + return uhc_renesas_ra_data_receive(dev, xfer); + } else { + return uhc_renesas_ra_data_send(dev, xfer); + } +} + +static int uhc_renesas_ra_edpt_open(const struct device *dev, uint8_t dev_addr, uint8_t attrib, + uint8_t ep, uint8_t mps) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + usb_desc_endpoint_t ep_desc = { + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = ep, + .bmAttributes = {attrib}, + .wMaxPacketSize = mps, + .bInterval = 0, + }; + fsp_err_t err; + int ret; + + if (dev_addr <= 0 || dev_addr > UHC_RENESA_RA_MAX_UDEV) { + LOG_ERR("Invalid device address"); + return -EINVAL; + } + + if (USB_EP_GET_IDX(ep) == 0) { + LOG_ERR("The default control pipe should be opened by a port open"); + return -EINVAL; + } + + err = R_USBH_EdptOpen(&priv->uhc_ctrl, dev_addr, &ep_desc); + if (err == FSP_SUCCESS) { + LOG_INF("ep 0x%02x has been opened", ep); + ret = 0; + } else if (err == FSP_ERR_USB_BUSY) { + LOG_ERR("No available pipe to configure for this EP"); + ret = -EBUSY; + } else { + LOG_ERR("Open ep 0x%02x failed", ep); + ret = -EIO; + } + + return ret; +} + +static int uhc_renesas_ra_control_xfer(const struct device *dev, struct uhc_transfer *const xfer) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + int ret = 0; + + switch (xfer->stage) { + case UHC_CONTROL_STAGE_SETUP: + if (R_USBH_SetupSend(&priv->uhc_ctrl, xfer->udev->addr, xfer->setup_pkt) != + FSP_SUCCESS) { + LOG_ERR("ep 0x%02x state setup error", xfer->ep); + ret = -EIO; + } + break; + + case UHC_CONTROL_STAGE_DATA: + ret = uhc_renesas_ra_data_xfer(dev, xfer); + break; + + case UHC_CONTROL_STAGE_STATUS: + ret = uhc_renesas_ra_control_status_xfer(dev, xfer); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int uhc_renesas_ra_schedule_xfer(const struct device *dev) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + int ret; + + if (priv->last_xfer == NULL) { + priv->last_xfer = uhc_xfer_get_next(dev); + if (priv->last_xfer == NULL) { + LOG_DBG("Nothing to transfer"); + return 0; + } + + if (R_USBH_PortOpen(&priv->uhc_ctrl, priv->last_xfer->udev->addr) != FSP_SUCCESS) { + return -EIO; + } + } + + if (USB_EP_GET_IDX(priv->last_xfer->ep) == 0) { + return uhc_renesas_ra_control_xfer(dev, priv->last_xfer); + } + + if (!atomic_test_bit(&priv->ep, priv->last_xfer->ep)) { + ret = uhc_renesas_ra_edpt_open(dev, priv->last_xfer->udev->addr, + priv->last_xfer->udev->actual_cfg, + priv->last_xfer->ep, priv->last_xfer->mps); + if (ret != 0) { + return ret; + } + + atomic_set_bit(&priv->ep, priv->last_xfer->ep); + } + + return uhc_renesas_ra_data_xfer(dev, priv->last_xfer); +} + +static void priv_ongoing_xfer_end(struct uhc_renesas_ra_data *const priv, const int err) +{ + if (priv->last_xfer == NULL) { + return; + } + + uhc_xfer_return(priv->dev, priv->last_xfer, err); + + priv->last_xfer = NULL; +} + +static void priv_ongoing_xfer_control_stage_update(struct uhc_renesas_ra_data *const priv, + uint16_t xfer_len) +{ + switch (priv->last_xfer->stage) { + case UHC_CONTROL_STAGE_SETUP: + if (priv->last_xfer->buf != NULL) { + /* Next state is data stage */ + priv->last_xfer->stage = UHC_CONTROL_STAGE_DATA; + } else { + /* There is no data so jump directly to the status stage */ + priv->last_xfer->stage = UHC_CONTROL_STAGE_STATUS; + } + uhc_renesas_ra_xfer_request(); + break; + + case UHC_CONTROL_STAGE_DATA: + if (USB_EP_DIR_IS_IN(priv->last_xfer->ep)) { + priv->xfer_len += xfer_len; + if (priv->xfer_len < priv->last_xfer->buf->size) { + /* Continue receiving data */ + priv->last_xfer->buf->len = priv->xfer_len; + } else { + /* No more data left to receive, go to the status stage */ + priv->last_xfer->stage = UHC_CONTROL_STAGE_STATUS; + } + } else { + /* OUT direction */ + if ((priv->last_xfer->buf == NULL) || (priv->last_xfer->buf->len == 0)) { + /* No more data left to send, go to the status stage */ + priv->last_xfer->stage = UHC_CONTROL_STAGE_STATUS; + priv_ongoing_xfer_end(priv, 0); + } + } + uhc_renesas_ra_xfer_request(); + break; + + case UHC_CONTROL_STAGE_STATUS: + /* Transfer is completed */ + priv_ongoing_xfer_end(priv, 0); + + default: + break; + } +} + +static int uhc_renesas_ra_event_xfer_complete(const struct device *dev, usbh_event_t *hal_evt) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + struct uhc_transfer *const xfer = priv->last_xfer; + int ret; + + switch (hal_evt->complete.result) { + case USB_XFER_RESULT_STALLED: + ret = -EBUSY; + break; + + case USB_XFER_RESULT_TIMEOUT: + ret = -EAGAIN; + break; + + case USB_XFER_RESULT_FAILED: + ret = -EIO; + break; + + case USB_XFER_RESULT_SUCCESS: + if (USB_EP_GET_IDX(hal_evt->complete.ep_addr) == 0) { + priv_ongoing_xfer_control_stage_update(priv, hal_evt->complete.len); + } else { + net_buf_pull(xfer->buf, hal_evt->complete.len); + if (USB_EP_DIR_IS_IN(hal_evt->complete.ep_addr)) { + if (net_buf_tailroom(xfer->buf) == 0) { + uhc_xfer_return(dev, priv->last_xfer, 0); + priv->last_xfer = NULL; + } + } else { + /* OUT */ + if (xfer->buf->len == 0) { + uhc_xfer_return(dev, priv->last_xfer, 0); + priv->last_xfer = NULL; + } + } + } + ret = 0; + break; + + default: + /* USB_XFER_RESULT_INVALID */ + ret = -EINVAL; + break; + } + + return ret; +} + +static int uhc_renesas_ra_ep_enqueue(const struct device *dev, struct uhc_transfer *const xfer) +{ + int ret; + + ret = uhc_xfer_append(dev, xfer); + if (ret != 0) { + return ret; + } + + uhc_renesas_ra_xfer_request(); + + return 0; +} + +static int uhc_renesas_ra_ep_dequeue(const struct device *dev, struct uhc_transfer *const xfer) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + + if (priv->last_xfer != xfer) { + sys_dlist_remove(&xfer->node); + uhc_xfer_free(dev, xfer); + } + + return 0; +} + +static int uhc_renesas_ra_device_attach(const struct device *dev, usbh_event_t *event) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + usb_speed_t speed = USB_SPEED_INVALID; + + if (R_USBH_PortReset(&priv->uhc_ctrl) != FSP_SUCCESS) { + return -EIO; + } + + /* Wait for a CHIRP signal */ + for (unsigned int i = 0; i < CONFIG_UHC_RENESAS_RA_OSC_WAIT_RETRIES; i++) { + if (R_USBH_GetDeviceSpeed(&priv->uhc_ctrl, &speed) != FSP_SUCCESS) { + return -EIO; + } + + if (speed != USB_SPEED_INVALID) { + break; + } + + k_msleep(3); + } + + switch (speed) { + case USB_SPEED_LS: + uhc_submit_event(dev, UHC_EVT_DEV_CONNECTED_LS, 0); + break; + + case USB_SPEED_FS: + uhc_submit_event(dev, UHC_EVT_DEV_CONNECTED_FS, 0); + break; + + case USB_SPEED_HS: + uhc_submit_event(dev, UHC_EVT_DEV_CONNECTED_HS, 0); + break; + + default: + return -EINVAL; + } + + LOG_INF("Device attached: speed type %d", speed); + + priv->xfer_len = 0; + + return 0; +} + +static int uhc_renesas_ra_port_release(const struct device *dev) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + + if (R_USBH_DeviceRelease(&priv->uhc_ctrl, 0x01) == FSP_SUCCESS) { + return -EIO; + } + + uhc_submit_event(dev, UHC_EVT_DEV_REMOVED, 0); + + return 0; +} + +static void uhc_renesas_ra_thread(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + const struct device *dev = p1; + + LOG_DBG("UHC_RENESAS_RA thread started"); + + while (true) { + struct uhc_renesas_ra_evt event; + int ret; + + k_msgq_get(&uhc_msgq, &event, K_FOREVER); + + if (event.type == UHC_RENESAS_RA_EVT_XFER) { + ret = uhc_renesas_ra_schedule_xfer(dev); + if (unlikely(ret)) { + uhc_submit_event(dev, UHC_EVT_ERROR, ret); + } + } else { + /* UHC_RENESAS_RA_EVT_HAL */ + usbh_event_t *hal_evt = &event.hal_evt; + + switch (hal_evt->event_id) { + case USBH_EVENT_XFER_COMPLETE: + ret = uhc_renesas_ra_event_xfer_complete(dev, hal_evt); + if (unlikely(ret)) { + uhc_submit_event(dev, UHC_EVT_EP_REQUEST, ret); + } + break; + + case USBH_EVENT_DEVICE_ATTACH: + ret = uhc_renesas_ra_device_attach(dev, hal_evt); + if (unlikely(ret)) { + uhc_submit_event(dev, UHC_EVT_ERROR, ret); + } + break; + + case USBH_EVENT_DEVICE_REMOVE: + ret = uhc_renesas_ra_port_release(dev); + if (unlikely(ret)) { + uhc_submit_event(dev, UHC_EVT_DEV_REMOVED, ret); + } + break; + + default: + break; + } + } + } +} + +/* Enable SOF generator */ +static int uhc_renesas_ra_sof_enable(const struct device *dev) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + + if (atomic_test_bit(&priv->bus_state, UHC_RENESAS_RA_BUS_STATE_SOF_ENABLE)) { + return -EALREADY; + } + + if (R_USBH_SOFEnable(&priv->uhc_ctrl) != FSP_SUCCESS) { + return -EIO; + } + + atomic_set_bit(&priv->bus_state, UHC_RENESAS_RA_BUS_STATE_SOF_ENABLE); + + return 0; +} + +/* Disable SOF generator and suspend bus */ +static int uhc_renesas_ra_bus_suspend(const struct device *dev) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + + if (!atomic_test_bit(&priv->bus_state, UHC_RENESAS_RA_BUS_STATE_RESUME)) { + return -EALREADY; + } + + if (R_USBH_BusSuspend(&priv->uhc_ctrl) != FSP_SUCCESS) { + return -EIO; + } + + atomic_clear_bit(&priv->bus_state, UHC_RENESAS_RA_BUS_STATE_RESUME); + uhc_submit_event(dev, UHC_EVT_SUSPENDED, 0); + + return 0; +} + +static int uhc_renesas_ra_bus_reset(const struct device *dev) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + + if (R_USBH_PortReset(&priv->uhc_ctrl) != FSP_SUCCESS) { + return -EIO; + } + + uhc_submit_event(dev, UHC_EVT_RESETED, 0); + atomic_clear(&priv->bus_state); + + return 0; +} + +static int uhc_renesas_ra_bus_resume(const struct device *dev) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + + if (atomic_test_bit(&priv->bus_state, UHC_RENESAS_RA_BUS_STATE_RESUME)) { + return -EALREADY; + } + + if (R_USBH_BusResume(&priv->uhc_ctrl) != FSP_SUCCESS) { + return -EIO; + } + + atomic_set_bit(&priv->bus_state, UHC_RENESAS_RA_BUS_STATE_RESUME); + uhc_submit_event(dev, UHC_EVT_RESUMED, 0); + + return 0; +} + +static int uhc_renesas_ra_init(const struct device *dev) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + + if (R_USBH_Open(&priv->uhc_ctrl, &priv->uhc_cfg) != FSP_SUCCESS) { + return -EIO; + } + + LOG_INF("Initialized"); + + atomic_clear(&priv->bus_state); + atomic_clear(&priv->ep); + +#if DT_HAS_COMPAT_STATUS_OKAY(renesas_ra_usbhs) + if (priv->uhc_cfg.hs_irq != FSP_INVALID_VECTOR) { + irq_enable(priv->uhc_cfg.hs_irq); + } +#endif + + if (priv->uhc_cfg.irq != FSP_INVALID_VECTOR) { + irq_enable(priv->uhc_cfg.irq); + } + + if (priv->uhc_cfg.irq_r != FSP_INVALID_VECTOR) { + irq_enable(priv->uhc_cfg.irq_r); + } + + return 0; +} + +static int uhc_renesas_ra_enable(const struct device *dev) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + + if (R_USBH_Enable(&priv->uhc_ctrl) != FSP_SUCCESS) { + return -EIO; + } + + return 0; +} + +static int uhc_renesas_ra_disable(const struct device *dev) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + + if (R_USBH_Disable(&priv->uhc_ctrl) != FSP_SUCCESS) { + return -EIO; + } + + return 0; +} + +static int uhc_renesas_ra_shutdown(const struct device *dev) +{ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + + if (priv->last_xfer != NULL) { + uhc_xfer_free(dev, priv->last_xfer); + priv->last_xfer = NULL; + } + + if (R_USBH_Close(&priv->uhc_ctrl) != FSP_SUCCESS) { + return -EIO; + } + + return 0; +} + +static const struct uhc_api uhc_renesas_ra_api = { + .lock = uhc_renesas_ra_lock, + .unlock = uhc_renesas_ra_unlock, + .init = uhc_renesas_ra_init, + .enable = uhc_renesas_ra_enable, + .disable = uhc_renesas_ra_disable, + .shutdown = uhc_renesas_ra_shutdown, + .bus_reset = uhc_renesas_ra_bus_reset, + .sof_enable = uhc_renesas_ra_sof_enable, + .bus_suspend = uhc_renesas_ra_bus_suspend, + .bus_resume = uhc_renesas_ra_bus_resume, + .ep_enqueue = uhc_renesas_ra_ep_enqueue, + .ep_dequeue = uhc_renesas_ra_ep_dequeue, +}; + +static int uhc_ra_driver_preinit(const struct device *dev) +{ + const struct uhc_renesas_ra_config *config = dev->config; + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); + int ret; + + ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_ERR("USB pinctrl setup failed (%d)", ret); + } + +#if DT_HAS_COMPAT_STATUS_OKAY(renesas_ra_usbhs) + if (priv->uhc_cfg.hs_irq != FSP_INVALID_VECTOR) { + R_ICU->IELSR[priv->uhc_cfg.hs_irq] = BSP_PRV_IELS_ENUM(EVENT_USBHS_USB_INT_RESUME); + } +#endif + + if (priv->uhc_cfg.irq != FSP_INVALID_VECTOR) { + R_ICU->IELSR[priv->uhc_cfg.irq] = BSP_PRV_IELS_ENUM(EVENT_USBFS_INT); + } + + if (priv->uhc_cfg.irq_r != FSP_INVALID_VECTOR) { + R_ICU->IELSR[priv->uhc_cfg.irq_r] = BSP_PRV_IELS_ENUM(EVENT_USBFS_RESUME); + } + + config->make_thread(dev); + + return ret; +} + +#define IS_USB_HIGH_SPEED(n) DT_NODE_HAS_COMPAT(DT_INST_PARENT(n), renesas_ra_usbhs) + +#define USB_MODULE_NUMBER(n) ((DT_REG_ADDR(n)) == R_USB_FS0_BASE ? 0 : 1) + +#define RENESAS_RA_USB_IRQ_CONNECT(idx, n) \ + IRQ_CONNECT(DT_IRQ_BY_IDX(DT_INST_PARENT(n), idx, irq), \ + DT_IRQ_BY_IDX(DT_INST_PARENT(n), idx, priority), \ + uhc_renesas_ra_interrupt_handler, DEVICE_DT_INST_GET(n), 0) + +#define RENESAS_RA_USB_IRQ_GET(id, name, cell) \ + COND_CODE_1(DT_IRQ_HAS_NAME(id, name), (DT_IRQ_BY_NAME(id, name, cell)), \ + ((IRQn_Type) FSP_INVALID_VECTOR)) + +#define UHC_RENESAS_RA_DEVICE_DEFINE(n) \ + PINCTRL_DT_DEFINE(DT_INST_PARENT(n)); \ + K_THREAD_STACK_DEFINE(uhc_renesas_ra_stack_##n, CONFIG_UHC_RENESAS_RA_STACK_SIZE); \ + \ + static void uhc_renesas_ra_create_thread_##n(const struct device *dev) \ + { \ + struct uhc_renesas_ra_data *priv = uhc_get_private(dev); \ + \ + k_thread_create(&priv->thread_data, uhc_renesas_ra_stack_##n, \ + K_THREAD_STACK_SIZEOF(uhc_renesas_ra_stack_##n), \ + uhc_renesas_ra_thread, (void *)dev, NULL, NULL, \ + K_PRIO_COOP(CONFIG_UHC_RENESAS_RA_THREAD_PRIORITY), K_ESSENTIAL, \ + K_NO_WAIT); \ + k_thread_name_set(&priv->thread_data, dev->name); \ + } \ + \ + static const struct uhc_renesas_ra_config uhc_config_##n = { \ + .pcfg = PINCTRL_DT_DEV_CONFIG_GET(DT_INST_PARENT(n)), \ + .make_thread = uhc_renesas_ra_create_thread_##n, \ + }; \ + \ + static struct uhc_renesas_ra_data uhc_priv_data_##n = { \ + .dev = DEVICE_DT_INST_GET(n), \ + .uhc_cfg = { \ + .irq = RENESAS_RA_USB_IRQ_GET(DT_INST_PARENT(n), usbfs_i, irq), \ + .irq_r = RENESAS_RA_USB_IRQ_GET(DT_INST_PARENT(n), usbfs_r, irq), \ + .hs_irq = RENESAS_RA_USB_IRQ_GET(DT_INST_PARENT(n), usbhs_ir, irq), \ + .ipl = RENESAS_RA_USB_IRQ_GET(DT_INST_PARENT(n), usbfs_i, priority), \ + .ipl_r = RENESAS_RA_USB_IRQ_GET(DT_INST_PARENT(n), usbfs_r, priority), \ + .hsipl = RENESAS_RA_USB_IRQ_GET(DT_INST_PARENT(n), usbhs_ir, priority), \ + .module_number = USB_MODULE_NUMBER(DT_INST_PARENT(n)), \ + .high_speed = IS_USB_HIGH_SPEED(DT_INST_PARENT(n)), \ + .p_callback = uhc_renesas_ra_callback, \ + }, \ + }; \ + \ + static struct uhc_data uhc_data_##n = { \ + .mutex = Z_MUTEX_INITIALIZER(uhc_data_##n.mutex), \ + .priv = &uhc_priv_data_##n, \ + }; \ + \ + static int uhc_ra_driver_init_##n(const struct device *dev) \ + { \ + LISTIFY(DT_NUM_IRQS(DT_INST_PARENT(n)), RENESAS_RA_USB_IRQ_CONNECT, (;), n); \ + return uhc_ra_driver_preinit(dev); \ + } \ + \ + DEVICE_DT_INST_DEFINE(n, uhc_ra_driver_init_##n, NULL, &uhc_data_##n, &uhc_config_##n, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ + &uhc_renesas_ra_api); + +DT_INST_FOREACH_STATUS_OKAY(UHC_RENESAS_RA_DEVICE_DEFINE) diff --git a/dts/arm/renesas/ra/ra4/ra4-cm33-common.dtsi b/dts/arm/renesas/ra/ra4/ra4-cm33-common.dtsi index d3bdde34f3be4..e6cf4057c09b2 100644 --- a/dts/arm/renesas/ra/ra4/ra4-cm33-common.dtsi +++ b/dts/arm/renesas/ra/ra4/ra4-cm33-common.dtsi @@ -375,6 +375,11 @@ compatible = "renesas,ra-udc"; status = "disabled"; }; + + uhc { + compatible = "renesas,ra-uhc"; + status = "disabled"; + }; }; port_irq0: external-interrupt@40006000 { diff --git a/dts/arm/renesas/ra/ra4/ra4-cm4-common.dtsi b/dts/arm/renesas/ra/ra4/ra4-cm4-common.dtsi index a59b558fabb6c..7479c16d31508 100644 --- a/dts/arm/renesas/ra/ra4/ra4-cm4-common.dtsi +++ b/dts/arm/renesas/ra/ra4/ra4-cm4-common.dtsi @@ -316,6 +316,11 @@ compatible = "renesas,ra-udc"; status = "disabled"; }; + + uhc { + compatible = "renesas,ra-uhc"; + status = "disabled"; + }; }; port_irq0: external-interrupt@40006000 { diff --git a/dts/arm/renesas/ra/ra6/r7fa6m3ax.dtsi b/dts/arm/renesas/ra/ra6/r7fa6m3ax.dtsi index 30df0b53af9f4..9ceb01b7a94e8 100644 --- a/dts/arm/renesas/ra/ra6/r7fa6m3ax.dtsi +++ b/dts/arm/renesas/ra/ra6/r7fa6m3ax.dtsi @@ -146,6 +146,11 @@ compatible = "renesas,ra-udc"; status = "disabled"; }; + + uhc { + compatible = "renesas,ra-uhc"; + status = "disabled"; + }; }; adc@4005c000 { diff --git a/dts/arm/renesas/ra/ra6/r7fa6m5xh.dtsi b/dts/arm/renesas/ra/ra6/r7fa6m5xh.dtsi index 899f4b3d0dacc..db2880eb1af5c 100644 --- a/dts/arm/renesas/ra/ra6/r7fa6m5xh.dtsi +++ b/dts/arm/renesas/ra/ra6/r7fa6m5xh.dtsi @@ -281,6 +281,11 @@ compatible = "renesas,ra-udc"; status = "disabled"; }; + + uhc { + compatible = "renesas,ra-uhc"; + status = "disabled"; + }; }; adc@40170000 { diff --git a/dts/arm/renesas/ra/ra6/ra6-cm33-common.dtsi b/dts/arm/renesas/ra/ra6/ra6-cm33-common.dtsi index 5fdf797779dc1..6bf572f2529b9 100644 --- a/dts/arm/renesas/ra/ra6/ra6-cm33-common.dtsi +++ b/dts/arm/renesas/ra/ra6/ra6-cm33-common.dtsi @@ -358,6 +358,11 @@ compatible = "renesas,ra-udc"; status = "disabled"; }; + + uhc { + compatible = "renesas,ra-uhc"; + status = "disabled"; + }; }; flash: flash-controller@407e0000 { diff --git a/dts/arm/renesas/ra/ra6/ra6-cm4-common.dtsi b/dts/arm/renesas/ra/ra6/ra6-cm4-common.dtsi index af760df763de8..c342023518420 100644 --- a/dts/arm/renesas/ra/ra6/ra6-cm4-common.dtsi +++ b/dts/arm/renesas/ra/ra6/ra6-cm4-common.dtsi @@ -421,6 +421,11 @@ compatible = "renesas,ra-udc"; status = "disabled"; }; + + uhc { + compatible = "renesas,ra-uhc"; + status = "disabled"; + }; }; port_irq0: external-interrupt@40006000 { diff --git a/dts/arm/renesas/ra/ra8/r7fa8d1xh.dtsi b/dts/arm/renesas/ra/ra8/r7fa8d1xh.dtsi index 98bc2cc321f95..4eb3cbf6fdfb8 100644 --- a/dts/arm/renesas/ra/ra8/r7fa8d1xh.dtsi +++ b/dts/arm/renesas/ra/ra8/r7fa8d1xh.dtsi @@ -296,6 +296,11 @@ compatible = "renesas,ra-udc"; status = "disabled"; }; + + uhc { + compatible = "renesas,ra-uhc"; + status = "disabled"; + }; }; ceu: ceu@40348000 { diff --git a/dts/arm/renesas/ra/ra8/r7fa8m1xh.dtsi b/dts/arm/renesas/ra/ra8/r7fa8m1xh.dtsi index f5c3a448aed26..b2b43160c6d56 100644 --- a/dts/arm/renesas/ra/ra8/r7fa8m1xh.dtsi +++ b/dts/arm/renesas/ra/ra8/r7fa8m1xh.dtsi @@ -260,6 +260,11 @@ compatible = "renesas,ra-udc"; status = "disabled"; }; + + uhc { + compatible = "renesas,ra-uhc"; + status = "disabled"; + }; }; ceu: ceu@40348000 { diff --git a/dts/arm/renesas/ra/ra8/ra8x1.dtsi b/dts/arm/renesas/ra/ra8/ra8x1.dtsi index 6881ff0c2ff0d..93d66bb7b13a5 100644 --- a/dts/arm/renesas/ra/ra8/ra8x1.dtsi +++ b/dts/arm/renesas/ra/ra8/ra8x1.dtsi @@ -931,6 +931,11 @@ compatible = "renesas,ra-udc"; status = "disabled"; }; + + uhc { + compatible = "renesas,ra-uhc"; + status = "disabled"; + }; }; acmphs_global: acmphs_global@40236000 { diff --git a/dts/bindings/usb/renesas/renesas,ra-uhc.yaml b/dts/bindings/usb/renesas/renesas,ra-uhc.yaml new file mode 100644 index 0000000000000..e98a3545f8694 --- /dev/null +++ b/dts/bindings/usb/renesas/renesas,ra-uhc.yaml @@ -0,0 +1,6 @@ +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas RA USB host controller + +compatible: "renesas,ra-uhc" diff --git a/modules/Kconfig.renesas b/modules/Kconfig.renesas index b6f4fdf600ea1..283529fe25db3 100644 --- a/modules/Kconfig.renesas +++ b/modules/Kconfig.renesas @@ -174,6 +174,11 @@ config USE_RA_FSP_USB_DEVICE help Enable RA FSP USB Device Controller driver +config USE_RA_FSP_USB_HOST + bool + help + Enable RA FSP USB Host Controller driver + config USE_RA_FSP_SDRAM bool help diff --git a/samples/subsys/usb/shell/sample.yaml b/samples/subsys/usb/shell/sample.yaml index a0a03a8c4c51b..0af6fe1094e17 100644 --- a/samples/subsys/usb/shell/sample.yaml +++ b/samples/subsys/usb/shell/sample.yaml @@ -14,6 +14,14 @@ tests: harness: keyboard tags: usb sample.usbh.shell: + tags: + - usb + platform_allow: + - ek_ra8m1 + extra_args: + - CONF_FILE="device_and_host_prj.conf" + build_only: true + sample.usbh.shell.shield: tags: - usb - shield