/* * 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 */