828eco基于GD32H7mcu
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.
 
 
 
 
 

693 lines
17 KiB

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