/*
 * SPDX-FileCopyrightText: Copyright (c) 2023 Ha Thach (tinyusb.org)
 * SPDX-License-Identifier: MIT
 *
 * This file is part of the TinyUSB stack.
 */

#include "tusb_option.h"

#if defined(TUP_USBIP_RUSB2) && (CFG_TUH_ENABLED || CFG_TUD_ENABLED)
  #include "osal/osal.h"
  #include "common/tusb_fifo.h"

  #include "rusb2_common.h"

  #if TU_CHECK_MCU(OPT_MCU_RAXXX)
    #include "rusb2_ra.h"

// USBFS_INT_IRQn and USBHS_USB_INT_RESUME_IRQn are generated by FSP
rusb2_controller_t rusb2_controller[] = {
  {.reg_base = R_USB_FS0_BASE, .irqnum = USBFS_INT_IRQn},
    #ifdef RUSB2_SUPPORT_HIGHSPEED
  {.reg_base = R_USB_HS0_BASE, .irqnum = USBHS_USB_INT_RESUME_IRQn},
    #endif
};

// Application API for setting IRQ number. May throw warnings for missing prototypes.
void tusb_rusb2_set_irqnum(uint8_t rhport, int32_t irqnum);
void tusb_rusb2_set_irqnum(uint8_t rhport, int32_t irqnum) {
  rusb2_controller[rhport].irqnum = irqnum;
}
  #endif

//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
static void hwfifo_set_mbw(rusb2_reg_t *rusb, uintptr_t hwfifo, uint16_t mbw) {
  volatile uint16_t *fifo_sel;
  if (hwfifo == (uintptr_t)&rusb->CFIFO) {
    fifo_sel = &rusb->CFIFOSEL;
  } else if (hwfifo == (uintptr_t)&rusb->D0FIFO) {
    fifo_sel = &rusb->D0FIFOSEL;
  } else if (hwfifo == (uintptr_t)&rusb->D1FIFO) {
    fifo_sel = &rusb->D1FIFOSEL;
  } else {
    return;
  }

  *fifo_sel = (*fifo_sel & ~RUSB2_CFIFOSEL_MBW_Msk) | mbw;
}

// write to hwfifo from buffer with access mode
void tu_hwfifo_write(volatile void *hwfifo, const uint8_t *src, uint16_t len, const tu_hwfifo_access_t *access_mode) {
  rusb2_reg_t   *rusb = (rusb2_reg_t *)access_mode->param;
  const uint8_t *buf8 = (const uint8_t *)src;

  volatile uint16_t *ff16;
  volatile uint8_t  *ff8;
  const bool         is_highspeed = rusb2_is_highspeed_reg(rusb);
  if (is_highspeed) {
    ff16 = (volatile uint16_t *)((uintptr_t)hwfifo + 2);
    ff8  = (volatile uint8_t *)((uintptr_t)hwfifo + 3);
  } else {
    ff16 = (volatile uint16_t *)hwfifo;
    ff8  = ((volatile uint8_t *)hwfifo);
  }

  // 32-bit access for highspeed
  if (is_highspeed) {
    volatile uint32_t *ff32 = (volatile uint32_t *)hwfifo;
    while (len >= 4) {
      *ff32 = tu_unaligned_read32(buf8);
      buf8 += 4;
      len -= 4;
    }

    if (len >= 2) {
      // switch to 16-bit access
      hwfifo_set_mbw(rusb, (uintptr_t)hwfifo, RUSB2_FIFOSEL_MBW_16BIT);
    }
  }

  // 16-bit access
  while (len >= 2) {
    *ff16 = tu_unaligned_read16(buf8);
    buf8 += 2;
    len -= 2;
  }

  // 8-bit access does not need to change MBW
  if (len > 0) {
    *ff8 = *buf8;
    ++buf8;
  }
}

#endif
