This commit is contained in:
sc committed 2026-02-14 19:03:02 +08:00
commit da43152a7e
3380 files changed
+1128754

No files matched your search

+336
View File
@@ -0,0 +1,336 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-11-07 RealThread the first version
*/
#include "drv_otghs_dev.h"
#include <rtthread.h>
#include <rtdevice.h>
#include "board.h"
#include "drv_usb_hw.h"
#if defined(BSP_USING_USB) && defined(RT_USING_USB_DEVICE)
usb_core_driver gd_usb_core;
static struct udcd _gd_udc;
#ifdef USE_USB_FS
static struct ep_id _ep_pool[] = {
{ 0x0, USB_EP_ATTR_CONTROL, USB_DIR_INOUT, 64, ID_ASSIGNED },
{ 0x1, USB_EP_ATTR_BULK, USB_DIR_IN, 64, ID_UNASSIGNED },
{ 0x1, USB_EP_ATTR_BULK, USB_DIR_OUT, 64, ID_UNASSIGNED },
{ 0x2, USB_EP_ATTR_INT, USB_DIR_IN, 64, ID_UNASSIGNED },
{ 0x2, USB_EP_ATTR_INT, USB_DIR_OUT, 64, ID_UNASSIGNED },
{ 0x3, USB_EP_ATTR_BULK, USB_DIR_IN, 64, ID_UNASSIGNED },
{ 0x3, USB_EP_ATTR_BULK, USB_DIR_OUT, 64, ID_UNASSIGNED },
{ 0xFF, USB_EP_ATTR_TYPE_MASK, USB_DIR_MASK, 0, ID_ASSIGNED },
};
#elif defined USE_USB_HS
static struct ep_id _ep_pool[] = {
{ 0x0, USB_EP_ATTR_CONTROL, USB_DIR_INOUT, 64, ID_ASSIGNED },
{ 0x1, USB_EP_ATTR_BULK, USB_DIR_IN, 64, ID_UNASSIGNED },
{ 0x1, USB_EP_ATTR_BULK, USB_DIR_OUT, 512, ID_UNASSIGNED },
{ 0x2, USB_EP_ATTR_INT, USB_DIR_IN, 512, ID_UNASSIGNED },
{ 0x2, USB_EP_ATTR_INT, USB_DIR_OUT, 512, ID_UNASSIGNED },
{ 0x3, USB_EP_ATTR_BULK, USB_DIR_IN, 512, ID_UNASSIGNED },
{ 0x3, USB_EP_ATTR_BULK, USB_DIR_OUT, 512, ID_UNASSIGNED },
{ 0xFF, USB_EP_ATTR_TYPE_MASK, USB_DIR_MASK, 0, ID_ASSIGNED },
};
#endif
void usbd_reset_callback(void)
{
rt_usbd_reset_handler(&_gd_udc);
}
void usbd_setup_stage_callback(struct urequest *setup_packet)
{
rt_usbd_ep0_setup_handler(&_gd_udc, setup_packet);
}
void usbd_data_in_stage_callback(uint8_t epnum, uint16_t data_count)
{
if (epnum == 0)
{
rt_usbd_ep0_in_handler(&_gd_udc);
}
else
{
rt_usbd_ep_in_handler(&_gd_udc, 0x80 | epnum, data_count);
}
}
void usbd_connect_callback(void)
{
rt_usbd_connect_handler(&_gd_udc);
}
void usbd_sof_callback(void)
{
rt_usbd_sof_handler(&_gd_udc);
}
void usbd_disconnect_callback(void)
{
rt_usbd_disconnect_handler(&_gd_udc);
}
void usbd_data_out_stage_callback(uint8_t epnum, uint16_t data_count)
{
if (epnum == 0)
{
rt_usbd_ep0_out_handler(&_gd_udc, data_count);
}
else
{
rt_usbd_ep_out_handler(&_gd_udc, epnum, data_count);
}
}
void usbd_set_connection_state(uint8_t state)
{
if (state == 1)
{
usb_dev_connect(&gd_usb_core);
usb_mdelay(3);
}
else
{
usb_dev_disconnect(&gd_usb_core);
usb_mdelay(3);
}
}
static rt_err_t _ep_set_stall(rt_uint8_t address)
{
usb_ep_stall(&gd_usb_core, address);
return RT_EOK;
}
static rt_err_t _ep_clear_stall(rt_uint8_t address)
{
usb_ep_clrstall(&gd_usb_core, address);
return RT_EOK;
}
static rt_err_t _set_address(rt_uint8_t address)
{
usb_devaddr_set(&gd_usb_core, address);
return RT_EOK;
}
static rt_err_t _set_config(rt_uint8_t address)
{
return RT_EOK;
}
static rt_err_t _ep_enable(uep_t ep)
{
RT_ASSERT(ep != RT_NULL);
RT_ASSERT(ep->ep_desc != RT_NULL);
usb_ep_active(&gd_usb_core,
ep->ep_desc->bEndpointAddress,
ep->ep_desc->wMaxPacketSize,
ep->ep_desc->bmAttributes);
return RT_EOK;
}
static rt_err_t _ep_disable(uep_t ep)
{
RT_ASSERT(ep != RT_NULL);
RT_ASSERT(ep->ep_desc != RT_NULL);
usb_ep_deactivate(&gd_usb_core, ep->ep_desc->bEndpointAddress);
return RT_EOK;
}
static rt_ssize_t _ep_read(rt_uint8_t address, void *buffer)
{
rt_size_t size = 0;
RT_ASSERT(buffer != RT_NULL);
return size;
}
static rt_ssize_t _ep_read_prepare(rt_uint8_t address, void *buffer, rt_size_t size)
{
usb_ep_outxfer(&gd_usb_core, address, buffer, size);
return size;
}
static rt_ssize_t _ep_write(rt_uint8_t address, void *buffer, rt_size_t size)
{
usb_ep_inxfer(&gd_usb_core, address, buffer, size);
return size;
}
static rt_err_t _ep0_send_status(void)
{
usb_ep_inxfer(&gd_usb_core, 0, NULL, 0);
usb_ctlep_startout(&gd_usb_core);
return RT_EOK;
}
static rt_err_t _suspend(void)
{
return RT_EOK;
}
static rt_err_t _wakeup(void)
{
return RT_EOK;
}
static rt_err_t _init(rt_device_t device)
{
usb_rcu_config();
usb_timer_init();
#ifdef USE_USBHS0
#ifdef USE_USB_FS
usb_para_init(&gd_usb_core, USBHS0, USB_SPEED_FULL);
#endif
#ifdef USE_USB_HS
usb_para_init(&gd_usb_core, USBHS0, USB_SPEED_HIGH);
#endif
#endif /* USE_USBHS0 */
#ifdef USE_USBHS1
#ifdef USE_USB_FS
usb_para_init(&gd_usb_core, USBHS1, USB_SPEED_FULL);
#endif
#ifdef USE_USB_HS
usb_para_init(&gd_usb_core, USBHS1, USB_SPEED_HIGH);
#endif
#endif /* USE_USBHS1 */
/* configure USB capabilites */
usb_basic_init(&gd_usb_core.bp, &gd_usb_core.regs);
usb_globalint_disable(&gd_usb_core.regs);
/* initailizes the USB core*/
usb_core_init(gd_usb_core.bp, &gd_usb_core.regs);
/* set device disconnect */
usbd_set_connection_state(0);
usb_curmode_set(&gd_usb_core.regs, DEVICE_MODE);
/* initailizes device mode */
usb_devcore_init(&gd_usb_core);
usb_globalint_enable(&gd_usb_core.regs);
/* set device connect */
usbd_set_connection_state(1);
#ifdef USE_USB_HS
#ifdef USE_USBHS0
pllusb_rcu_config(USBHS0);
#endif
#ifdef USE_USBHS1
pllusb_rcu_config(USBHS1);
#endif
#endif /* USE_USB_HS */
usb_intr_config();
return RT_EOK;
}
const static struct udcd_ops _udc_ops = {
_set_address,
_set_config,
_ep_set_stall,
_ep_clear_stall,
_ep_enable,
_ep_disable,
_ep_read_prepare,
_ep_read,
_ep_write,
_ep0_send_status,
_suspend,
_wakeup,
};
int gd_usbd_register(void)
{
rt_memset((void *)&_gd_udc, 0, sizeof(struct udcd));
_gd_udc.parent.type = RT_Device_Class_USBDevice;
_gd_udc.parent.init = _init;
_gd_udc.parent.user_data = NULL;
_gd_udc.ops = &_udc_ops;
/* Register endpoint infomation */
_gd_udc.ep_pool = _ep_pool;
_gd_udc.ep0.id = &_ep_pool[0];
rt_device_register((rt_device_t)&_gd_udc, "usbd", 0);
rt_usb_device_init();
return RT_EOK;
}
INIT_DEVICE_EXPORT(gd_usbd_register);
#ifdef RT_USB_DEVICE_HID
void usbd_hid_send(uint8_t *buffer, uint16_t size)
{
struct hid_s
{
struct rt_device parent;
struct ufunction *func;
uep_t ep_in;
uep_t ep_out;
int status;
rt_uint16_t protocol;
rt_uint8_t report_buf[64];
struct rt_messagequeue hid_mq;
};
udevice_t device = RT_NULL;
uintf_t intf;
ufunction_t func;
struct hid_s *hiddev;
device = rt_usbd_find_device(&_gd_udc);
intf = rt_usbd_find_interface(device, 0, &func);
if (RT_NULL != intf)
{
hiddev = (struct hid_s *)func->user_data;
hiddev->parent.write(&hiddev->parent, 0, buffer, size);
}
}
#endif
#endif /* BSP_USING_USB && RT_USING_USB_DEVICE */
+315
View File
@@ -0,0 +1,315 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-11-07 RealThread the first version
*/
#include "drv_otghs_host.h"
#include <rtthread.h>
#include <rtdevice.h>
#include "board.h"
#include "drv_usb_hw.h"
#if defined(BSP_USING_USB) && defined(RT_USING_USB_HOST)
#define OTG_FS_PORT 1
usb_core_driver gd_usb_core;
static struct rt_completion urb_completion;
static volatile rt_bool_t connect_status = RT_FALSE;
void usbh_connect_callback(usb_core_driver *hhcd)
{
uhcd_t hcd = (uhcd_t)hhcd->host.data;
if (!connect_status)
{
connect_status = RT_TRUE;
rt_usbh_root_hub_connect_handler(hcd, OTG_FS_PORT, RT_FALSE);
}
}
void usbh_disconnect_callback(usb_core_driver *hhcd)
{
uhcd_t hcd = (uhcd_t)hhcd->host.data;
if (connect_status)
{
connect_status = RT_FALSE;
rt_usbh_root_hub_disconnect_handler(hcd, OTG_FS_PORT);
}
}
void usbh_hc_notifyurbchange_callback(usb_core_driver *hhcd, uint8_t chnum, usb_urb_state urb_state)
{
rt_completion_done(&urb_completion);
}
static rt_err_t drv_reset_port(rt_uint8_t port)
{
usb_port_reset(&gd_usb_core);
return RT_EOK;
}
static int drv_pipe_xfer(upipe_t pipe, rt_uint8_t token, void *buffer, int nbytes, int timeout)
{
while (1)
{
uint8_t dir = (pipe->ep.bEndpointAddress & 0x80) >> 7;
if (!connect_status)
{
return -1;
}
rt_completion_init(&urb_completion);
if (dir == 0)
{
if (token == 0)
{
usbh_ctlsetup_send(&gd_usb_core, buffer, pipe->pipe_index);
}
else
{
usbh_data_send(&gd_usb_core, buffer, pipe->pipe_index, nbytes);
}
}
else
{
usbh_data_recev(&gd_usb_core, buffer, pipe->pipe_index, nbytes);
}
rt_completion_wait(&urb_completion, timeout);
rt_thread_mdelay(1);
if (usbh_urbstate_get(&gd_usb_core, pipe->pipe_index) == URB_NOTREADY)
{
if (pipe->ep.bmAttributes == USB_EP_ATTR_INT)
{
rt_thread_delay((pipe->ep.bInterval * RT_TICK_PER_SECOND / 1000) > 0 ? (pipe->ep.bInterval * RT_TICK_PER_SECOND / 1000) : 1);
}
usb_pipe_halt(&gd_usb_core, pipe->pipe_index);
usbh_pipe_create(&gd_usb_core,
pipe->inst->address,
(uint8_t)PORT_SPEED_FULL,
pipe->pipe_index,
pipe->ep.bEndpointAddress,
pipe->ep.bmAttributes,
pipe->ep.wMaxPacketSize);
continue;
}
else if (usbh_urbstate_get(&gd_usb_core, pipe->pipe_index) == URB_STALL)
{
pipe->status = UPIPE_STATUS_STALL;
if (pipe->callback != RT_NULL)
{
pipe->callback(pipe);
}
return -1;
}
else if (usbh_urbstate_get(&gd_usb_core, pipe->pipe_index) == URB_ERROR)
{
pipe->status = UPIPE_STATUS_ERROR;
if (pipe->callback != RT_NULL)
{
pipe->callback(pipe);
}
return -1;
}
else if (usbh_urbstate_get(&gd_usb_core, pipe->pipe_index) == URB_DONE)
{
pipe->status = UPIPE_STATUS_OK;
if (pipe->callback != RT_NULL)
{
pipe->callback(pipe);
}
if (pipe->ep.bEndpointAddress & 0x80)
{
return usbh_xfercount_get(&gd_usb_core, pipe->pipe_index);
}
return nbytes;
}
return -1;
}
}
static rt_uint16_t pipe_index = 0;
static rt_uint8_t drv_get_free_pipe_index()
{
rt_uint8_t idx;
for (idx = 1; idx < 16; idx++)
{
if (!(pipe_index & (0x01 << idx)))
{
pipe_index |= (0x01 << idx);
return (idx - 1);
}
}
return 0xff;
}
static void drv_free_pipe_index(rt_uint8_t index)
{
pipe_index &= ~(0x01 << index);
}
static rt_err_t drv_open_pipe(upipe_t pipe)
{
pipe->pipe_index = drv_get_free_pipe_index();
usbh_pipe_create(&gd_usb_core,
pipe->inst->address,
(uint8_t)PORT_SPEED_FULL,
pipe->pipe_index,
pipe->ep.bEndpointAddress,
pipe->ep.bmAttributes,
pipe->ep.wMaxPacketSize);
/* Set DATA0 PID token*/
if (gd_usb_core.host.pipe[pipe->pipe_index].ep.dir)
{
gd_usb_core.host.pipe[pipe->pipe_index].data_toggle_in = 0;
}
else
{
gd_usb_core.host.pipe[pipe->pipe_index].data_toggle_out = 0;
}
return RT_EOK;
}
static rt_err_t drv_close_pipe(upipe_t pipe)
{
usb_pipe_halt(&gd_usb_core, pipe->pipe_index);
drv_free_pipe_index(pipe->pipe_index);
return RT_EOK;
}
struct uhcd_ops _uhcd_ops = {
drv_reset_port,
drv_pipe_xfer,
drv_open_pipe,
drv_close_pipe,
};
static rt_err_t gd32_hcd_init(rt_device_t device)
{
usb_rcu_config();
usb_timer_init();
/* configure GPIO pin used for switching VBUS power and charge pump I/O */
usb_vbus_config();
uint8_t i = 0U;
#ifdef USE_USBHS0
#ifdef USE_USB_FS
usb_para_init(&gd_usb_core, USBHS0, USB_SPEED_FULL);
#endif
#ifdef USE_USB_HS
usb_para_init(&gd_usb_core, USBHS0, USB_SPEED_HIGH);
#endif
#endif /* USE_USBHS0 */
#ifdef USE_USBHS1
#ifdef USE_USB_FS
usb_para_init(&gd_usb_core, USBHS1, USB_SPEED_FULL);
#endif
#ifdef USE_USB_HS
usb_para_init(&gd_usb_core, USBHS1, USB_SPEED_HIGH);
#endif
#endif /* USE_USBHS1 */
gd_usb_core.host.connect_status = 0U;
for (i = 0U; i < USBHS_MAX_TX_FIFOS; i++)
{
gd_usb_core.host.pipe[i].err_count = 0U;
gd_usb_core.host.pipe[i].pp_status = PIPE_IDLE;
gd_usb_core.host.backup_xfercount[i] = 0U;
}
gd_usb_core.host.pipe[0].ep.mps = 8U;
usb_basic_init(&gd_usb_core.bp, &gd_usb_core.regs);
usb_globalint_disable(&gd_usb_core.regs);
usb_core_init(gd_usb_core.bp, &gd_usb_core.regs);
#ifndef USE_OTG_MODE
usb_curmode_set(&gd_usb_core.regs, HOST_MODE);
#endif /* USE_OTG_MODE */
usb_host_init(&gd_usb_core);
usb_globalint_enable(&gd_usb_core.regs);
#ifdef USE_USB_HS
#ifdef USE_USBHS0
pllusb_rcu_config(USBHS0);
#elif defined USE_USBHS1
pllusb_rcu_config(USBHS1);
#else
#endif
#endif /* USE_USB_HS */
/* enable interrupts */
usb_intr_config();
return RT_EOK;
}
int gd_usbh_register(void)
{
rt_err_t res = -RT_ERROR;
uhcd_t uhcd = (uhcd_t)rt_malloc(sizeof(struct uhcd));
if (uhcd == RT_NULL)
{
rt_kprintf("uhcd malloc failed\r\n");
return -RT_ERROR;
}
rt_memset((void *)uhcd, 0, sizeof(struct uhcd));
uhcd->parent.type = RT_Device_Class_USBHost;
uhcd->parent.init = gd32_hcd_init;
uhcd->parent.user_data = &gd_usb_core;
uhcd->ops = &_uhcd_ops;
uhcd->num_ports = OTG_FS_PORT;
gd_usb_core.host.data = uhcd;
res = rt_device_register(&uhcd->parent, "usbh", RT_DEVICE_FLAG_DEACTIVATE);
if (res != RT_EOK)
{
rt_kprintf("register usb host failed res = %d\r\n", res);
return -RT_ERROR;
}
rt_usb_host_init("usbh");
return RT_EOK;
}
INIT_DEVICE_EXPORT(gd_usbh_register);
#endif /* BSP_USING_USB && RT_USING_USB_HOST */
+440
View File
@@ -0,0 +1,440 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-11-07 RealThread the first version
*/
#include "drv_usb_hw.h"
#include "drv_usb_core.h"
#if defined(BSP_USING_USB)
/* local function prototypes ('static') */
static void usb_core_reset(usb_core_regs *usb_regs);
/*!
\brief configures USB parameters
\param[in] udev: pointer to USB core instance
\param[in] usb_periph: USBHSx(x=0,1)
\param[in] usb_speed: USB speed type (full-speed or high-speed)
\param[out] none
\retval none
*/
void usb_para_init(usb_core_driver *udev, uint32_t usb_periph, uint32_t usb_speed)
{
usb_core_basic *usb_basic = &udev->bp;
/* configure USB default transfer mode as FIFO mode */
usb_basic->transfer_mode = (uint8_t)USB_USE_FIFO;
/* USB default speed is full-speed */
usb_basic->core_speed = (uint8_t)USB_SPEED_FULL;
/* USB basic register address setting */
switch (usb_periph)
{
case USBHS0:
usb_basic->base_reg = (uint32_t)USBHS0_REG_BASE;
break;
case USBHS1:
usb_basic->base_reg = (uint32_t)USBHS1_REG_BASE;
break;
default:
break;
}
/* set the host channel numbers */
usb_basic->num_pipe = USBHS_MAX_CHANNEL_COUNT;
/* set the device endpoint numbers */
usb_basic->num_ep = USBHS_MAX_EP_COUNT;
if (USB_SPEED_HIGH == usb_speed)
{
#ifdef USB_EXTERNAL_ULPI_PHY_ENABLED
usb_basic->phy_itf = USB_ULPI_PHY_EXTERNAL;
#endif /* USB_EXTERNAL_ULPI_PHY_ENABLED */
#ifdef USB_EMBEDDED_HS_PHY_ENABLED
usb_basic->phy_itf = USB_EMBEDDED_PHY_HS;
#endif /* USB_EMBEDDED_HS_PHY_ENABLED */
}
else if (USB_SPEED_FULL == usb_speed)
{
#ifdef USB_EMBEDDED_FS_PHY_ENABLED
usb_basic->phy_itf = USB_EMBEDDED_PHY_FS;
#endif /* USB_EMBEDDED_FS_PHY_ENABLED */
}
else
{
/* no operation */
}
#ifdef USB_INTERNAL_DMA_ENABLED
usb_basic->transfer_mode = USB_USE_DMA;
#endif /* USB_INTERNAL_DMA_ENABLED */
usb_basic->sof_enable = (uint8_t)USB_SOF_OUTPUT;
usb_basic->low_power = (uint8_t)USB_LOW_POWER;
#if (1U == LPM_ENABLED)
usb_basic->lpm_enable = 1U;
#endif /* LPM_ENABLED */
}
/*!
\brief configure USB core basic
\param[in] usb_basic: pointer to USB capabilities
\param[in] usb_regs: pointer to USB core registers
\param[out] none
\retval operation status
*/
usb_status usb_basic_init(usb_core_basic *usb_basic, usb_core_regs *usb_regs)
{
/* assign main registers address */
*usb_regs = (usb_core_regs){
.gr = (usb_gr *)(usb_basic->base_reg + (uint32_t)USB_REG_OFFSET_CORE),
.hr = (usb_hr *)(usb_basic->base_reg + (uint32_t)USB_REG_OFFSET_HOST),
.dr = (usb_dr *)(usb_basic->base_reg + (uint32_t)USB_REG_OFFSET_DEV),
.HPCS = (uint32_t *)(usb_basic->base_reg + (uint32_t)USB_REG_OFFSET_PORT),
.PWRCLKCTL = (uint32_t *)(usb_basic->base_reg + (uint32_t)USB_REG_OFFSET_PWRCLKCTL)
};
/* assign device endpoint registers address */
for (uint8_t i = 0U; i < usb_basic->num_ep; i++)
{
usb_regs->er_in[i] = (usb_erin *)(usb_basic->base_reg + (uint32_t)USB_REG_OFFSET_EP_IN + (i * (uint32_t)USB_REG_OFFSET_EP));
usb_regs->er_out[i] = (usb_erout *)(usb_basic->base_reg + (uint32_t)USB_REG_OFFSET_EP_OUT + (i * (uint32_t)USB_REG_OFFSET_EP));
}
/* assign host pipe registers address */
for (uint8_t i = 0U; i < usb_basic->num_pipe; i++)
{
usb_regs->pr[i] = (usb_pr *)(usb_basic->base_reg + (uint32_t)USB_REG_OFFSET_CH_INOUT + (i * (uint32_t)USB_REG_OFFSET_CH));
usb_regs->DFIFO[i] = (uint32_t *)(usb_basic->base_reg + (uint32_t)USB_DATA_FIFO_OFFSET + (i * (uint32_t)USB_DATA_FIFO_SIZE));
}
return USB_OK;
}
/*!
\brief initializes the USB controller registers and
prepares the core device mode or host mode operation
\param[in] usb_basic: pointer to USB capabilities
\param[in] usb_regs: pointer to USB core registers
\param[out] none
\retval operation status
*/
usb_status usb_core_init(usb_core_basic usb_basic, usb_core_regs *usb_regs)
{
if (USB_ULPI_PHY_EXTERNAL == usb_basic.phy_itf)
{
usb_regs->gr->GCCFG &= ~GCCFG_PWRON;
usb_regs->gr->GUSBCS &= ~(GUSBCS_EMBPHY_HS | GUSBCS_EMBPHY_FS);
if (usb_basic.sof_enable)
{
usb_regs->gr->GCCFG |= GCCFG_SOFOEN;
}
/* initialize the ULPI interface */
usb_regs->gr->GUSBCS &= ~GUSBCS_ULPIEOI;
#ifdef USBHS_EXTERNAL_VBUS_ENABLED
/* use external VBUS driver */
usb_regs->gr->GUSBCS |= GUSBCS_ULPIEVD;
#else
/* use internal VBUS driver */
usb_regs->gr->GUSBCS &= ~GUSBCS_ULPIEVD;
#endif /* USBHS_EXTERNAL_VBUS_ENABLED */
/* soft reset the core */
usb_core_reset(usb_regs);
}
else if (USB_EMBEDDED_PHY_HS == usb_basic.phy_itf)
{
usb_regs->gr->GUSBCS |= GUSBCS_EMBPHY_HS;
/* soft reset the core */
usb_core_reset(usb_regs);
usb_regs->gr->GCCFG = 0U;
#ifdef VBUS_SENSING_ENABLED
/* active the transceiver and enable VBUS sensing */
usb_regs->gr->GCCFG |= GCCFG_VDEN | GCCFG_PWRON;
#else
#ifdef USE_HOST_MODE
usb_regs->gr->GOTGCS |= GOTGCS_AVOV | GOTGCS_AVOE;
#endif /* USE_HOST_MODE */
#ifdef USE_DEVICE_MODE
usb_regs->gr->GOTGCS |= GOTGCS_BVOV | GOTGCS_BVOE;
#endif /* USE_DEVICE_MODE */
/* active the transceiver */
usb_regs->gr->GCCFG |= GCCFG_PWRON;
#endif /* VBUS_SENSING_ENABLED */
if (usb_basic.sof_enable)
{
usb_regs->gr->GCCFG |= GCCFG_SOFOEN;
}
usb_mdelay(20U);
}
else if (USB_EMBEDDED_PHY_FS == usb_basic.phy_itf)
{
usb_regs->gr->GUSBCS |= GUSBCS_EMBPHY_FS;
/* soft reset the core */
usb_core_reset(usb_regs);
#ifdef VBUS_SENSING_ENABLED
/* active the transceiver and enable VBUS sensing */
usb_regs->gr->GCCFG |= GCCFG_VDEN | GCCFG_PWRON;
#else
#ifdef USE_HOST_MODE
usb_regs->gr->GOTGCS |= GOTGCS_AVOV | GOTGCS_AVOE;
#endif /* USE_HOST_MODE */
#ifdef USE_DEVICE_MODE
usb_regs->gr->GOTGCS |= GOTGCS_BVOV | GOTGCS_BVOE;
#endif /* USE_DEVICE_MODE */
/* active the transceiver */
usb_regs->gr->GCCFG |= GCCFG_PWRON;
#endif /* VBUS_SENSING_ENABLED */
/* enable SOF output */
if (usb_basic.sof_enable)
{
usb_regs->gr->GCCFG |= GCCFG_SOFOEN;
}
usb_mdelay(20U);
}
else
{
/* no operation */
}
if ((uint8_t)USB_USE_DMA == usb_basic.transfer_mode)
{
usb_regs->gr->GAHBCS &= ~GAHBCS_BURST;
usb_regs->gr->GAHBCS |= DMA_INCR8 | GAHBCS_DMAEN;
}
#ifdef USE_OTG_MODE
/* enable USB OTG features */
usb_regs->gr->GUSBCS |= GUSBCS_HNPCEN | GUSBCS_SRPCEN;
/* enable the USB wakeup and suspend interrupts */
usb_regs->gr->GINTF = 0xBFFFFFFFU;
usb_regs->gr->GINTEN = GINTEN_WKUPIE | GINTEN_SPIE |
GINTEN_OTGIE | GINTEN_SESIE | GINTEN_IDPSCIE;
#endif /* USE_OTG_MODE */
return USB_OK;
}
/*!
\brief write a packet into the TX FIFO associated with the endpoint
\param[in] usb_regs: pointer to USB core registers
\param[in] src_buf: pointer to source buffer
\param[in] fifo_num: FIFO number which is in (0..5)
\param[in] byte_count: packet byte count
\param[out] none
\retval operation status
*/
usb_status usb_txfifo_write(usb_core_regs *usb_regs,
uint8_t *src_buf,
uint8_t fifo_num,
uint16_t byte_count)
{
uint32_t word_count = (byte_count + 3U) / 4U;
__IO uint32_t *fifo = usb_regs->DFIFO[fifo_num];
while (word_count-- > 0U)
{
*fifo = *((__IO uint32_t *)src_buf);
src_buf += 4U;
}
return USB_OK;
}
/*!
\brief read a packet from the Rx FIFO associated with the endpoint
\param[in] usb_regs: pointer to USB core registers
\param[in] dest_buf: pointer to destination buffer
\param[in] byte_count: packet byte count
\param[out] none
\retval void type pointer
*/
void *usb_rxfifo_read(usb_core_regs *usb_regs, uint8_t *dest_buf, uint16_t byte_count)
{
__IO uint32_t word_count = (byte_count + 3U) / 4U;
__IO uint32_t *fifo = usb_regs->DFIFO[0];
while (word_count-- > 0U)
{
*(__IO uint32_t *)dest_buf = *fifo;
dest_buf += 4U;
}
return ((void *)dest_buf);
}
/*!
\brief flush a TX FIFO or all TX FIFOs
\param[in] usb_regs: pointer to USB core registers
\param[in] fifo_num: FIFO number which is in (0..5)
\param[out] none
\retval operation status
*/
usb_status usb_txfifo_flush(usb_core_regs *usb_regs, uint8_t fifo_num)
{
usb_regs->gr->GRSTCTL = ((uint32_t)fifo_num << 6) | GRSTCTL_TXFF;
/* wait for TX FIFO flush bit is set */
while (usb_regs->gr->GRSTCTL & GRSTCTL_TXFF)
{
/* no operation */
}
/* wait for 3 PHY clocks*/
usb_udelay(3U);
return USB_OK;
}
/*!
\brief flush the entire RX FIFO
\param[in] usb_regs: pointer to USB core registers
\param[out] none
\retval operation status
*/
usb_status usb_rxfifo_flush(usb_core_regs *usb_regs)
{
usb_regs->gr->GRSTCTL = GRSTCTL_RXFF;
/* wait for RX FIFO flush bit is set */
while (usb_regs->gr->GRSTCTL & GRSTCTL_RXFF)
{
/* no operation */
}
/* wait for 3 PHY clocks */
usb_udelay(3U);
return USB_OK;
}
/*!
\brief set endpoint or channel TX FIFO size
\param[in] usb_regs: pointer to USB core registers
\param[in] fifo: TX FIFO number
\param[in] size: assigned TX FIFO size
\param[out] none
\retval none
*/
void usb_set_txfifo(usb_core_regs *usb_regs, uint8_t fifo, uint16_t size)
{
uint32_t tx_offset = usb_regs->gr->GRFLEN;
if (0U == fifo)
{
usb_regs->gr->DIEP0TFLEN_HNPTFLEN = ((uint32_t)size << 16) | tx_offset;
}
else
{
tx_offset += (usb_regs->gr->DIEP0TFLEN_HNPTFLEN) >> 16;
for (uint8_t i = 0U; i < (fifo - 1U); i++)
{
tx_offset += (usb_regs->gr->DIEPTFLEN[i] >> 16);
}
usb_regs->gr->DIEPTFLEN[fifo - 1U] = ((uint32_t)size << 16) | tx_offset;
}
}
/*!
\brief set USB current mode
\param[in] usb_regs: pointer to USB core registers
\param[in] mode: USB current mode
\param[out] none
\retval none
*/
void usb_curmode_set(usb_core_regs *usb_regs, uint8_t mode)
{
usb_regs->gr->GUSBCS &= ~(GUSBCS_FDM | GUSBCS_FHM);
if (DEVICE_MODE == mode)
{
usb_regs->gr->GUSBCS |= GUSBCS_FDM;
}
else if (HOST_MODE == mode)
{
usb_regs->gr->GUSBCS |= GUSBCS_FHM;
}
else
{
/* OTG mode and other mode can not be here! */
}
}
/*!
\brief configure USB core to soft reset
\param[in] usb_regs: pointer to USB core registers
\param[out] none
\retval none
*/
static void usb_core_reset(usb_core_regs *usb_regs)
{
/* enable core soft reset */
usb_regs->gr->GRSTCTL |= GRSTCTL_CSRST;
/* wait for the core to be soft reset */
while (usb_regs->gr->GRSTCTL & GRSTCTL_CSRST)
{
/* no operation */
}
/* wait for additional 3 PHY clocks */
usb_udelay(3U);
}
/*!
\brief active USB core clock
\param[in] udev: pointer to USB device
\param[out] none
\retval none
*/
void usb_clock_active(usb_core_driver *udev)
{
if (udev->bp.low_power)
{
if (udev->regs.dr->DSTAT & DSTAT_SPST)
{
/* un-gate USB Core clock */
*udev->regs.PWRCLKCTL &= ~(PWRCLKCTL_SHCLK | PWRCLKCTL_SUCLK);
}
}
}
#endif /* BSP_USING_USB */
+755
View File
@@ -0,0 +1,755 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-11-07 RealThread the first version
*/
#include "drv_usb_hw.h"
#include "drv_usb_dev.h"
#include <stdio.h>
#if defined(BSP_USING_USB) && defined(RT_USING_USB_DEVICE)
/* endpoint 0 max packet length */
static const uint8_t EP0_MAXLEN[4] = {
[DSTAT_EM_HS_PHY_30MHZ_60MHZ] = EP0MPL_64,
[DSTAT_EM_FS_PHY_30MHZ_60MHZ] = EP0MPL_64,
[DSTAT_EM_FS_PHY_48MHZ] = EP0MPL_64,
[DSTAT_EM_LS_PHY_6MHZ] = EP0MPL_8
};
/* USB endpoint TX FIFO size */
static uint16_t USBHS_TX_FIFO_SIZE[USBHS_MAX_EP_COUNT] = {
(uint16_t)TX0_FIFO_SIZE,
(uint16_t)TX1_FIFO_SIZE,
(uint16_t)TX2_FIFO_SIZE,
(uint16_t)TX3_FIFO_SIZE,
(uint16_t)TX4_FIFO_SIZE,
(uint16_t)TX5_FIFO_SIZE,
(uint16_t)TX6_FIFO_SIZE,
(uint16_t)TX7_FIFO_SIZE,
};
/*!
\brief initialize USB core registers for device mode
\param[in] udev: pointer to USB device
\param[out] none
\retval operation status
*/
usb_status usb_devcore_init(usb_core_driver *udev)
{
uint8_t i = 0U;
/* restart the PHY clock (maybe don't need to...) */
*udev->regs.PWRCLKCTL = 0U;
/* configure periodic frame interval to default value */
udev->regs.dr->DCFG &= ~DCFG_EOPFT;
udev->regs.dr->DCFG |= FRAME_INTERVAL_80;
udev->regs.dr->DCFG &= ~DCFG_DS;
if (USB_EMBEDDED_PHY_HS == udev->bp.phy_itf)
{
udev->regs.dr->DCFG |= USB_SPEED_INP_HIGH;
}
else if (USB_EMBEDDED_PHY_FS == udev->bp.phy_itf)
{
udev->regs.dr->DCFG |= USB_SPEED_INP_FULL;
}
else
{
/* no operation */
}
/* set RX FIFO size */
usb_set_rxfifo(&udev->regs, (uint16_t)RX_FIFO_SIZE);
/* set endpoint 0 to 3's TX FIFO length and RAM address */
for (i = 0U; i < USBHS_MAX_EP_COUNT; i++)
{
usb_set_txfifo(&udev->regs, i, USBHS_TX_FIFO_SIZE[i]);
}
/* make sure all FIFOs are flushed */
/* flush all TX FIFOs */
(void)usb_txfifo_flush(&udev->regs, 0x10U);
/* flush entire RX FIFO */
(void)usb_rxfifo_flush(&udev->regs);
/* clear all pending device interrupts */
udev->regs.dr->DIEPINTEN = 0U;
udev->regs.dr->DOEPINTEN = 0U;
udev->regs.dr->DAEPINT = 0xFFFFFFFFU;
udev->regs.dr->DAEPINTEN = 0U;
/* configure all IN/OUT endpoints */
for (i = 0U; i < udev->bp.num_ep; i++)
{
if (udev->regs.er_in[i]->DIEPCTL & DEPCTL_EPEN)
{
udev->regs.er_in[i]->DIEPCTL |= DEPCTL_EPD | DEPCTL_SNAK;
}
else
{
udev->regs.er_in[i]->DIEPCTL = 0U;
}
/* set IN endpoint transfer length to 0 */
udev->regs.er_in[i]->DIEPLEN = 0U;
/* clear all pending IN endpoint interrupts */
udev->regs.er_in[i]->DIEPINTF = 0xFFU;
if (udev->regs.er_out[i]->DOEPCTL & DEPCTL_EPEN)
{
udev->regs.er_out[i]->DOEPCTL |= DEPCTL_EPD | DEPCTL_SNAK;
}
else
{
udev->regs.er_out[i]->DOEPCTL = 0U;
}
/* set OUT endpoint transfer length to 0 */
udev->regs.er_out[i]->DOEPLEN = 0U;
/* clear all pending OUT endpoint interrupts */
udev->regs.er_out[i]->DOEPINTF = 0xFFU;
}
udev->regs.dr->DIEPINTEN |= DIEPINTEN_EPTXFUDEN;
(void)usb_devint_enable(udev);
/* configure LPM function */
if (1U == udev->bp.lpm_enable)
{
udev->dev.pm.lpm_state = LPM_L0;
udev->regs.gr->GLPMCFG = GLPMCFG_LPMEN | GLPMCFG_ACKLPM | GLPMCFG_BESLEN;
}
return USB_OK;
}
/*!
\brief enable the USB device mode interrupts
\param[in] udev: pointer to USB device
\param[out] none
\retval operation status
*/
usb_status usb_devint_enable(usb_core_driver *udev)
{
/* clear any pending USB OTG interrupts */
udev->regs.gr->GOTGINTF = 0xFFFFFFFFU;
/* clear any pending interrupts */
udev->regs.gr->GINTF = 0xBFFFFFFFU;
/* enable the USB wakeup and suspend interrupts */
udev->regs.gr->GINTEN = GINTEN_WKUPIE | GINTEN_SPIE;
/* enable device_mode-related interrupts */
if ((uint8_t)USB_USE_FIFO == udev->bp.transfer_mode)
{
udev->regs.gr->GINTEN |= GINTEN_RXFNEIE;
}
udev->regs.gr->GINTEN |= GINTEN_RSTIE | GINTEN_ENUMFIE | GINTEN_IEPIE |
GINTEN_OEPIE | GINTEN_SOFIE | GINTEN_ISOONCIE | GINTEN_ISOINCIE;
#ifdef VBUS_SENSING_ENABLED
udev->regs.gr->GINTEN |= GINTEN_SESIE | GINTEN_OTGIE;
#endif /* VBUS_SENSING_ENABLED */
if (1U == udev->bp.lpm_enable)
{
udev->regs.gr->GINTEN |= GINTEN_LPMIE;
}
return USB_OK;
}
/*!
\brief active the USB endpoint0 transaction
\param[in] udev: pointer to USB device
\param[in] transc: the USB endpoint0 transaction
\param[out] none
\retval operation status
*/
usb_status usb_transc0_active(usb_core_driver *udev, usb_transc *transc)
{
__IO uint32_t *reg_addr = NULL;
uint32_t enum_speed = udev->regs.dr->DSTAT & DSTAT_ES;
/* get the endpoint number */
uint8_t ep_num = transc->ep_addr.num;
if (ep_num)
{
/* not endpoint 0 */
return USB_FAIL;
}
if (transc->ep_addr.dir)
{
reg_addr = &udev->regs.er_in[0]->DIEPCTL;
}
else
{
reg_addr = &udev->regs.er_out[0]->DOEPCTL;
}
/* endpoint 0 is activated after USB clock is enabled */
*reg_addr &= ~(DEPCTL_MPL | DEPCTL_EPTYPE | DIEPCTL_TXFNUM);
/* set endpoint 0 maximum packet length */
*reg_addr |= EP0_MAXLEN[enum_speed];
/* activate endpoint */
*reg_addr |= ((uint32_t)transc->ep_type << 18) | ((uint32_t)ep_num << 22) | DEPCTL_SD0PID | DEPCTL_EPACT;
return USB_OK;
}
/*!
\brief active the USB transaction
\param[in] udev: pointer to USB device
\param[in] transc: the USB transaction
\param[out] none
\retval status
*/
usb_status usb_ep_active(usb_core_driver *udev, uint8_t ep_addr, uint8_t ep_mps, uint8_t ep_attr)
{
__IO uint32_t *reg_addr = NULL;
uint32_t epinten = 0U;
uint32_t enum_speed = udev->regs.dr->DSTAT & DSTAT_ES;
usb_transc *transc = NULL;
/* get the endpoint number */
uint8_t ep_num = EP_ID(ep_addr);
/* enable endpoint interrupt number */
if (EP_DIR(ep_addr))
{
reg_addr = &udev->regs.er_in[ep_num]->DIEPCTL;
epinten = 1U << ep_num;
transc = &udev->dev.transc_in[ep_num];
}
else
{
reg_addr = &udev->regs.er_out[ep_num]->DOEPCTL;
epinten = 1U << (16U + ep_num);
transc = &udev->dev.transc_out[ep_num];
}
*transc = (usb_transc){ 0 };
transc->max_len = ep_mps;
transc->ep_type = ep_attr & USB_EPTYPE_MASK;
/* if the endpoint is not active, need change the endpoint control register */
if (!(*reg_addr & DEPCTL_EPACT))
{
*reg_addr &= ~(DEPCTL_MPL | DEPCTL_EPTYPE | DIEPCTL_TXFNUM);
/* set endpoint maximum packet length */
if (0U == ep_num)
{
*reg_addr |= EP0_MAXLEN[enum_speed];
}
else
{
*reg_addr |= transc->max_len;
}
/* activate endpoint */
*reg_addr |= ((uint32_t)transc->ep_type << 18) | ((uint32_t)ep_num << 22) | DEPCTL_SD0PID | DEPCTL_EPACT;
}
#ifdef USB_DEDICATED_EP1_ENABLED
if (1U == ep_num)
{
udev->regs.dr->DEP1INTEN |= epinten;
}
else
#endif
{
/* enable the interrupts for this endpoint */
udev->regs.dr->DAEPINTEN |= epinten;
}
return USB_OK;
}
/*!
\brief deactivate the USB transaction
\param[in] udev: pointer to USB device
\param[in] transc: the USB transaction
\param[out] none
\retval status
*/
usb_status usb_ep_deactivate(usb_core_driver *udev, uint8_t ep_addr)
{
uint32_t epinten = 0U;
uint8_t ep_num = EP_ID(ep_addr);
/* disable endpoint interrupt number */
if (EP_DIR(ep_addr))
{
epinten = 1U << ep_num;
udev->regs.er_in[ep_num]->DIEPCTL &= ~DEPCTL_EPACT;
}
else
{
epinten = 1U << (ep_num + 16U);
udev->regs.er_out[ep_num]->DOEPCTL &= ~DEPCTL_EPACT;
}
#ifdef USB_DEDICATED_EP1_ENABLED
if (1U == ep_num)
{
udev->regs.dr->DEP1INTEN &= ~epinten;
}
else
#endif
{
/* disable the interrupts for this endpoint */
udev->regs.dr->DAEPINTEN &= ~epinten;
}
return USB_OK;
}
/*!
\brief configure USB transaction to start IN transfer
\param[in] udev: pointer to USB device
\param[in] transc: the USB IN transaction
\param[out] none
\retval operation status
*/
usb_status usb_ep_inxfer(usb_core_driver *udev, uint8_t ep_addr, uint8_t *buf, uint16_t count)
{
usb_status status = USB_OK;
uint8_t ep_num = EP_ID(ep_addr);
__IO uint32_t epctl = udev->regs.er_in[ep_num]->DIEPCTL;
__IO uint32_t eplen = udev->regs.er_in[ep_num]->DIEPLEN;
usb_transc *transc = &udev->dev.transc_in[ep_num];
eplen &= ~(DEPLEN_TLEN | DEPLEN_PCNT);
transc->xfer_len = count;
transc->xfer_buf = buf;
transc->xfer_count = 0U;
/* zero length packet or endpoint 0 */
if (0U == count)
{
/* set transfer packet count to 1 */
eplen |= 1U << 19;
}
else
{
/* set transfer packet count */
if (0U == ep_num)
{
transc->xfer_len = USB_MIN(count, transc->max_len);
eplen |= 1U << 19;
}
else
{
eplen |= ((count - 1 + transc->max_len) / transc->max_len) << 19;
}
/* set endpoint transfer length */
eplen |= count;
if ((uint8_t)USB_EPTYPE_ISOC == transc->ep_type)
{
eplen |= DIEPLEN_MCNT & (1U << 29);
}
}
udev->regs.er_in[ep_num]->DIEPLEN = eplen;
if ((uint8_t)USB_EPTYPE_ISOC == transc->ep_type)
{
if (((udev->regs.dr->DSTAT & DSTAT_FNRSOF) >> 8) & 0x01U)
{
epctl |= DEPCTL_SEVNFRM;
}
else
{
epctl |= DEPCTL_SODDFRM;
}
}
if ((uint8_t)USB_USE_DMA == udev->bp.transfer_mode)
{
udev->regs.er_in[ep_num]->DIEPDMAADDR = transc->dma_addr;
}
/* enable the endpoint and clear the NAK */
epctl |= DEPCTL_CNAK | DEPCTL_EPEN;
udev->regs.er_in[ep_num]->DIEPCTL = epctl;
if ((uint8_t)USB_USE_FIFO == udev->bp.transfer_mode)
{
if ((uint8_t)USB_EPTYPE_ISOC != transc->ep_type)
{
/* enable the TX FIFO empty interrupt for this endpoint */
if (count > 0U)
{
udev->regs.dr->DIEPFEINTEN |= 1U << ep_num;
}
}
else
{
(void)usb_txfifo_write(&udev->regs, transc->xfer_buf, ep_num, (uint16_t)transc->xfer_len);
}
}
return status;
}
/*!
\brief configure USB transaction to start OUT transfer
\param[in] udev: pointer to USB device
\param[in] transc: the USB OUT transaction
\param[out] none
\retval status
*/
usb_status usb_ep_outxfer(usb_core_driver *udev, uint8_t ep_addr, uint8_t *buf, uint16_t count)
{
usb_status status = USB_OK;
uint8_t ep_num = EP_ID(ep_addr);
uint32_t epctl = udev->regs.er_out[ep_num]->DOEPCTL;
uint32_t eplen = udev->regs.er_out[ep_num]->DOEPLEN;
usb_transc *transc = &udev->dev.transc_out[ep_num];
transc->xfer_buf = buf;
transc->xfer_len = count;
eplen &= ~(DEPLEN_TLEN | DEPLEN_PCNT);
/* zero length packet or endpoint 0 */
if ((0U == count) || (0U == ep_num))
{
/* set the transfer length to max packet size */
eplen |= transc->max_len;
/* set the transfer packet count to 1 */
eplen |= 1U << 19;
}
else
{
/* configure the transfer size and packet count as follows:
* pktcnt = N
* xfersize = N * maxpacket
*/
uint32_t packet_count = (count + transc->max_len - 1) / transc->max_len;
eplen |= packet_count << 19;
eplen |= packet_count * transc->max_len;
}
udev->regs.er_out[ep_num]->DOEPLEN = eplen;
if ((uint8_t)USB_USE_DMA == udev->bp.transfer_mode)
{
udev->regs.er_out[ep_num]->DOEPDMAADDR = transc->dma_addr;
}
if ((uint8_t)USB_EPTYPE_ISOC == transc->ep_type)
{
if (transc->frame_num)
{
epctl |= DEPCTL_SD1PID;
}
else
{
epctl |= DEPCTL_SD0PID;
}
}
/* enable the endpoint and clear the NAK */
epctl |= DEPCTL_EPEN | DEPCTL_CNAK;
udev->regs.er_out[ep_num]->DOEPCTL = epctl;
return status;
}
/*!
\brief set the USB transaction STALL status
\param[in] udev: pointer to USB device
\param[in] transc: the USB transaction
\param[out] none
\retval status
*/
usb_status usb_ep_stall(usb_core_driver *udev, uint8_t ep_addr)
{
__IO uint32_t *reg_addr = NULL;
uint8_t ep_num = EP_ID(ep_addr);
if (EP_DIR(ep_addr))
{
reg_addr = &(udev->regs.er_in[ep_num]->DIEPCTL);
/* set the endpoint disable bit */
if (*reg_addr & DEPCTL_EPEN)
{
*reg_addr |= DEPCTL_EPD;
}
}
else
{
/* set the endpoint STALL bit */
reg_addr = &(udev->regs.er_out[ep_num]->DOEPCTL);
}
/* set the endpoint STALL bit */
*reg_addr |= DEPCTL_STALL;
return USB_OK;
}
/*!
\brief clear the USB transaction STALL status
\param[in] udev: pointer to USB device
\param[in] transc: the USB transaction
\param[out] none
\retval operation status
*/
usb_status usb_ep_clrstall(usb_core_driver *udev, uint8_t ep_addr)
{
__IO uint32_t *reg_addr = NULL;
uint32_t ep_type = 0U;
uint8_t ep_num = EP_ID(ep_addr);
if (EP_DIR(ep_addr))
{
reg_addr = &(udev->regs.er_in[ep_num]->DIEPCTL);
}
else
{
reg_addr = &(udev->regs.er_out[ep_num]->DOEPCTL);
}
/* clear the endpoint STALL bit */
*reg_addr &= ~DEPCTL_STALL;
ep_type = (DEPCTL_EPTYPE & *reg_addr) >> 18;
/* reset data PID of the periodic endpoints */
if (((uint8_t)USB_EPTYPE_INTR == ep_type) || ((uint8_t)USB_EPTYPE_BULK == ep_type))
{
*reg_addr |= DEPCTL_SD0PID;
}
return USB_OK;
}
/*!
\brief read device IN endpoint interrupt flag register
\param[in] udev: pointer to USB device
\param[in] ep_num: endpoint number
\param[out] none
\retval interrupt value
*/
uint32_t usb_iepintr_read(usb_core_driver *udev, uint8_t ep_num)
{
__IO uint32_t value = 0U;
uint32_t fifoemptymask, commonintmask;
commonintmask = udev->regs.dr->DIEPINTEN;
fifoemptymask = udev->regs.dr->DIEPFEINTEN;
/* check FIFO empty interrupt enable bit */
commonintmask |= ((fifoemptymask >> ep_num) & 0x1U) << 7;
value = udev->regs.er_in[ep_num]->DIEPINTF & commonintmask;
return value;
}
/*!
\brief configures OUT endpoint 0 to receive SETUP packets
\param[in] udev: pointer to USB device
\param[out] none
\retval none
*/
void usb_ctlep_startout(usb_core_driver *udev)
{
/* set OUT endpoint 0 receive length to 24 bytes, 1 packet and 3 SETUP packets */
udev->regs.er_out[0]->DOEPLEN = DOEP0_TLEN(8U * 3U) | DOEP0_PCNT(1U) | DOEP0_STPCNT(3U);
if ((uint8_t)USB_USE_DMA == udev->bp.transfer_mode)
{
udev->regs.er_out[0]->DOEPDMAADDR = (uint32_t)&udev->dev.control.req;
/* endpoint enable */
udev->regs.er_out[0]->DOEPCTL |= DEPCTL_EPACT | DEPCTL_EPEN;
}
}
/*!
\brief active remote wakeup signaling
\param[in] udev: pointer to USB device
\param[out] none
\retval none
*/
void usb_rwkup_active(usb_core_driver *udev)
{
if (udev->dev.pm.dev_remote_wakeup)
{
if (udev->regs.dr->DSTAT & DSTAT_SPST)
{
if (udev->bp.low_power)
{
/* ungate USB core clock */
*udev->regs.PWRCLKCTL &= ~(PWRCLKCTL_SHCLK | PWRCLKCTL_SUCLK);
}
/* active remote wakeup signaling */
udev->regs.dr->DCTL |= DCTL_RWKUP;
usb_mdelay(5U);
udev->regs.dr->DCTL &= ~DCTL_RWKUP;
}
}
}
/*!
\brief USB device suspend
\param[in] udev: pointer to USB device
\param[out] none
\retval none
*/
void usb_dev_suspend(usb_core_driver *udev)
{
__IO uint32_t devstat = udev->regs.dr->DSTAT;
if ((devstat & DSTAT_SPST) && (udev->bp.low_power))
{
/* switch-off the USB clocks */
*udev->regs.PWRCLKCTL |= PWRCLKCTL_SHCLK;
/* enter DEEP_SLEEP mode with LDO in low power mode */
pmu_to_deepsleepmode(WFI_CMD);
}
}
/*!
\brief stop the device and clean up FIFOs
\param[in] udev: pointer to USB device
\param[out] none
\retval none
*/
void usb_dev_stop(usb_core_driver *udev)
{
uint32_t i;
udev->dev.cur_status = 1U;
/* clear all interrupt flag and enable bits */
for (i = 0U; i < udev->bp.num_ep; i++)
{
udev->regs.er_in[i]->DIEPINTF = 0xFFU;
udev->regs.er_out[i]->DOEPINTF = 0xFFU;
}
udev->regs.dr->DIEPINTEN = 0U;
udev->regs.dr->DOEPINTEN = 0U;
udev->regs.dr->DAEPINTEN = 0U;
udev->regs.dr->DAEPINT = 0xFFFFFFFFU;
/* flush the FIFO */
(void)usb_rxfifo_flush(&udev->regs);
(void)usb_txfifo_flush(&udev->regs, 0x10U);
}
/*!
\brief usb battery charging detect operation
\param[in] udev: pointer to USB device instance
\param[out] none
\retval bcd type
*/
bcd_type usbd_bcd_detect(usb_core_driver *udev)
{
udev->regs.gr->GCCFG |= GCCFG_DCDEN;
usb_mdelay(1000U);
if (0U == (udev->regs.gr->GCCFG & GCCFG_DCDF))
{
udev->regs.gr->GCCFG &= ~GCCFG_DCDEN;
return BCD_ERROR;
}
udev->regs.gr->GCCFG &= ~GCCFG_DCDEN;
usb_mdelay(20U);
udev->regs.gr->GCCFG |= GCCFG_PEMEN;
usb_mdelay(50U);
if (udev->regs.gr->GCCFG & GCCFG_PDF)
{
udev->regs.gr->GCCFG &= ~GCCFG_PEMEN;
usb_mdelay(20U);
udev->regs.gr->GCCFG |= GCCFG_SDMEN;
usb_mdelay(50U);
if (udev->regs.gr->GCCFG & GCCFG_SDF)
{
udev->regs.gr->GCCFG &= ~GCCFG_SDMEN;
usb_mdelay(20U);
return BCD_DCP;
}
else
{
udev->regs.gr->GCCFG &= ~GCCFG_SDMEN;
usb_mdelay(20U);
return BCD_CDP;
}
}
else if (udev->regs.gr->GCCFG & GCCFG_PS2F)
{
udev->regs.gr->GCCFG &= ~GCCFG_PEMEN;
usb_mdelay(20U);
return BCD_PS2;
}
else
{
udev->regs.gr->GCCFG &= ~GCCFG_PEMEN;
usb_mdelay(20U);
return BCD_SDP;
}
}
#endif /* BSP_USING_USB && RT_USING_USB_DEVICE */
+448
View File
@@ -0,0 +1,448 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-11-07 RealThread the first version
*/
#include "drv_usb_hw.h"
#include "drv_usb_host.h"
#if defined(BSP_USING_USB) && defined(RT_USING_USB_HOST)
const uint32_t PIPE_DPID[2] = {
PIPE_DPID_DATA0,
PIPE_DPID_DATA1
};
/*!
\brief initializes USB core for host mode
\param[in] udev: pointer to selected USB host
\param[out] none
\retval operation status
*/
usb_status usb_host_init(usb_core_driver *udev)
{
uint32_t i = 0U, inten = 0U;
uint32_t nptxfifolen = 0U;
uint32_t ptxfifolen = 0U;
/* restart the PHY Clock */
*udev->regs.PWRCLKCTL = 0U;
/* support HS, FS and LS */
udev->regs.hr->HCTL &= ~HCTL_SPDFSLS;
/* set RX FIFO size */
udev->regs.gr->GRFLEN = USB_RX_FIFO_SIZE;
/* set non-periodic TX FIFO size and address */
nptxfifolen |= USB_RX_FIFO_SIZE;
nptxfifolen |= USB_HTX_NPFIFO_SIZE << 16;
udev->regs.gr->DIEP0TFLEN_HNPTFLEN = nptxfifolen;
/* set periodic TX FIFO size and address */
ptxfifolen |= USB_RX_FIFO_SIZE + USB_HTX_PFIFO_SIZE;
ptxfifolen |= USB_HTX_PFIFO_SIZE << 16;
udev->regs.gr->HPTFLEN = ptxfifolen;
#ifdef USE_OTG_MODE
/* clear host set HNP enable in the usb_otg control register */
udev->regs.gr->GOTGCS &= ~GOTGCS_HHNPEN;
#endif /* USE_OTG_MODE */
/* make sure the FIFOs are flushed */
/* flush all TX FIFOs in device or host mode */
usb_txfifo_flush(&udev->regs, 0x10U);
/* flush the entire RX FIFO */
usb_rxfifo_flush(&udev->regs);
/* disable all interrupts */
udev->regs.gr->GINTEN = 0U;
/* clear any pending USB OTG interrupts */
udev->regs.gr->GOTGINTF = 0xFFFFFFFFU;
/* enable the USB wakeup and suspend interrupts */
udev->regs.gr->GINTF = 0xBFFFFFFFU;
/* clear all pending host channel interrupts */
for (i = 0U; i < udev->bp.num_pipe; i++)
{
udev->regs.pr[i]->HCHINTF = 0xFFFFFFFFU;
udev->regs.pr[i]->HCHINTEN = 0U;
}
#ifndef USE_OTG_MODE
usb_portvbus_switch(udev, 1U);
#endif /* USE_OTG_MODE */
udev->regs.gr->GINTEN = GINTEN_WKUPIE | GINTEN_SPIE;
/* enable host_mode-related interrupts */
if (USB_USE_FIFO == udev->bp.transfer_mode)
{
inten = GINTEN_RXFNEIE;
}
/* configure USBHS interrupts */
inten |= GINTEN_HPIE | GINTEN_HCIE | GINTEN_ISOINCIE;
udev->regs.gr->GINTEN |= inten;
inten = GINTEN_DISCIE | GINTEN_SOFIE;
udev->regs.gr->GINTEN &= ~inten;
return USB_OK;
}
/*!
\brief control the VBUS to power
\param[in] udev: pointer to selected USB host
\param[in] state: VBUS state
\param[out] none
\retval none
*/
void usb_portvbus_switch(usb_core_driver *udev, uint8_t state)
{
uint32_t port = 0U;
/* enable or disable the external charge pump */
usb_vbus_drive(state);
/* turn on the host port power. */
port = usb_port_read(udev);
if ((!(port & HPCS_PP)) && (1U == state))
{
port |= HPCS_PP;
}
if ((port & HPCS_PP) && (0U == state))
{
port &= ~HPCS_PP;
}
*udev->regs.HPCS = port;
usb_mdelay(200U);
}
/*!
\brief reset host port
\param[in] udev: pointer to USB device
\param[out] none
\retval operation status
*/
uint32_t usb_port_reset(usb_core_driver *udev)
{
__IO uint32_t port = usb_port_read(udev);
*udev->regs.HPCS = port | HPCS_PRST;
usb_mdelay(20U); /* see note */
*udev->regs.HPCS = port & ~HPCS_PRST;
usb_mdelay(20U);
return 1U;
}
/*!
\brief initialize host pipe
\param[in] udev: pointer to USB device
\param[in] pipe_num: host pipe number which is in (0..7)
\param[out] none
\retval operation status
*/
usb_status usb_pipe_init(usb_core_driver *udev, uint8_t pipe_num)
{
usb_status status = USB_OK;
__IO uint32_t pp_ctl = 0U;
__IO uint32_t pp_inten = HCHINTEN_TFIE;
usb_pipe *pp = &udev->host.pipe[pipe_num];
/* clear old interrupt conditions for this host channel */
udev->regs.pr[pipe_num]->HCHINTF = 0xFFFFFFFFU;
if (USB_USE_DMA == udev->bp.transfer_mode)
{
pp_inten |= HCHINTEN_DMAERIE;
}
if (pp->ep.dir)
{
pp_inten |= HCHINTEN_BBERIE;
}
/* enable channel interrupts required for this transfer */
switch (pp->ep.type)
{
case USB_EPTYPE_CTRL:
case USB_EPTYPE_BULK:
pp_inten |= HCHINTEN_STALLIE | HCHINTEN_USBERIE | HCHINTEN_DTERIE | HCHINTEN_NAKIE;
if (!pp->ep.dir)
{
if (PORT_SPEED_HIGH == pp->dev_speed)
{
pp_inten |= HCHINTEN_NYETIE;
pp_inten |= HCHINTEN_ACKIE;
}
}
break;
case USB_EPTYPE_INTR:
pp_inten |= HCHINTEN_STALLIE | HCHINTEN_USBERIE | HCHINTEN_DTERIE | HCHINTEN_NAKIE | HCHINTEN_REQOVRIE;
break;
case USB_EPTYPE_ISOC:
pp_inten |= HCHINTEN_REQOVRIE | HCHINTEN_ACKIE;
if (pp->ep.dir)
{
pp_inten |= HCHINTEN_USBERIE;
}
break;
default:
break;
}
udev->regs.pr[pipe_num]->HCHINTEN = pp_inten;
/* enable the top level host channel interrupt */
udev->regs.hr->HACHINTEN |= 1U << pipe_num;
/* make sure host channel interrupts are enabled */
udev->regs.gr->GINTEN |= GINTEN_HCIE;
/* program the host channel control register */
pp_ctl |= PIPE_CTL_DAR(pp->dev_addr);
pp_ctl |= PIPE_CTL_EPNUM(pp->ep.num);
pp_ctl |= PIPE_CTL_EPDIR(pp->ep.dir);
pp_ctl |= PIPE_CTL_EPTYPE(pp->ep.type);
pp_ctl |= PIPE_CTL_LSD(PORT_SPEED_LOW == pp->dev_speed);
pp_ctl |= pp->ep.mps;
pp_ctl |= ((uint32_t)(USB_EPTYPE_INTR == pp->ep.type) << 29) & HCHCTL_ODDFRM;
udev->regs.pr[pipe_num]->HCHCTL = pp_ctl;
return status;
}
/*!
\brief prepare host channel for transferring packets
\param[in] udev: pointer to USB device
\param[in] pipe_num: host pipe number which is in (0..7)
\param[out] none
\retval operation status
*/
usb_status usb_pipe_xfer(usb_core_driver *udev, uint8_t pipe_num)
{
usb_status status = USB_OK;
uint16_t dword_len = 0U;
uint16_t packet_count = 0U;
__IO uint32_t pp_ctl = 0U;
usb_pipe *pp = &udev->host.pipe[pipe_num];
uint16_t max_packet_len = pp->ep.mps;
/* compute the expected number of packets associated to the transfer */
if (pp->xfer_len > 0U)
{
packet_count = (uint16_t)((pp->xfer_len + max_packet_len - 1U) / max_packet_len);
if (packet_count > HC_MAX_PACKET_COUNT)
{
packet_count = HC_MAX_PACKET_COUNT;
pp->xfer_len = (uint16_t)(packet_count * max_packet_len);
}
}
else
{
packet_count = 1U;
}
if (pp->ep.dir)
{
pp->xfer_len = (uint16_t)(packet_count * max_packet_len);
}
/* initialize the host channel transfer information */
udev->regs.pr[pipe_num]->HCHLEN = pp->xfer_len | pp->DPID | PIPE_XFER_PCNT(packet_count);
if (USB_USE_DMA == udev->bp.transfer_mode)
{
udev->regs.pr[pipe_num]->HCHDMAADDR = (unsigned int)pp->xfer_buf;
}
pp_ctl = udev->regs.pr[pipe_num]->HCHCTL;
if (usb_frame_even(udev))
{
pp_ctl |= HCHCTL_ODDFRM;
}
else
{
pp_ctl &= ~HCHCTL_ODDFRM;
}
/* set host channel enabled */
pp_ctl |= HCHCTL_CEN;
pp_ctl &= ~HCHCTL_CDIS;
udev->regs.pr[pipe_num]->HCHCTL = pp_ctl;
if (USB_USE_FIFO == udev->bp.transfer_mode)
{
if ((0U == pp->ep.dir) && (pp->xfer_len > 0U))
{
switch (pp->ep.type)
{
/* non-periodic transfer */
case USB_EPTYPE_CTRL:
case USB_EPTYPE_BULK:
dword_len = (uint16_t)((pp->xfer_len + 3U) / 4U);
/* check if there is enough space in FIFO space */
if (dword_len > (udev->regs.gr->HNPTFQSTAT & HNPTFQSTAT_NPTXFS))
{
/* need to process data in nptxfempty interrupt */
udev->regs.gr->GINTEN |= GINTEN_NPTXFEIE;
}
break;
/* periodic transfer */
case USB_EPTYPE_INTR:
case USB_EPTYPE_ISOC:
dword_len = (uint16_t)((pp->xfer_len + 3U) / 4U);
/* check if there is enough space in FIFO space */
if (dword_len > (udev->regs.hr->HPTFQSTAT & HPTFQSTAT_PTXFS))
{
/* need to process data in ptxfempty interrupt */
udev->regs.gr->GINTEN |= GINTEN_PTXFEIE;
}
break;
default:
break;
}
/* write packet into the TX FIFO */
usb_txfifo_write(&udev->regs, pp->xfer_buf, pipe_num, (uint16_t)pp->xfer_len);
}
}
return status;
}
/*!
\brief halt pipe
\param[in] udev: pointer to USB device
\param[in] pipe_num: host pipe number which is in (0..7)
\param[out] none
\retval operation status
*/
usb_status usb_pipe_halt(usb_core_driver *udev, uint8_t pipe_num)
{
__IO uint32_t pp_ctl = udev->regs.pr[pipe_num]->HCHCTL;
uint8_t ep_type = (uint8_t)((pp_ctl & HCHCTL_EPTYPE) >> 18U);
pp_ctl |= HCHCTL_CEN | HCHCTL_CDIS;
switch (ep_type)
{
case USB_EPTYPE_CTRL:
case USB_EPTYPE_BULK:
if (0U == (udev->regs.gr->HNPTFQSTAT & HNPTFQSTAT_NPTXFS))
{
pp_ctl &= ~HCHCTL_CEN;
}
break;
case USB_EPTYPE_INTR:
case USB_EPTYPE_ISOC:
if (0U == (udev->regs.hr->HPTFQSTAT & HPTFQSTAT_PTXFS))
{
pp_ctl &= ~HCHCTL_CEN;
}
break;
default:
break;
}
udev->regs.pr[pipe_num]->HCHCTL = pp_ctl;
return USB_OK;
}
/*!
\brief configure host pipe to do ping operation
\param[in] udev: pointer to USB device
\param[in] pipe_num: host pipe number which is in (0..7)
\param[out] none
\retval operation status
*/
usb_status usb_pipe_ping(usb_core_driver *udev, uint8_t pipe_num)
{
uint32_t pp_ctl = 0U;
udev->regs.pr[pipe_num]->HCHLEN = HCHLEN_PING;
pp_ctl = udev->regs.pr[pipe_num]->HCHCTL;
pp_ctl |= HCHCTL_CEN;
pp_ctl &= ~HCHCTL_CDIS;
udev->regs.pr[pipe_num]->HCHCTL = pp_ctl;
return USB_OK;
}
/*!
\brief stop the USB host and clean up FIFO
\param[in] udev: pointer to USB device
\param[out] none
\retval none
*/
void usb_host_stop(usb_core_driver *udev)
{
uint32_t i;
__IO uint32_t pp_ctl = 0U;
udev->regs.hr->HACHINTEN = 0x0U;
udev->regs.hr->HACHINT = 0xFFFFFFFFU;
/* flush out any leftover queued requests. */
for (i = 0U; i < udev->bp.num_pipe; i++)
{
pp_ctl = udev->regs.pr[i]->HCHCTL;
pp_ctl &= ~(HCHCTL_CEN | HCHCTL_EPDIR);
pp_ctl |= HCHCTL_CDIS;
udev->regs.pr[i]->HCHCTL = pp_ctl;
}
/* flush the FIFO */
usb_rxfifo_flush(&udev->regs);
usb_txfifo_flush(&udev->regs, 0x10U);
}
#endif /* BSP_USING_USB && RT_USING_USB_HOST */
+703
View File
@@ -0,0 +1,703 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-11-07 RealThread the first version
*/
#include "drv_usbd_int.h"
#include "drv_otghs_dev.h"
#include <stdio.h>
#if defined(BSP_USING_USB) && defined(RT_USING_USB_DEVICE)
static const uint8_t USB_SPEED[4] = {
[DSTAT_EM_HS_PHY_30MHZ_60MHZ] = (uint8_t)USB_SPEED_HIGH,
[DSTAT_EM_FS_PHY_30MHZ_60MHZ] = (uint8_t)USB_SPEED_FULL,
[DSTAT_EM_FS_PHY_48MHZ] = (uint8_t)USB_SPEED_FULL,
[DSTAT_EM_LS_PHY_6MHZ] = (uint8_t)USB_SPEED_LOW
};
/* local function prototypes ('static') */
static uint32_t usbd_int_epout(usb_core_driver *udev);
static uint32_t usbd_int_epin(usb_core_driver *udev);
static uint32_t usbd_int_rxfifo(usb_core_driver *udev);
static uint32_t usbd_int_reset(usb_core_driver *udev);
static uint32_t usbd_int_enumfinish(usb_core_driver *udev);
static uint32_t usbd_int_suspend(usb_core_driver *udev);
static uint32_t usbd_int_wakeup(usb_core_driver *udev);
#if (1U == LPM_ENABLED)
static uint32_t usbd_int_lpm(usb_core_driver *udev, usb_lpm_type active_type);
#endif /* LPM_ENABLED */
static uint32_t usbd_emptytxfifo_write(usb_core_driver *udev, uint32_t ep_num);
/*!
\brief USB device-mode interrupts global service routine handler
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
void usbd_isr(usb_core_driver *udev)
{
if (HOST_MODE != (udev->regs.gr->GINTF & GINTF_COPM))
{
uint32_t intr = udev->regs.gr->GINTF;
intr &= udev->regs.gr->GINTEN;
/* there are no interrupts, avoid spurious interrupt */
if (!intr)
{
return;
}
/* OUT endpoints interrupts */
if (intr & GINTF_OEPIF)
{
(void)usbd_int_epout(udev);
}
/* IN endpoints interrupts */
if (intr & GINTF_IEPIF)
{
(void)usbd_int_epin(udev);
}
/* suspend interrupt */
if (intr & GINTF_SP)
{
(void)usbd_int_suspend(udev);
}
/* wakeup interrupt */
if (intr & GINTF_WKUPIF)
{
(void)usbd_int_wakeup(udev);
}
/* start of frame interrupt */
if (intr & GINTF_SOF)
{
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_SOF;
}
/* receive FIFO not empty interrupt */
if (intr & GINTF_RXFNEIF)
{
(void)usbd_int_rxfifo(udev);
}
/* USB reset interrupt */
if (intr & GINTF_RST)
{
(void)usbd_int_reset(udev);
}
/* enumeration has been done interrupt */
if (intr & GINTF_ENUMFIF)
{
(void)usbd_int_enumfinish(udev);
}
/* incomplete synchronization IN transfer interrupt*/
if (intr & GINTF_ISOINCIF)
{
if (NULL != udev->dev.class_core->incomplete_isoc_in)
{
(void)udev->dev.class_core->incomplete_isoc_in(udev);
}
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_ISOINCIF;
}
/* incomplete synchronization OUT transfer interrupt*/
if (intr & GINTF_ISOONCIF)
{
if (NULL != udev->dev.class_core->incomplete_isoc_out)
{
(void)udev->dev.class_core->incomplete_isoc_out(udev);
}
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_ISOONCIF;
}
#if (1U == LPM_ENABLED)
if (intr & GINTF_LPMIF)
{
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_LPMIF;
udev->dev.pm.BESL = (udev->regs.gr->GLPMCFG & GLPMCFG_BESL) >> 2U;
udev->dev.pm.dev_remote_wakeup = (udev->regs.gr->GLPMCFG & GLPMCFG_REW) >> 6U;
if (LPM_L0 == udev->dev.pm.lpm_state)
{
udev->dev.pm.lpm_state = LPM_L1;
usbd_int_lpm(udev, LPM_L1_ACTIVE);
}
else
{
udev->dev.pm.lpm_state = LPM_L0;
usbd_int_lpm(udev, LPM_L0_ACTIVE);
}
}
#endif /* LPM_ENABLED */
#ifdef VBUS_SENSING_ENABLED
/* session request interrupt */
if (intr & GINTF_SESIF)
{
udev->regs.gr->GINTF = GINTF_SESIF;
}
/* OTG mode interrupt */
if (intr & GINTF_OTGIF)
{
if (udev->regs.gr->GOTGINTF & GOTGINTF_SESEND)
{
}
/* clear OTG interrupt */
udev->regs.gr->GINTF = GINTF_OTGIF;
}
#endif /* VBUS_SENSING_ENABLED */
}
}
/*!
\brief indicates that an OUT endpoint has a pending interrupt
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
static uint32_t usbd_int_epout(usb_core_driver *udev)
{
uint32_t epintnum = 0U;
uint8_t ep_num = 0U;
for (epintnum = usb_oepintnum_read(udev); epintnum; epintnum >>= 1)
{
if (epintnum & 0x01U)
{
__IO uint32_t oepintr = usb_oepintr_read(udev, ep_num);
/* transfer complete interrupt */
if (oepintr & DOEPINTF_TF)
{
/* clear the bit in DOEPINTF for this interrupt */
udev->regs.er_out[ep_num]->DOEPINTF = DOEPINTF_TF;
if ((uint8_t)USB_USE_DMA == udev->bp.transfer_mode)
{
__IO uint32_t eplen = udev->regs.er_out[ep_num]->DOEPLEN;
udev->dev.transc_out[ep_num].xfer_count = udev->dev.transc_out[ep_num].max_len -
(eplen & DEPLEN_TLEN);
}
/* inform upper layer: data ready */
usbd_data_out_stage_callback(ep_num, udev->dev.transc_out[ep_num].xfer_len);
if ((uint8_t)USB_USE_DMA == udev->bp.transfer_mode)
{
if ((0U == ep_num) && ((uint8_t)USB_CTL_STATUS_OUT == udev->dev.control.ctl_state))
{
usb_ctlep_startout(udev);
}
}
}
/* SETUP phase finished interrupt (control endpoints) */
if (oepintr & DOEPINTF_STPF)
{
/* inform the upper layer that a SETUP packet is available */
usbd_setup_stage_callback((struct urequest *)&udev->dev.control.req);
udev->regs.er_out[ep_num]->DOEPINTF = DOEPINTF_STPF;
}
}
ep_num++;
}
return 1U;
}
/*!
\brief indicates that an IN endpoint has a pending interrupt
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
static uint32_t usbd_int_epin(usb_core_driver *udev)
{
uint32_t epintnum = 0U;
uint8_t ep_num = 0U;
for (epintnum = usb_iepintnum_read(udev); epintnum; epintnum >>= 1)
{
if (epintnum & 0x1U)
{
__IO uint32_t iepintr = usb_iepintr_read(udev, ep_num);
if (iepintr & DIEPINTF_TF)
{
udev->regs.er_in[ep_num]->DIEPINTF = DIEPINTF_TF;
/* data transmission is completed */
usbd_data_in_stage_callback(ep_num, udev->dev.transc_in[ep_num].xfer_len);
if ((uint8_t)USB_USE_DMA == udev->bp.transfer_mode)
{
if ((0U == ep_num) && ((uint8_t)USB_CTL_STATUS_IN == udev->dev.control.ctl_state))
{
usb_ctlep_startout(udev);
}
}
}
if (iepintr & DIEPINTF_TXFE)
{
usbd_emptytxfifo_write(udev, (uint32_t)ep_num);
udev->regs.er_in[ep_num]->DIEPINTF = DIEPINTF_TXFE;
}
}
ep_num++;
}
return 1U;
}
/*!
\brief handle the RX status queue level interrupt
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
static uint32_t usbd_int_rxfifo(usb_core_driver *udev)
{
usb_transc *transc = NULL;
uint8_t data_PID = 0U;
uint32_t bcount = 0U;
__IO uint32_t devrxstat = 0U;
/* disable the RX status queue non-empty interrupt */
udev->regs.gr->GINTEN &= ~GINTEN_RXFNEIE;
/* get the status from the top of the FIFO */
devrxstat = udev->regs.gr->GRSTATP;
uint8_t ep_num = (uint8_t)(devrxstat & GRSTATRP_EPNUM);
transc = &udev->dev.transc_out[ep_num];
bcount = (devrxstat & GRSTATRP_BCOUNT) >> 4;
data_PID = (uint8_t)((devrxstat & GRSTATRP_DPID) >> 15);
switch ((devrxstat & GRSTATRP_RPCKST) >> 17)
{
case RSTAT_GOUT_NAK:
break;
case RSTAT_DATA_UPDT:
if (bcount > 0U)
{
(void)usb_rxfifo_read(&udev->regs, transc->xfer_buf, (uint16_t)bcount);
transc->xfer_buf += bcount;
transc->xfer_count += bcount;
}
break;
case RSTAT_XFER_COMP:
/* trigger the OUT endpoint interrupt */
break;
case RSTAT_SETUP_COMP:
/* trigger the OUT endpoint interrupt */
break;
case RSTAT_SETUP_UPDT:
if ((0U == transc->ep_addr.num) && (8U == bcount) && (DPID_DATA0 == data_PID))
{
/* copy the SETUP packet received in FIFO into the setup buffer in RAM */
(void)usb_rxfifo_read(&udev->regs, (uint8_t *)&udev->dev.control.req, (uint16_t)bcount);
transc->xfer_count += bcount;
}
break;
default:
break;
}
/* enable the RX status queue level interrupt */
udev->regs.gr->GINTEN |= GINTEN_RXFNEIE;
return 1U;
}
/*!
\brief handle USB reset interrupt
\param[in] udev: pointer to USB device instance
\param[out] none
\retval status
*/
static uint32_t usbd_int_reset(usb_core_driver *udev)
{
uint32_t i;
/* clear the remote wakeup signaling */
udev->regs.dr->DCTL &= ~DCTL_RWKUP;
/* flush the TX FIFO */
(void)usb_txfifo_flush(&udev->regs, 0U);
for (i = 0U; i < udev->bp.num_ep; i++)
{
udev->regs.er_in[i]->DIEPINTF = 0xFFU;
udev->regs.er_out[i]->DOEPINTF = 0xFFU;
}
/* clear all pending device endpoint interrupts */
udev->regs.dr->DAEPINT = 0xFFFFFFFFU;
/* enable endpoint 0 interrupts */
udev->regs.dr->DAEPINTEN = 1U | (1U << 16U);
/* enable OUT endpoint interrupts */
udev->regs.dr->DOEPINTEN = DOEPINTEN_STPFEN | DOEPINTEN_TFEN;
#ifdef USB_DEDICATED_EP1_ENABLED
udev->regs.dr->DOEP1INTEN = DOEPINTEN_STPFEN | DOEPINTEN_TFEN;
#endif
/* enable IN endpoint interrupts */
udev->regs.dr->DIEPINTEN = DIEPINTEN_TFEN;
#ifdef USB_DEDICATED_EP1_ENABLED
udev->regs.dr->DIEP1INTEN = DIEPINTEN_TFEN;
#endif
/* reset device address */
udev->regs.dr->DCFG &= ~DCFG_DAR;
/* configure endpoint 0 to receive SETUP packets */
usb_ctlep_startout(udev);
/* clear USB reset interrupt */
udev->regs.gr->GINTF = GINTF_RST;
usb_ep_active(udev, 0x00, USB_FS_EP0_MAX_LEN, USB_EPTYPE_CTRL);
usb_ep_active(udev, 0x80, USB_FS_EP0_MAX_LEN, USB_EPTYPE_CTRL);
usbd_reset_callback();
return 1U;
}
/*!
\brief handle USB speed enumeration finish interrupt
\param[in] udev: pointer to USB device instance
\param[out] none
\retval status
*/
static uint32_t usbd_int_enumfinish(usb_core_driver *udev)
{
uint8_t enum_speed = (uint8_t)((udev->regs.dr->DSTAT & DSTAT_ES) >> 1);
udev->regs.dr->DCTL &= ~DCTL_CGINAK;
udev->regs.dr->DCTL |= DCTL_CGINAK;
udev->regs.gr->GUSBCS &= ~GUSBCS_UTT;
/* set USB turn-around time based on device speed and PHY interface */
if ((uint8_t)USB_SPEED_HIGH == USB_SPEED[enum_speed])
{
udev->bp.core_speed = (uint8_t)USB_SPEED_HIGH;
udev->regs.gr->GUSBCS |= 0x09U << 10;
}
else
{
udev->bp.core_speed = (uint8_t)USB_SPEED_FULL;
udev->regs.gr->GUSBCS |= 0x05U << 10;
}
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_ENUMFIF;
return 1U;
}
/*!
\brief USB suspend interrupt handler
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
static uint32_t usbd_int_suspend(usb_core_driver *udev)
{
__IO uint8_t low_power = udev->bp.low_power;
__IO uint8_t suspend = (uint8_t)(udev->regs.dr->DSTAT & DSTAT_SPST);
udev->dev.backup_status = udev->dev.cur_status;
if (low_power && suspend)
{
/* switch-off the USB clocks */
*udev->regs.PWRCLKCTL |= PWRCLKCTL_SUCLK | PWRCLKCTL_SHCLK;
/* enter DEEP_SLEEP mode with LDO in low power mode */
pmu_to_deepsleepmode(WFI_CMD);
}
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_SP;
return 1U;
}
/*!
\brief USB wakeup interrupt handler
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
static uint32_t usbd_int_wakeup(usb_core_driver *udev)
{
__IO uint8_t low_power = udev->bp.low_power;
__IO uint8_t remote_wakeup = udev->dev.pm.dev_remote_wakeup;
#if (1U == LPM_ENABLED)
if (LPM_L1 == udev->dev.pm.lpm_state)
{
udev->dev.pm.lpm_state = LPM_L0;
usbd_int_lpm(udev, LPM_L0_ACTIVE);
}
else
#endif /* LPM_ENABLED */
{
if (remote_wakeup && low_power)
{
/* resume MCU CLK */
/* reset SLEEPDEEP bit of Cortex-M33 system control register */
SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
}
usb_clock_active(udev);
/* inform upper layer by the resume event */
udev->dev.cur_status = udev->dev.backup_status;
}
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_WKUPIF;
return 1U;
}
#ifdef USB_DEDICATED_EP1_ENABLED
/*!
\brief USB dedicated OUT endpoint 1 interrupt service routine handler
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
uint32_t usbd_int_dedicated_ep1out(usb_core_driver *udev)
{
uint32_t oepintr = 0U;
uint32_t oeplen = 0U;
oepintr = udev->regs.er_out[1]->DOEPINTF;
oepintr &= udev->regs.dr->DOEP1INTEN;
/* transfer complete */
if (oepintr & DOEPINTF_TF)
{
/* clear the bit in DOEPINTn for this interrupt */
udev->regs.er_out[1]->DOEPINTF = DOEPINTF_TF;
if (USB_USE_DMA == udev->bp.transfer_mode)
{
oeplen = udev->regs.er_out[1]->DOEPLEN;
/* ToDo : handle more than one single MPS size packet */
udev->dev.transc_out[1].xfer_count = udev->dev.transc_out[1].max_len -
(oeplen & DEPLEN_TLEN);
}
/* RX COMPLETE */
usbd_out_transc(udev, 1U);
}
return 1U;
}
/*!
\brief USB dedicated IN endpoint 1 interrupt service routine handler
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
uint32_t usbd_int_dedicated_ep1in(usb_core_driver *udev)
{
uint32_t inten, intr, emptyen;
inten = udev->regs.dr->DIEP1INTEN;
emptyen = udev->regs.dr->DIEPFEINTEN;
inten |= ((emptyen >> 1U) & 0x1U) << 7U;
intr = udev->regs.er_in[1]->DIEPINTF & inten;
if (intr & DIEPINTF_TF)
{
udev->regs.dr->DIEPFEINTEN &= ~(0x1U << 1U);
udev->regs.er_in[1]->DIEPINTF = DIEPINTF_TF;
/* TX COMPLETE */
usbd_in_transc(udev, 1U);
}
if (intr & DIEPINTF_TXFE)
{
usbd_emptytxfifo_write(udev, 1U);
udev->regs.er_in[1]->DIEPINTF = DIEPINTF_TXFE;
}
return 1U;
}
#endif
#if (1U == LPM_ENABLED)
/*!
\brief USB LPM interrupt handler
\param[in] udev: pointer to USB device instance
\param[in] active_type: active type
only one parameter can be selected which is shown as below:
\arg LPM_L0_ACTIVE: select L0 to active
\arg LPM_L1_ACTIVE: select L1 to active
\param[out] none
\retval operation status
*/
static uint32_t usbd_int_lpm(usb_core_driver *udev, usb_lpm_type active_type)
{
__IO uint8_t low_power = udev->bp.low_power;
__IO uint8_t suspend = (uint8_t)(udev->regs.dr->DSTAT & DSTAT_SPST);
__IO uint8_t is_configured = (udev->dev.cur_status == (uint8_t)USBD_CONFIGURED) ? 1U : 0U;
switch (active_type)
{
case LPM_L0_ACTIVE:
udev->dev.cur_status = udev->dev.backup_status;
/* switch-on the OTG clocks */
usb_clock_active(udev);
if (low_power)
{
/* resume MCU CLK */
/* reset SLEEPDEEP bit of Cortex-M7 system control register */
SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
}
break;
case LPM_L1_ACTIVE:
udev->dev.backup_status = udev->dev.cur_status;
udev->dev.cur_status = (uint8_t)USBD_SUSPENDED;
/* add delay */
usb_mdelay(300U);
/* switch-off the OTG clocks */
*udev->regs.PWRCLKCTL |= PWRCLKCTL_SUCLK | PWRCLKCTL_SHCLK;
if (low_power)
{
/* enter DEEP_SLEEP mode with LDO in low power mode */
pmu_to_deepsleepmode(WFI_CMD);
}
break;
}
return 1U;
}
#endif /* LPM_ENABLED */
/*!
\brief check FIFO for the next packet to be loaded
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint identifier which is in (0..5)
\param[out] none
\retval status
*/
static uint32_t usbd_emptytxfifo_write(usb_core_driver *udev, uint32_t ep_num)
{
uint32_t len;
uint32_t word_count;
usb_transc *transc = &udev->dev.transc_in[ep_num];
len = transc->xfer_len - transc->xfer_count;
/* get the data length to write */
if (len > transc->max_len)
{
len = transc->max_len;
}
word_count = (len + 3U) / 4U;
while (((udev->regs.er_in[ep_num]->DIEPTFSTAT & DIEPTFSTAT_IEPTFS) >= word_count) &&
(transc->xfer_count < transc->xfer_len))
{
len = transc->xfer_len - transc->xfer_count;
if (len > transc->max_len)
{
len = transc->max_len;
}
/* write FIFO in word(4bytes) */
word_count = (len + 3U) / 4U;
/* write the FIFO */
(void)usb_txfifo_write(&udev->regs, transc->xfer_buf, (uint8_t)ep_num, (uint16_t)len);
transc->xfer_buf += len;
transc->xfer_count += len;
if (transc->xfer_count == transc->xfer_len)
{
/* disable the device endpoint FIFO empty interrupt */
udev->regs.dr->DIEPFEINTEN &= ~(0x01U << ep_num);
}
}
return 1U;
}
#endif /* BSP_USING_USB && RT_USING_USB_DEVICE */
+693
View File
@@ -0,0 +1,693 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-11-07 RealThread the first version
*/
#include "drv_usbh_int.h"
#include "drv_otghs_host.h"
#if defined(__CC_ARM) /*!< ARM compiler */
#pragma O0
#elif defined(__GNUC__) /*!< GNU compiler */
#pragma GCC optimize("O0")
#elif defined(__TASKING__) /*!< TASKING compiler */
#pragma optimize = 0
#endif /* __CC_ARM */
#if defined(BSP_USING_USB) && defined(RT_USING_USB_HOST)
/* local function prototypes ('static') */
static uint32_t usbh_int_port(usb_core_driver *udev);
static uint32_t usbh_int_pipe(usb_core_driver *udev);
static uint32_t usbh_int_pipe_in(usb_core_driver *udev, uint32_t pp_num);
static uint32_t usbh_int_pipe_out(usb_core_driver *udev, uint32_t pp_num);
static uint32_t usbh_int_rxfifonoempty(usb_core_driver *udev);
static uint32_t usbh_int_txfifoempty(usb_core_driver *udev, usb_pipe_mode pp_mode);
/*!
\brief handle global host interrupt
\param[in] udev: pointer to USB core instance
\param[out] none
\retval operation status
*/
uint32_t usbh_isr(usb_core_driver *udev)
{
uint32_t retval = 0U;
__IO uint32_t intr = 0U;
/* check if host mode */
if (HOST_MODE == (udev->regs.gr->GINTF & GINTF_COPM))
{
intr = usb_coreintr_get(&udev->regs);
if (!intr)
{
return 0U;
}
if (intr & GINTF_SOF)
{
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_SOF;
}
if (intr & GINTF_RXFNEIF)
{
retval |= usbh_int_rxfifonoempty(udev);
}
if (intr & GINTF_NPTXFEIF)
{
retval |= usbh_int_txfifoempty(udev, PIPE_NON_PERIOD);
}
if (intr & GINTF_PTXFEIF)
{
retval |= usbh_int_txfifoempty(udev, PIPE_PERIOD);
}
if (intr & GINTF_HCIF)
{
retval |= usbh_int_pipe(udev);
}
if (intr & GINTF_HPIF)
{
retval |= usbh_int_port(udev);
}
if (intr & GINTF_WKUPIF)
{
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_WKUPIF;
}
if (intr & GINTF_DISCIF)
{
if ((*udev->regs.HPCS & HPCS_PCST) == 0)
{
usbh_disconnect_callback(udev);
}
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_DISCIF;
}
if (intr & GINTF_ISOONCIF)
{
udev->regs.pr[0]->HCHCTL |= HCHCTL_CEN | HCHCTL_CDIS;
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_ISOONCIF;
}
}
return retval;
}
/*!
\brief handle USB pipe halt
\param[in] udev: pointer to USB core instance
\param[in] pp_num: pp_num: host channel number which is in (0..7)
\param[in] pp_int: pipe interrupt
\param[in] pp_status: pipe status
\param[out] none
\retval none
*/
static inline void usb_pp_halt(usb_core_driver *udev,
uint8_t pp_num,
uint32_t pp_int,
usb_pipe_status pp_status)
{
udev->regs.pr[pp_num]->HCHINTEN |= HCHINTEN_CHIE;
usb_pipe_halt(udev, pp_num);
udev->regs.pr[pp_num]->HCHINTF = pp_int;
udev->host.pipe[pp_num].pp_status = pp_status;
}
/*!
\brief handle the host port interrupt
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
#if defined(__ICCARM__) /*!< IAR compiler */
#pragma optimize = none
#endif /* __ICCARM */
static uint32_t usbh_int_port(usb_core_driver *udev)
{
uint32_t retval = 0U;
__IO uint32_t port_state = *udev->regs.HPCS;
__IO uint32_t port_reset = 0U;
/* clear the interrupt bit in GINTF */
port_state &= ~(HPCS_PE | HPCS_PCD | HPCS_PEDC);
/* port connect detected */
if (*udev->regs.HPCS & HPCS_PCD)
{
port_state |= HPCS_PCD;
if ((*udev->regs.HPCS & HPCS_PCST) == HPCS_PCST)
{
usbh_connect_callback(udev);
}
retval |= 1U;
}
/* port enable changed */
if (*udev->regs.HPCS & HPCS_PEDC)
{
port_state |= HPCS_PEDC;
if (*udev->regs.HPCS & HPCS_PE)
{
uint32_t port_speed = usb_curspeed_get(udev);
udev->host.connect_status = 1U;
if (PORT_SPEED_LOW == port_speed)
{
udev->regs.hr->HFT = 6000U;
}
else if (PORT_SPEED_FULL == port_speed)
{
udev->regs.hr->HFT = 48000U;
}
else
{
/* no operation */
}
port_reset = 1U;
udev->regs.gr->GINTEN |= GINTEN_DISCIE | GINTEN_SOFIE;
}
else
{
/* no operation */
}
}
if (1U == port_reset)
{
usb_port_reset(udev);
}
/* clear port interrupts */
*udev->regs.HPCS = port_state;
return retval;
}
/*!
\brief handle all host channels interrupt
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
static uint32_t usbh_int_pipe(usb_core_driver *udev)
{
uint32_t pp_num = 0U;
uint32_t retval = 0U;
for (pp_num = 0U; pp_num < udev->bp.num_pipe; pp_num++)
{
if ((udev->regs.hr->HACHINT & HACHINT_HACHINT) & (1UL << pp_num))
{
if (udev->regs.pr[pp_num]->HCHCTL & HCHCTL_EPDIR)
{
retval |= usbh_int_pipe_in(udev, pp_num);
}
else
{
retval |= usbh_int_pipe_out(udev, pp_num);
}
}
}
return retval;
}
/*!
\brief handle the IN channel interrupt
\param[in] udev: pointer to USB device instance
\param[in] pp_num: host channel number which is in (0..7)
\param[out] none
\retval operation status
*/
#if defined(__ICCARM__) /*!< IAR compiler */
#pragma optimize = none
#endif /* __ICCARM */
static uint32_t usbh_int_pipe_in(usb_core_driver *udev, uint32_t pp_num)
{
usb_pr *pp_reg = udev->regs.pr[pp_num];
usb_pipe *pp = &udev->host.pipe[pp_num];
uint32_t intr_pp = pp_reg->HCHINTF;
intr_pp &= pp_reg->HCHINTEN;
uint8_t ep_type = (uint8_t)((pp_reg->HCHCTL & HCHCTL_EPTYPE) >> 18);
if (intr_pp & HCHINTF_ACK)
{
pp_reg->HCHINTF = HCHINTF_ACK;
}
else if (intr_pp & HCHINTF_STALL)
{
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_STALL, PIPE_STALL);
pp_reg->HCHINTF = HCHINTF_NAK;
/* note: When there is a 'STALL', reset also NAK,
else, the udev->host.pp_status = HC_STALL
will be overwritten by 'NAK' in code below */
intr_pp &= ~HCHINTF_NAK;
}
else if (intr_pp & HCHINTF_DTER)
{
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_DTER, PIPE_DTGERR);
pp_reg->HCHINTF = HCHINTF_NAK;
}
else
{
/* no operation */
}
if (intr_pp & HCHINTF_REQOVR)
{
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_REQOVR, PIPE_REQOVR);
}
else if (intr_pp & HCHINTF_TF)
{
if ((uint8_t)USB_USE_DMA == udev->bp.transfer_mode)
{
udev->host.backup_xfercount[pp_num] = pp->xfer_len - (pp_reg->HCHLEN & HCHLEN_TLEN);
}
pp->pp_status = PIPE_XF;
pp->err_count = 0U;
pp_reg->HCHINTF = HCHINTF_TF;
switch (ep_type)
{
case USB_EPTYPE_CTRL:
case USB_EPTYPE_BULK:
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_NAK, PIPE_XF);
pp->data_toggle_in ^= 1U;
break;
case USB_EPTYPE_INTR:
case USB_EPTYPE_ISOC:
pp_reg->HCHCTL |= HCHCTL_ODDFRM;
pp->urb_state = URB_DONE;
#ifdef USBH_USE_RTOS
usbh_hc_notifyurbchange_callback(udev, pp_num, pp->urb_state);
#endif /* USBH_USE_RTOS */
break;
default:
break;
}
}
else if (intr_pp & HCHINTF_CH)
{
pp_reg->HCHINTEN &= ~HCHINTEN_CHIE;
switch (pp->pp_status)
{
case PIPE_XF:
pp->urb_state = URB_DONE;
break;
case PIPE_STALL:
pp->urb_state = URB_STALL;
break;
case PIPE_TRACERR:
case PIPE_DTGERR:
pp->err_count = 0U;
pp->urb_state = URB_ERROR;
pp->data_toggle_in ^= 1U;
break;
case PIPE_IDLE:
case PIPE_HALTED:
case PIPE_NAK:
case PIPE_NYET:
case PIPE_BBERR:
case PIPE_REQOVR:
default:
if ((uint8_t)USB_EPTYPE_INTR == ep_type)
{
pp->data_toggle_in ^= 1U;
}
break;
}
#ifdef USBH_USE_RTOS
usbh_hc_notifyurbchange_callback(udev, pp_num, pp->urb_state);
#endif /* USBH_USE_RTOS */
pp_reg->HCHINTF = HCHINTF_CH;
}
else if (intr_pp & HCHINTF_USBER)
{
pp->err_count++;
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_USBER, PIPE_TRACERR);
}
else if (intr_pp & HCHINTF_NAK)
{
pp_reg->HCHINTF = HCHINTF_NAK;
switch (ep_type)
{
case USB_EPTYPE_CTRL:
case USB_EPTYPE_BULK:
/* re-activate the channel */
pp_reg->HCHCTL = (pp_reg->HCHCTL | HCHCTL_CEN) & ~HCHCTL_CDIS;
break;
case USB_EPTYPE_INTR:
pp_reg->HCHINTEN |= HCHINTEN_CHIE;
(void)usb_pipe_halt(udev, (uint8_t)pp_num);
break;
default:
break;
}
pp->pp_status = PIPE_NAK;
}
else
{
/* no operation */
}
return 1U;
}
/*!
\brief handle the OUT channel interrupt
\param[in] udev: pointer to USB device instance
\param[in] pp_num: host channel number which is in (0..7)
\param[out] none
\retval operation status
*/
#if defined(__ICCARM__) /*!< IAR compiler */
#pragma optimize = none
#endif /* __ICCARM */
static uint32_t usbh_int_pipe_out(usb_core_driver *udev, uint32_t pp_num)
{
usb_pr *pp_reg = udev->regs.pr[pp_num];
usb_pipe *pp = &udev->host.pipe[pp_num];
uint32_t intr_pp = pp_reg->HCHINTF;
intr_pp &= pp_reg->HCHINTEN;
if (intr_pp & HCHINTF_ACK)
{
if (1U == udev->host.pipe[pp_num].do_ping)
{
udev->host.pipe[pp_num].do_ping = 0U;
pp->err_count = 0U;
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_ACK, PIPE_NAK);
}
pp_reg->HCHINTF = HCHINTF_ACK;
}
else if (intr_pp & HCHINTF_STALL)
{
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_STALL, PIPE_STALL);
}
else if (intr_pp & HCHINTF_DTER)
{
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_DTER, PIPE_DTGERR);
pp_reg->HCHINTF = HCHINTF_NAK;
}
else if (intr_pp & HCHINTF_REQOVR)
{
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_REQOVR, PIPE_REQOVR);
}
else if (intr_pp & HCHINTF_TF)
{
pp->err_count = 0U;
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_TF, PIPE_XF);
}
else if (intr_pp & HCHINTF_NAK)
{
if (0U == udev->host.pipe[pp_num].do_ping)
{
if (1U == udev->host.pipe[pp_num].supp_ping)
{
udev->host.pipe[pp_num].do_ping = 1U;
}
}
pp->err_count = 0U;
if (USB_USE_FIFO == udev->bp.transfer_mode)
{
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_NAK, PIPE_NAK);
}
else
{
pp_reg->HCHINTF = HCHINTF_NAK;
}
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_NAK, PIPE_NAK);
}
else if (intr_pp & HCHINTF_USBER)
{
pp->err_count++;
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_USBER, PIPE_TRACERR);
}
else if (intr_pp & HCHINTF_NYET)
{
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_NYET, PIPE_NYET);
pp->err_count = 0U;
}
else if (intr_pp & HCHINTF_CH)
{
udev->regs.pr[pp_num]->HCHINTEN &= ~HCHINTEN_CHIE;
switch (pp->pp_status)
{
case PIPE_XF:
pp->urb_state = URB_DONE;
if ((uint8_t)USB_EPTYPE_BULK == ((pp_reg->HCHCTL & HCHCTL_EPTYPE) >> 18U))
{
pp->data_toggle_out ^= 1U;
}
break;
case PIPE_NAK:
pp->urb_state = URB_NOTREADY;
break;
case PIPE_NYET:
pp->urb_state = URB_DONE;
if ((uint8_t)USB_EPTYPE_BULK == ((pp_reg->HCHCTL & HCHCTL_EPTYPE) >> 18U))
{
pp->data_toggle_out ^= 1U;
}
break;
case PIPE_STALL:
pp->urb_state = URB_STALL;
break;
case PIPE_TRACERR:
if (3U == pp->err_count)
{
pp->urb_state = URB_ERROR;
pp->err_count = 0U;
}
break;
case PIPE_IDLE:
case PIPE_HALTED:
case PIPE_BBERR:
case PIPE_REQOVR:
case PIPE_DTGERR:
default:
break;
}
#ifdef USBH_USE_RTOS
usbh_hc_notifyurbchange_callback(udev, pp_num, pp->urb_state);
#endif /* USBH_USE_RTOS */
pp_reg->HCHINTF = HCHINTF_CH;
}
else
{
/* no operation */
}
return 1U;
}
/*!
\brief handle the RX FIFO non-empty interrupt
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
#if defined(__ICCARM__) /*!< IAR compiler */
#pragma optimize = none
#endif /* __ICCARM */
static uint32_t usbh_int_rxfifonoempty(usb_core_driver *udev)
{
uint32_t count = 0U, xfer_count = 0U;
__IO uint8_t pp_num = 0U;
__IO uint32_t rx_stat = 0U;
/* disable the RX status queue level interrupt */
udev->regs.gr->GINTEN &= ~GINTEN_RXFNEIE;
rx_stat = udev->regs.gr->GRSTATP;
pp_num = (uint8_t)(rx_stat & GRSTATRP_CNUM);
switch ((rx_stat & GRSTATRP_RPCKST) >> 17)
{
case GRXSTS_PKTSTS_IN:
count = (rx_stat & GRSTATRP_BCOUNT) >> 4;
/* read the data into the host buffer. */
if ((NULL != udev->host.pipe[pp_num].xfer_buf) && (count > 0U))
{
(void)usb_rxfifo_read(&udev->regs, udev->host.pipe[pp_num].xfer_buf, (uint16_t)count);
/* manage multiple transfer packet */
udev->host.pipe[pp_num].xfer_buf += count;
udev->host.pipe[pp_num].xfer_count += count;
xfer_count = udev->host.pipe[pp_num].xfer_count;
udev->host.backup_xfercount[pp_num] = xfer_count;
if (udev->regs.pr[pp_num]->HCHLEN & HCHLEN_PCNT)
{
/* re-activate the channel when more packets are expected */
uint32_t pp_ctl = udev->regs.pr[pp_num]->HCHCTL;
pp_ctl |= HCHCTL_CEN;
pp_ctl &= ~HCHCTL_CDIS;
udev->regs.pr[pp_num]->HCHCTL = pp_ctl;
}
}
break;
case GRXSTS_PKTSTS_IN_XFER_COMP:
break;
case GRXSTS_PKTSTS_DATA_TOGGLE_ERR:
count = (rx_stat & GRSTATRP_BCOUNT) >> 4;
while (count > 0U)
{
rx_stat = udev->regs.gr->GRSTATP;
count--;
}
break;
case GRXSTS_PKTSTS_CH_HALTED:
break;
default:
break;
}
/* enable the RX status queue level interrupt */
udev->regs.gr->GINTEN |= GINTEN_RXFNEIE;
return 1U;
}
/*!
\brief handle the TX FIFO empty interrupt
\param[in] udev: pointer to USB device instance
\param[in] pp_mode: pipe mode
\param[out] none
\retval operation status
*/
#if defined(__ICCARM__) /*!< IAR compiler */
#pragma optimize = none
#endif /* __ICCARM */
static uint32_t usbh_int_txfifoempty(usb_core_driver *udev, usb_pipe_mode pp_mode)
{
uint8_t pp_num = 0U;
uint16_t word_count = 0U, len = 0U;
__IO uint32_t *txfiforeg = 0U, txfifostate = 0U;
if (PIPE_NON_PERIOD == pp_mode)
{
txfiforeg = &udev->regs.gr->HNPTFQSTAT;
}
else if (PIPE_PERIOD == pp_mode)
{
txfiforeg = &udev->regs.hr->HPTFQSTAT;
}
else
{
return 0U;
}
txfifostate = *txfiforeg;
pp_num = (uint8_t)((txfifostate & TFQSTAT_CNUM) >> 27);
word_count = (uint16_t)(udev->host.pipe[pp_num].xfer_len + 3U) / 4U;
while (((txfifostate & TFQSTAT_TXFS) >= word_count) && (0U != udev->host.pipe[pp_num].xfer_len))
{
len = (uint16_t)(txfifostate & TFQSTAT_TXFS) * 4U;
if (len > udev->host.pipe[pp_num].xfer_len)
{
/* last packet */
len = (uint16_t)udev->host.pipe[pp_num].xfer_len;
if (PIPE_NON_PERIOD == pp_mode)
{
udev->regs.gr->GINTEN &= ~GINTEN_NPTXFEIE;
}
else
{
udev->regs.gr->GINTEN &= ~GINTEN_PTXFEIE;
}
}
word_count = (uint16_t)((udev->host.pipe[pp_num].xfer_len + 3U) / 4U);
usb_txfifo_write(&udev->regs, udev->host.pipe[pp_num].xfer_buf, pp_num, len);
udev->host.pipe[pp_num].xfer_buf += len;
udev->host.pipe[pp_num].xfer_len -= len;
udev->host.pipe[pp_num].xfer_count += len;
txfifostate = *txfiforeg;
}
return 1U;
}
#endif /* BSP_USING_USB && RT_USING_USB_HOST */
+178
View File
@@ -0,0 +1,178 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-11-07 RealThread the first version
*/
#include "drv_usbh_pipe.h"
#if defined(BSP_USING_USB) && defined(RT_USING_USB_HOST)
static uint16_t usbh_freepipe_get(usb_core_driver *pudev);
/*!
\brief create a pipe
\param[in] pudev: pointer to usb core instance
\param[in] pp_num: pipe number
\param[in] udev: USB device
\param[in] ep_type: endpoint type
\param[in] ep_mpl: endpoint max packet length
\param[out] none
\retval operation status
*/
uint8_t usbh_pipe_create(usb_core_driver *pudev,
uint8_t dev_addr,
uint8_t dev_speed,
uint8_t pp_num,
uint8_t ep_num,
uint8_t ep_type,
uint16_t ep_mpl)
{
usb_pipe *pp = &pudev->host.pipe[pp_num];
pp->dev_addr = dev_addr;
pp->dev_speed = dev_speed;
pp->ep.num = ep_num & 0x7FU;
pp->ep.type = ep_type;
pp->ep.mps = ep_mpl;
if (((USB_EPTYPE_BULK == pp->ep.type) || (USB_EPTYPE_CTRL == pp->ep.type)))
{
pp->supp_ping = (uint8_t)(pp->dev_speed == PORT_SPEED_HIGH);
}
if (0x80U == (ep_num & 0x80U))
{
pp->ep.dir = 1;
}
else
{
pp->ep.dir = 0;
}
usb_pipe_init(pudev, pp_num);
return HC_OK;
}
/*!
\brief modify a pipe
\param[in] pudev: pointer to usb core instance
\param[in] pp_num: pipe number
\param[in] dev_addr: device address
\param[in] dev_speed: device speed
\param[in] ep_type: endpoint type
\param[in] ep_mpl: endpoint max packet length
\param[out] none
\retval operation status
*/
uint8_t usbh_pipe_update(usb_core_driver *pudev,
uint8_t pp_num,
uint8_t dev_addr,
uint32_t dev_speed,
uint16_t ep_mpl)
{
usb_pipe *pp = &pudev->host.pipe[pp_num];
if ((pp->dev_addr != dev_addr) && (dev_addr))
{
pp->dev_addr = dev_addr;
}
if ((pp->dev_speed != dev_speed) && (dev_speed))
{
pp->dev_speed = dev_speed;
}
if ((pp->ep.mps != ep_mpl) && (ep_mpl))
{
pp->ep.mps = ep_mpl;
}
usb_pipe_init(pudev, pp_num);
return HC_OK;
}
/*!
\brief allocate a new pipe
\param[in] pudev: pointer to usb core instance
\param[in] ep_addr: endpoint address
\param[out] none
\retval operation status
*/
uint8_t usbh_pipe_allocate(usb_core_driver *pudev, uint8_t ep_addr)
{
uint16_t pp_num = usbh_freepipe_get(pudev);
if (HC_ERROR != pp_num)
{
pudev->host.pipe[pp_num].in_used = 1U;
pudev->host.pipe[pp_num].ep.dir = EP_DIR(ep_addr);
pudev->host.pipe[pp_num].ep.num = EP_ID(ep_addr);
}
return pp_num;
}
/*!
\brief free a pipe
\param[in] pudev: pointer to usb core instance
\param[in] pp_num: pipe number
\param[out] none
\retval operation status
*/
uint8_t usbh_pipe_free(usb_core_driver *pudev, uint8_t pp_num)
{
if (pp_num < HC_MAX)
{
pudev->host.pipe[pp_num].in_used = 0U;
}
return USBH_OK;
}
/*!
\brief delete all USB host pipe
\param[in] pudev: pointer to usb core instance
\param[out] none
\retval operation status
*/
uint8_t usbh_pipe_delete(usb_core_driver *pudev)
{
uint8_t pp_num = 0U;
for (pp_num = 2U; pp_num < HC_MAX; pp_num++)
{
pudev->host.pipe[pp_num] = (usb_pipe){ 0 };
}
return USBH_OK;
}
/*!
\brief get a free pipe number for allocation
\param[in] pudev: pointer to usb core instance
\param[out] none
\retval operation status
*/
static uint16_t usbh_freepipe_get(usb_core_driver *pudev)
{
uint8_t pp_num = 0U;
for (pp_num = 0U; pp_num < HC_MAX; pp_num++)
{
if (pudev->host.pipe[pp_num].in_used == 0U)
{
return pp_num;
}
}
return HC_ERROR;
}
#endif /* BSP_USING_USB && RT_USING_USB_HOST */
+147
View File
@@ -0,0 +1,147 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-11-07 RealThread the first version
*/
#include "drv_usbh_transc.h"
#if defined(BSP_USING_USB) && defined(RT_USING_USB_HOST)
#define USB_SETUP_PACKET_LEN 8U
/*!
\brief prepare a pipe and start a transfer
\param[in] pudev: pointer to usb core instance
\param[in] pp_num: pipe number
\param[out] none
\retval operation status
*/
static uint32_t usbh_request_submit(usb_core_driver *pudev, uint8_t pp_num)
{
pudev->host.pipe[pp_num].urb_state = URB_IDLE;
pudev->host.pipe[pp_num].xfer_count = 0U;
return usb_pipe_xfer(pudev, pp_num);
}
/*!
\brief send the setup packet to the USB device
\param[in] pudev: pointer to usb core instance
\param[in] buf: data buffer which will be sent to USB device
\param[in] pp_num: pipe number
\param[out] none
\retval operation status
*/
usbh_status usbh_ctlsetup_send(usb_core_driver *pudev, uint8_t *buf, uint8_t pp_num)
{
usb_pipe *pp = &pudev->host.pipe[pp_num];
pp->DPID = PIPE_DPID_SETUP;
pp->xfer_buf = buf;
pp->xfer_len = USB_SETUP_PACKET_LEN;
return (usbh_status)usbh_request_submit(pudev, pp_num);
}
/*!
\brief send a data packet to the USB device
\param[in] pudev: pointer to usb core instance
\param[in] buf: data buffer which will be sent to USB device
\param[in] pp_num: pipe number
\param[in] len: length of the data to be sent
\param[out] none
\retval operation status
*/
usbh_status usbh_data_send(usb_core_driver *pudev, uint8_t *buf, uint8_t pp_num, uint16_t len)
{
usb_pipe *pp = &pudev->host.pipe[pp_num];
pp->xfer_buf = buf;
pp->xfer_len = len;
switch (pp->ep.type)
{
case USB_EPTYPE_CTRL:
if (0U == len)
{
pp->data_toggle_out = 1U;
}
pp->DPID = PIPE_DPID[pp->data_toggle_out];
break;
case USB_EPTYPE_INTR:
pp->DPID = PIPE_DPID[pp->data_toggle_out];
pp->data_toggle_out ^= 1U;
break;
case USB_EPTYPE_BULK:
pp->DPID = PIPE_DPID[pp->data_toggle_out];
break;
case USB_EPTYPE_ISOC:
pp->DPID = PIPE_DPID[0];
break;
default:
break;
}
usbh_request_submit(pudev, pp_num);
return USBH_OK;
}
/*!
\brief receive a data packet from the USB device
\param[in] pudev: pointer to usb core instance
\param[in] buf: data buffer which will be received from USB device
\param[in] pp_num: pipe number
\param[in] len: length of the data to be received
\param[out] none
\retval operation status
*/
usbh_status usbh_data_recev(usb_core_driver *pudev, uint8_t *buf, uint8_t pp_num, uint16_t len)
{
usb_pipe *pp = &pudev->host.pipe[pp_num];
pp->xfer_buf = buf;
pp->xfer_len = len;
switch (pp->ep.type)
{
case USB_EPTYPE_CTRL:
pp->DPID = PIPE_DPID[1];
break;
case USB_EPTYPE_INTR:
pp->DPID = PIPE_DPID[pp->data_toggle_in];
/* Toggle DATA PID */
pp->data_toggle_in ^= 1U;
break;
case USB_EPTYPE_BULK:
pp->DPID = PIPE_DPID[pp->data_toggle_in];
break;
case USB_EPTYPE_ISOC:
pp->DPID = PIPE_DPID[0];
break;
default:
break;
}
usbh_request_submit(pudev, pp_num);
return USBH_OK;
}
#endif /* BSP_USING_USB && RT_USING_USB_HOST */
+733
View File
@@ -0,0 +1,733 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-11-07 RealThread the first version
*/
#include "drv_usb_hw.h"
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#if defined(BSP_USING_USB)
#define TIM_MSEC_DELAY 0x01U
#define TIM_USEC_DELAY 0x02U
#if defined(RT_USING_USB_HOST)
#ifndef USE_ULPI_PHY
#ifdef USE_USBHS0
#if defined(SOC_SERIES_GD32H75E)
#define HOST_POWERSW_PORT_RCC RCU_GPIOG
#define HOST_POWERSW_PORT GPIOG
#define HOST_POWERSW_VBUS GPIO_PIN_8
#elif defined(SOC_SERIES_GD32H7xx)
#warning "Please refer to the relevant sections in the chip manual to redefine the pins."
#define HOST_POWERSW_PORT_RCC RCU_GPIOB
#define HOST_POWERSW_PORT GPIOB
#define HOST_POWERSW_VBUS GPIO_PIN_13
#endif
#endif
#ifdef USE_USBHS1
#define HOST_POWERSW_PORT_RCC RCU_GPIOG
#define HOST_POWERSW_PORT GPIOG
#define HOST_POWERSW_VBUS GPIO_PIN_7
#endif
#else
#ifdef USE_USBHS0
#define HOST_POWERSW_PORT_RCC RCU_GPIOC
#define HOST_POWERSW_PORT GPIOC
#define HOST_POWERSW_VBUS GPIO_PIN_7
#endif
#ifdef USE_USBHS1
#define HOST_POWERSW_PORT_RCC RCU_GPIOC
#define HOST_POWERSW_PORT GPIOC
#define HOST_POWERSW_VBUS GPIO_PIN_9
#endif
#endif
#define HOST_SOF_OUTPUT_RCC RCC_APB2PERIPH_GPIOA
#define HOST_SOF_PORT GPIOA
#define HOST_SOF_SIGNAL GPIO_PIN_8
#endif
__IO uint32_t delay_time = 0U;
__IO uint16_t timer_prescaler = 23U;
/* local function prototypes ('static') */
static void hw_time_set(uint8_t unit);
static void hw_delay(uint32_t ntime, uint8_t unit);
/*!
\brief configure USB GPIO
\param[in] none
\param[out] none
\retval none
*/
void usb_gpio_config(void)
{
#ifdef USE_USBHS0
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_GPIOC);
/* ULPI_STP(PC0) GPIO pin configuration */
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_0);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_0);
/* ULPI_CK(PA5) GPIO pin configuration */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_5);
/* ULPI_NXT(PC3) GPIO pin configuration */
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_3);
/* ULPI_DIR(PC2) GPIO pin configuration */
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_2);
/* ULPI_D1(PB0), ULPI_D2(PB1), ULPI_D3(PB10), ULPI_D4(PB11) \
ULPI_D5(PB12), ULPI_D6(PB13) and ULPI_D7(PB5) GPIO pin configuration */
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE,
GPIO_PIN_0 |
GPIO_PIN_1 | GPIO_PIN_10 | GPIO_PIN_11);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ,
GPIO_PIN_12 |
GPIO_PIN_13 | GPIO_PIN_5);
/* ULPI_D0(PA3) GPIO pin configuration */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_3);
gpio_af_set(GPIOA, GPIO_AF_10, GPIO_PIN_5);
gpio_af_set(GPIOB, GPIO_AF_10, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_5 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13);
gpio_af_set(GPIOC, GPIO_AF_10, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3);
#endif /* USE_USBHS0 */
#ifdef USE_USBHS1
rcu_periph_clock_enable(RCU_GPIOH);
rcu_periph_clock_enable(RCU_GPIOG);
/* ULPI_STP(PH2) GPIO pin configuration */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_2);
/* ULPI_CK(PH5) GPIO pin configuration */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_5);
/* ULPI_NXT(PH4) GPIO pin configuration */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_4);
/* ULPI_DIR(PH3) GPIO pin configuration */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_3);
/* ULPI_D0(PH6) GPIO pin configuration */
/* ULPI_D1(PH7), ULPI_D2(PH8), ULPI_D3(PH9), ULPI_D4(PH10) \
ULPI_D5(PH11), ULPI_D6(PH12) and ULPI_D7(PG5) GPIO pin configuration */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE,
GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 |
GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ,
GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 |
GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_5);
gpio_af_set(GPIOG, GPIO_AF_8, GPIO_PIN_5);
gpio_af_set(GPIOH, GPIO_AF_8, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12);
#endif /* USE_USBHS1 */
}
extern usb_core_driver gd_usb_core;
#ifdef USE_USBHS0
/*!
\brief this function handles USBHS0 interrupt
\param[in] none
\param[out] none
\retval none
*/
void USBHS0_IRQHandler(void)
{
rt_interrupt_enter();
#if defined(RT_USING_USB_DEVICE)
usbd_isr(&gd_usb_core);
#else
usbh_isr(&gd_usb_core);
#endif
rt_interrupt_leave();
}
#endif /* USE_USBHS0 */
#ifdef USE_USBHS1
/*!
\brief this function handles USBHS1 interrupt
\param[in] none
\param[out] none
\retval none
*/
void USBHS1_IRQHandler(void)
{
rt_interrupt_enter();
#if defined(RT_USING_USB_DEVICE)
usbd_isr(&gd_usb_core);
#else
usbh_isr(&gd_usb_core);
#endif
rt_interrupt_leave();
}
#endif /* USE_USBHS1 */
/*!
\brief resume MCU clock
\param[in] none
\param[out] none
\retval none
*/
static void resume_mcu_clk(void)
{
/* enable HXTAL */
rcu_osci_on(RCU_HXTAL);
/* wait till HXTAL is ready */
while (RESET == rcu_flag_get(RCU_FLAG_HXTALSTB))
{
}
/* enable PLL */
rcu_osci_on(RCU_PLL0_CK);
/* wait till PLL is ready */
while (RESET == rcu_flag_get(RCU_FLAG_PLL0STB))
{
}
/* select PLL as system clock source */
rcu_system_clock_source_config(RCU_CKSYSSRC_PLL0P);
/* wait till PLL is used as system clock source */
while (RCU_SCSS_PLL0P != rcu_system_clock_source_get())
{
}
}
#ifdef USE_USBHS0
/*!
\brief this function handles USBHS0 wakeup interrupt request
\param[in] none
\param[out] none
\retval none
*/
void USBHS0_WKUP_IRQHandler(void)
{
rt_interrupt_enter();
if (gd_usb_core.bp.low_power)
{
resume_mcu_clk();
#ifndef USE_IRC48M
rcu_usb48m_clock_config(IDX_USBHS0, RCU_USB48MSRC_PLL0R);
#else
/* enable IRC48M clock */
rcu_osci_on(RCU_IRC48M);
/* wait till IRC48M is ready */
while (SUCCESS != rcu_osci_stab_wait(RCU_IRC48M))
{
}
rcu_ck48m_clock_config(RCU_CK48MSRC_IRC48M);
#endif /* USE_IRC48M */
rcu_periph_clock_enable(RCU_USBHS0);
usb_clock_active(&gd_usb_core);
}
exti_interrupt_flag_clear(EXTI_31);
rt_interrupt_leave();
}
#endif /* USE_USBHS0 */
#ifdef USE_USBHS1
/*!
\brief this function handles USBHS1 wakeup interrupt request
\param[in] none
\param[out] none
\retval none
*/
void USBHS1_WKUP_IRQHandler(void)
{
rt_interrupt_enter();
if (gd_usb_core.bp.low_power)
{
resume_mcu_clk();
#ifndef USE_IRC48M
rcu_usb48m_clock_config(IDX_USBHS0, RCU_USB48MSRC_PLL0R);
#else
/* enable IRC48M clock */
rcu_osci_on(RCU_IRC48M);
/* wait till IRC48M is ready */
while (SUCCESS != rcu_osci_stab_wait(RCU_IRC48M))
{
}
rcu_ck48m_clock_config(RCU_CK48MSRC_IRC48M);
#endif /* USE_IRC48M */
rcu_periph_clock_enable(RCU_USBHS1);
usb_clock_active(&gd_usb_core);
}
exti_interrupt_flag_clear(EXTI_32);
rt_interrupt_leave();
}
#endif /* USE_USBHS1 */
#ifdef USB_DEDICATED_EP1_ENABLED
#ifdef USE_USBHS0
/*!
\brief this function handles USBHS0 dedicated endpoint 1 OUT interrupt request.
\param[in] none
\param[out] none
\retval none
*/
void USBHS0_EP1_OUT_IRQHandler(void)
{
usbd_int_dedicated_ep1out(&cdc_acm);
}
#endif /* USE_USBHS0 */
#ifdef USE_USBHS1
/*!
\brief this function handles USBHS1 dedicated endpoint 1 OUT interrupt request.
\param[in] none
\param[out] none
\retval none
*/
void USBHS1_EP1_OUT_IRQHandler(void)
{
usbd_int_dedicated_ep1out(&cdc_acm);
}
#endif /* USE_USBHS1 */
#ifdef USE_USBHS0
/*!
\brief this function handles USBHS0 dedicated endpoint 1 IN interrupt request
\param[in] none
\param[out] none
\retval none
*/
void USBHS0_EP1_IN_IRQHandler(void)
{
usbd_int_dedicated_ep1in(&cdc_acm);
}
#endif /* USE_USBHS0 */
#ifdef USE_USBHS1
/*!
\brief this function handles USBHS1 dedicated endpoint 1 IN interrupt request
\param[in] none
\param[out] none
\retval none
*/
void USBHS1_EP1_IN_IRQHandler(void)
{
usbd_int_dedicated_ep1in(&cdc_acm);
}
#endif /* USE_USBHS1 */
#endif /* USB_DEDICATED_EP1_ENABLED */
/*!
\brief configure USB clock
\param[in] none
\param[out] none
\retval none
*/
void usb_rcu_config(void)
{
pmu_usb_regulator_enable();
pmu_usb_voltage_detector_enable();
while (SET != pmu_flag_get(PMU_FLAG_USB33RF))
{
}
#ifdef USE_USB_FS
#ifndef USE_IRC48M
/* configure the pll1 input and output clock range */
rcu_pll_input_output_clock_range_config(IDX_PLL1, RCU_PLL1RNG_4M_8M, RCU_PLL1VCO_192M_836M);
rcu_pll1_config(5, 96, 2, 10, 2);
/* enable PLL1Q clock output */
rcu_pll_clock_output_enable(RCU_PLL1Q);
rcu_osci_on(RCU_PLL1_CK);
#ifdef USE_USBHS0
rcu_usbhs_pll1qpsc_config(IDX_USBHS0, RCU_USBHSPSC_DIV1);
rcu_usb48m_clock_config(IDX_USBHS0, RCU_USB48MSRC_PLL1Q);
#endif /* USE_USBHS0 */
#ifdef USE_USBHS1
rcu_usbhs_pll1qpsc_config(IDX_USBHS1, RCU_USBHSPSC_DIV1);
rcu_usb48m_clock_config(IDX_USBHS1, RCU_USB48MSRC_PLL1Q);
#endif /* USE_USBHS1 */
#else
/* enable IRC48M clock */
rcu_osci_on(RCU_IRC48M);
/* wait till IRC48M is ready */
while (SUCCESS != rcu_osci_stab_wait(RCU_IRC48M))
{
}
rcu_ck48m_clock_config(RCU_CK48MSRC_IRC48M);
#ifdef USE_USBHS0
rcu_usb48m_clock_config(IDX_USBHS0, RCU_USB48MSRC_IRC48M);
#endif /* USE_USBHS0 */
#ifdef USE_USBHS1
rcu_usb48m_clock_config(IDX_USBHS1, RCU_USB48MSRC_IRC48M);
#endif /* USE_USBHS1 */
#endif /* USE_IRC48M */
#endif /* USE_USB_FS */
#ifdef USE_USBHS0
rcu_periph_clock_enable(RCU_USBHS0);
#endif /* USE_USBHS0 */
#ifdef USE_USBHS1
rcu_periph_clock_enable(RCU_USBHS1);
#endif /* USE_USBHS1 */
#ifdef USE_ULPI_PHY
#ifdef USE_USBHS0
rcu_periph_clock_enable(RCU_USBHS0ULPI);
#endif
#ifdef USE_USBHS1
rcu_periph_clock_enable(RCU_USBHS1ULPI);
#endif
#endif /* USE_ULPI_PHY */
}
/*!
\brief configure USB interrupt
\param[in] none
\param[out] none
\retval none
*/
void usb_intr_config(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
#ifdef USE_USBHS0
nvic_irq_enable((uint8_t)USBHS0_IRQn, 3U, 0U);
#endif /* USE_USBHS0 */
#ifdef USE_USBHS1
nvic_irq_enable((uint8_t)USBHS1_IRQn, 3U, 0U);
#endif /* USE_USBHS0 */
/* enable the power module clock */
rcu_periph_clock_enable(RCU_PMU);
#ifdef USE_USBHS0
/* USB wakeup EXTI line configuration */
exti_interrupt_flag_clear(EXTI_31);
exti_init(EXTI_31, EXTI_INTERRUPT, EXTI_TRIG_RISING);
exti_interrupt_enable(EXTI_31);
nvic_irq_enable((uint8_t)USBHS0_WKUP_IRQn, 1U, 0U);
#endif /* USE_USBHS0 */
#ifdef USE_USBHS1
/* USB wakeup EXTI line configuration */
exti_interrupt_flag_clear(EXTI_32);
exti_init(EXTI_32, EXTI_INTERRUPT, EXTI_TRIG_RISING);
exti_interrupt_enable(EXTI_32);
nvic_irq_enable((uint8_t)USBHS1_WKUP_IRQn, 1U, 0U);
#endif /* USE_USBHS1 */
#ifdef USB_DEDICATED_EP1_ENABLED
#ifdef USE_USBHS0
nvic_irq_enable((uint8_t)USBHS0_EP1_OUT_IRQn, 1U, 0U);
nvic_irq_enable((uint8_t)USBHS0_EP1_IN_IRQn, 1U, 0U);
#endif /* USE_USBHS0 */
#ifdef USE_USBHS1
nvic_irq_enable((uint8_t)USBHS1_EP1_OUT_IRQn, 1U, 0U);
nvic_irq_enable((uint8_t)USBHS1_EP1_IN_IRQn, 1U, 0U);
#endif /* USE_USBHS1 */
#endif /* USB_DEDICATED_EP1_ENABLED */
}
#if defined(RT_USING_USB_HOST)
/*!
\brief drives the VBUS signal through GPIO
\param[in] state: VBUS states
\param[out] none
\retval none
*/
void usb_vbus_drive(uint8_t state)
{
if (0U == state)
{
/* disable is needed on output of the power switch */
gpio_bit_reset(HOST_POWERSW_PORT, HOST_POWERSW_VBUS);
}
else
{
/* enable the power switch by driving the enable high */
gpio_bit_set(HOST_POWERSW_PORT, HOST_POWERSW_VBUS);
}
}
/*!
\brief configures the GPIO for the VBUS
\param[in] none
\param[out] none
\retval none
*/
void usb_vbus_config(void)
{
rcu_periph_clock_enable(HOST_POWERSW_PORT_RCC);
/* USBFS_VBUS_CTRL(PD13) GPIO pin configuration */
gpio_mode_set(HOST_POWERSW_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, HOST_POWERSW_VBUS);
gpio_output_options_set(HOST_POWERSW_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, HOST_POWERSW_VBUS);
/* by default, disable is needed on output of the power switch */
usb_vbus_drive(0U);
/* delay is need for stabilizing the VBUS low in reset condition,
* when VBUS = 1 and reset-button is pressed by user
*/
usb_mdelay(200);
}
#endif
#if defined(BSP_USING_TIMER2) && defined(BSP_USING_USB)
#error "Timer2 has been occupied by USB. Please select another timer resource."
#endif
/*!
\brief initializes delay unit using Timer2
\param[in] none
\param[out] none
\retval none
*/
void usb_timer_init(void)
{
/* configure the priority group to 2 bits */
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
/* enable the TIM2 global interrupt */
nvic_irq_enable((uint8_t)TIMER2_IRQn, 1U, 0U);
rcu_periph_clock_enable(RCU_TIMER2);
}
/*!
\brief delay in microseconds
\param[in] usec: value of delay required in microseconds
\param[out] none
\retval none
*/
void usb_udelay(const uint32_t usec)
{
hw_delay(usec, TIM_USEC_DELAY);
}
/*!
\brief delay in milliseconds
\param[in] msec: value of delay required in milliseconds
\param[out] none
\retval none
*/
void usb_mdelay(const uint32_t msec)
{
hw_delay(msec, TIM_MSEC_DELAY);
}
/*!
\brief time base IRQ
\param[in] none
\param[out] none
\retval none
*/
void usb_timer_irq(void)
{
if (RESET != timer_interrupt_flag_get(TIMER2, TIMER_INT_UP))
{
timer_interrupt_flag_clear(TIMER2, TIMER_INT_UP);
if (delay_time > 0x00U)
{
delay_time--;
}
else
{
timer_disable(TIMER2);
}
}
}
void TIMER2_IRQHandler(void)
{
rt_interrupt_enter();
usb_timer_irq();
rt_interrupt_leave();
}
/*!
\brief delay routine based on TIMER2
\param[in] ntime: delay Time
\param[in] unit: delay Time unit = miliseconds / microseconds
\param[out] none
\retval none
*/
static void hw_delay(uint32_t ntime, uint8_t unit)
{
delay_time = ntime;
hw_time_set(unit);
while (0U != delay_time)
{
}
timer_disable(TIMER2);
}
/*!
\brief configures TIMER2 for delay routine based on TIMER2
\param[in] unit: msec /usec
\param[out] none
\retval none
*/
static void hw_time_set(uint8_t unit)
{
timer_parameter_struct timer_basestructure;
timer_disable(TIMER2);
timer_interrupt_disable(TIMER2, TIMER_INT_UP);
if (TIM_USEC_DELAY == unit)
{
timer_basestructure.period = 9U;
}
else if (TIM_MSEC_DELAY == unit)
{
timer_basestructure.period = 9999U;
}
else
{
/* no operation */
}
timer_basestructure.prescaler = timer_prescaler;
timer_basestructure.alignedmode = TIMER_COUNTER_EDGE;
timer_basestructure.counterdirection = TIMER_COUNTER_UP;
timer_basestructure.clockdivision = TIMER_CKDIV_DIV1;
timer_basestructure.repetitioncounter = 0U;
timer_init(TIMER2, &timer_basestructure);
timer_interrupt_flag_clear(TIMER2, TIMER_INT_UP);
timer_auto_reload_shadow_enable(TIMER2);
/* TIMER IT enable */
timer_interrupt_enable(TIMER2, TIMER_INT_UP);
/* TIMER2 enable counter */
timer_enable(TIMER2);
}
/*!
\brief configure the PLL of USB
\param[in] usb_periph: USBHS0 or USBHS1
\param[out] none
\retval none
*/
void pllusb_rcu_config(uint32_t usb_periph)
{
if (USBHS0 == usb_periph)
{
rcu_pllusb0_config(RCU_PLLUSBHSPRE_HXTAL, RCU_PLLUSBHSPRE_DIV5, RCU_PLLUSBHS_MUL96, RCU_USBHS_DIV8);
RCU_ADDCTL1 |= RCU_ADDCTL1_PLLUSBHS0EN;
while (0U == (RCU_ADDCTL1 & RCU_ADDCTL1_PLLUSBHS0STB))
{
}
rcu_usbhs_clock_selection_enable(IDX_USBHS0);
rcu_usb48m_clock_config(IDX_USBHS0, RCU_USB48MSRC_PLLUSBHS);
rcu_usbhs_clock_config(IDX_USBHS0, RCU_USBHSSEL_60M);
}
else
{
rcu_pllusb1_config(RCU_PLLUSBHSPRE_HXTAL, RCU_PLLUSBHSPRE_DIV5, RCU_PLLUSBHS_MUL96, RCU_USBHS_DIV8);
RCU_ADDCTL1 |= RCU_ADDCTL1_PLLUSBHS1EN;
while (0U == (RCU_ADDCTL1 & RCU_ADDCTL1_PLLUSBHS1STB))
{
}
rcu_usbhs_clock_selection_enable(IDX_USBHS1);
rcu_usb48m_clock_config(IDX_USBHS1, RCU_USB48MSRC_PLLUSBHS);
rcu_usbhs_clock_config(IDX_USBHS1, RCU_USBHSSEL_60M);
}
}
#endif /* BSP_USING_USB */