You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
756 lines
18 KiB
756 lines
18 KiB
|
5 months ago
|
/*
|
||
|
|
* 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 */
|