/*******************************************************************************
* DISCLAIMER
* This software is supplied by Renesas Electronics Corporation and is only
* intended for use with Renesas products. No other uses are authorized. This
* software is owned by Renesas Electronics Corporation and is protected under
* all applicable laws, including copyright laws.
* THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
* THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT
* LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.
* TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS
* ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE
* FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR
* ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
* Renesas reserves the right, without notice, to make changes to this software
* and to discontinue the availability of this software. By using this software,
* you agree to the additional terms and conditions found by accessing the
* following link:
* http://www.renesas.com/disclaimer
* Copyright (C) 2014 Renesas Electronics Corporation. All rights reserved.
*******************************************************************************/
/*******************************************************************************
* File Name    : usb0_userdef.c
* $Rev: 682 $
* $Date:: 2014-01-23 09:15:07 +0900#$
* Device(s)    :
* Tool-Chain   :
* OS           : None
* H/W Platform :
* Description  : USB Sample Program
* Operation    :
* Limitations  :
*******************************************************************************/


/*******************************************************************************
Includes   <System Includes> , "Project Includes"
*******************************************************************************/
#include <stdio.h>
#include "r_typedefs.h"
#include "iodefine.h"
#include "rza_io_regrw.h"
#include "usb_hdrv_rza1.h"
#include "usb_haudio_rza1.h"
#include "usb0_host_dmacdrv.h"


/*******************************************************************************
Typedef definitions
*******************************************************************************/


/*******************************************************************************
Macro definitions
*******************************************************************************/
#define DMAC_LINK_COUNT          (USB_HAUDIO_LINK_COUNT*8)
#define DMAC_LINK_CHANGE         USB_HAUDIO_LINK_CHANGE

//#define    CLK_USB_EXTAL    /* if defined, USB_EXTAL is enable             */
#define USBHCLOCK_X1_48MHZ       (0x0000u)
#define USBHCLOCK_EXTAL_12MHZ    (0x0004u)
#define USB_HOST_BITUCKSEL       (0x0004u)    /* b2: Select Clock USB_X1 or EXTAL */
#define USB_HOST_BITUPLLE        (0x0002u)    /* b1: USB PLL Enable               */

#define DUMMY_ACCESS      (*(volatile uint16_t *)(&USB200.SYSCFG0))


/*******************************************************************************
Imported global variables and functions (from other files)
*******************************************************************************/
extern USB_HAUDIO_t   g_usb0_haudio[ USB_HAUDIO_MAX_DEVADDRESS + 1 ];

/*******************************************************************************
Exported global variables and functions (to be accessed by other files)
*******************************************************************************/
void usb0_make_dmac0_link_table (uint32_t dir, uint8_t *buf_adr, uint32_t size, uint32_t total_size);
void usb0_make_dmac1_link_table (uint32_t dir, uint8_t *buf_adr, uint32_t size, uint32_t total_size);
int32_t usb0_check_interrupt_index_0_iso(int32_t index);
int32_t usb0_check_interrupt_index_1_iso(int32_t index);


/*******************************************************************************
Private global variables and functions
*******************************************************************************/
static void usb0_host_get_dma_adrs(USB_HOST_DMA_t *dma, uint16_t *dfacc, uint32_t *src, uint32_t *dst);

static void usb0_enable_dmac0(uint32_t src, uint32_t dst, uint32_t count,
                              uint32_t size, uint32_t dir, uint32_t fifo, uint16_t dfacc);
static void usb0_enable_dmac0_iso(uint32_t src, uint32_t dst, uint32_t dir);

static void usb0_enable_dmac1(uint32_t src, uint32_t dst, uint32_t count,
                              uint32_t size, uint32_t dir, uint32_t fifo, uint16_t dfacc);
static void usb0_enable_dmac1_iso(uint32_t src, uint32_t dst, uint32_t dir);

static void usb0_userdef_wait_100ns_2(void);
static void usb0_userdef_wait_1us_2(void);

static int32_t  g_usb0_buf_index_0;
static uint8_t  *g_usb0_buf_start_0;
static uint8_t  *g_usb0_buf_end_0;
static uint8_t  *g_usb0_buf_current_0;

static int32_t  g_usb0_buf_index_1;
static uint8_t  *g_usb0_buf_start_1;
static uint8_t  *g_usb0_buf_end_1;
static uint8_t  *g_usb0_buf_current_1;

volatile static int32_t  g_usb0_buf_interrupt_index_0;
volatile static int32_t  g_usb0_buf_interrupt_index_1;

static volatile link_reg_info_t g_usb0_link_register_0[DMAC_LINK_CHANGE][DMAC_LINK_COUNT] __attribute__ ((section ("NC_DATA")));
static volatile link_reg_info_t g_usb0_link_register_1[DMAC_LINK_CHANGE][DMAC_LINK_COUNT] __attribute__ ((section ("NC_DATA")));

static uint32_t g_dmac_link_count;

/*******************************************************************************
* Function Name: usb0_userdef_dma_interrupt_d0fifo_link
* Description  :
* Argument     :
* Return Value :
*******************************************************************************/
void usb0_userdef_dma_interrupt_d0fifo_link(uint16_t dir)
{
    int32_t    link_num;

    g_usb0_buf_interrupt_index_0 = g_usb0_buf_index_0;

    if (dir == USB_FIFO2BUF)
    {
        usb0_host_DMAC1_LinkRegLv(&g_usb0_link_register_0[g_usb0_buf_index_0][0], 0);

        g_usb0_buf_index_0++;
        if( g_usb0_buf_index_0 == DMAC_LINK_CHANGE )
        {
            g_usb0_buf_index_0 = 0;
        }
    }
    else
    {
        /* [n][0] - [n][g_dmac_link_count - 1] */
        for( link_num = 0; link_num < g_dmac_link_count; link_num++ )
        {
            if( g_usb0_buf_current_0 >= g_usb0_buf_end_0 )
            {
                g_usb0_buf_current_0 = g_usb0_buf_start_0;
            }
            g_usb0_link_register_0[g_usb0_buf_index_0][link_num].src_addr = (uint32_t)g_usb0_buf_current_0;
            g_usb0_buf_current_0 += g_usb0_link_register_0[g_usb0_buf_index_0][link_num].trs_count;

            usb0_host_DMAC1_LinkRegLv(&g_usb0_link_register_0[g_usb0_buf_index_0][link_num], 0);
        }

        g_usb0_buf_index_0++;
        if( g_usb0_buf_index_0 == DMAC_LINK_CHANGE )
        {
            g_usb0_buf_index_0 = 0;
        }
    }

    usb0_userdef_os_set_signal(USB_D0FIFO);
}

/*******************************************************************************
* Function Name: usb0_userdef_dma_interrupt_d1fifo_link
* Description  :
* Argument     :
* Return Value :
*******************************************************************************/
void usb0_userdef_dma_interrupt_d1fifo_link(uint16_t dir)
{
    int32_t    link_num;

    g_usb0_buf_interrupt_index_1 = g_usb0_buf_index_1;

    if (dir == USB_FIFO2BUF)
    {
        usb0_host_DMAC2_LinkRegLv(&g_usb0_link_register_1[g_usb0_buf_index_1][0], 0);

        g_usb0_buf_index_1++;
        if( g_usb0_buf_index_1 == DMAC_LINK_CHANGE )
        {
            g_usb0_buf_index_1 = 0;
        }
    }
    else
    {
        /* [n][0] - [0][g_dmac_link_count - 1] */
        for( link_num = 0; link_num < g_dmac_link_count; link_num++ )
        {
            if( g_usb0_buf_current_1 >= g_usb0_buf_end_1 )
            {
                g_usb0_buf_current_1 = g_usb0_buf_start_1;
            }
            g_usb0_link_register_1[g_usb0_buf_index_1][link_num].src_addr = (uint32_t)g_usb0_buf_current_1;
            g_usb0_buf_current_1 += g_usb0_link_register_1[g_usb0_buf_index_1][link_num].trs_count;

            usb0_host_DMAC2_LinkRegLv(&g_usb0_link_register_1[g_usb0_buf_index_1][link_num], 0);
        }

        g_usb0_buf_index_1++;
        if( g_usb0_buf_index_1 == DMAC_LINK_CHANGE )
        {
            g_usb0_buf_index_1 = 0;
        }
    }

    usb0_userdef_os_set_signal(USB_D1FIFO);
}

/*******************************************************************************
* Function Name: usb0_check_interrupt_index_0_iso
* Description  :
* Argument     :
* Return Value :
*******************************************************************************/
int32_t usb0_check_interrupt_index_0_iso(int32_t index)
{
    if( g_usb0_buf_interrupt_index_0 != index )
    {
        return USB_ERROR;
    }
    return USB_SUCCESS;
}

/*******************************************************************************
* Function Name: usb0_check_interrupt_index_1_iso
* Description  :
* Argument     :
* Return Value :
*******************************************************************************/
int32_t usb0_check_interrupt_index_1_iso(int32_t index)
{
    if( g_usb0_buf_interrupt_index_1 != index )
    {
        return USB_ERROR;
    }
    return USB_SUCCESS;
}

/*******************************************************************************
* Function Name: usb0_make_dmac0_link_table
* Description  : Enables DMA transfer on the information specified by the argument.
* Outline      : setting DMA for D0FIFO
* Argument     : uint32_t dir        : direction
*              : uint8_t  *buf_adr   : buffer address
*              : uint32_t size       : buffer size
*              : uint32_t total_size : total size
* Return Value : none
*******************************************************************************/
void usb0_make_dmac0_link_table (uint32_t dir, uint8_t *buf_adr, uint32_t size, uint32_t total_size)
{
    dmac_transinfo_t    trans_info;
    uint32_t            request_factor;
    uint8_t             *buf_adrs_set;
    int32_t             link_num;
    uint32_t            trans_size;
    uint32_t            trans_size_total;
    uint32_t            half_size;
    int32_t             link_change;
    uint32_t            loopcount;

    buf_adrs_set = buf_adr;
    g_dmac_link_count = USB_HAUDIO_LINK_COUNT;
    if (dir == USB_FIFO2BUF)
    {
        g_usb0_buf_interrupt_index_0 = -1;

        request_factor       = DMAC_REQ_USB0_DMA0_RX;         /* USB channel 0 receive D0FIFO full               */

        trans_info.src_size  = DMAC_TRANS_SIZE_32;            /* Transfer source transfer size                   */
        trans_info.dst_size  = DMAC_TRANS_SIZE_32;            /* Transfer destination transfer size              */
        trans_info.saddr_dir = DMAC_TRANS_ADR_NO_INC;         /* Transfer source address counting direction      */
        trans_info.daddr_dir = DMAC_TRANS_ADR_INC;            /* Transfer destination address counting direction */
        trans_info.src_addr  = (uint32_t)(&USB200.D0FIFO.UINT32);

        half_size = (size / DMAC_LINK_CHANGE);

        for( link_change = 0; link_change < (DMAC_LINK_CHANGE - 1); link_change++ )
        {
            trans_info.count    = half_size;                  /* Total transfer byte count                       */
            trans_info.dst_addr = (uint32_t)buf_adrs_set;     /* Start address of transfer destination           */
            usb0_host_DMAC1_LinkRegInit(&trans_info,
                                        request_factor,
                                        &g_usb0_link_register_0[link_change][0],
                                        0,
                                        0,
                                        (uint32_t)&g_usb0_link_register_0[link_change + 1][0]);
            buf_adrs_set += trans_info.count;
        }

        trans_info.count    = half_size;                      /* Total transfer byte count                       */
        trans_info.dst_addr = (uint32_t)buf_adrs_set;         /* Start address of transfer destination           */
        usb0_host_DMAC1_LinkRegInit(&trans_info,
                                    request_factor,
                                    &g_usb0_link_register_0[link_change][0],
                                    0,
                                    0,
                                    (uint32_t)&g_usb0_link_register_0[0][0]);
        buf_adrs_set += trans_info.count;
    }
    else
    {
        for(loopcount = 0;
          (loopcount <= USB_HAUDIO_MAX_DEVADDRESS) && (g_dmac_link_count == USB_HAUDIO_LINK_COUNT);
          loopcount++)
        {
            if(g_usb0_haudio[ loopcount ].StreamOut.FifoPort == USB_D0DMA)
            {
                if( g_usb0_haudio[ loopcount ].audio_dev_status == USB_HAUDIO_DEV_HIGH_ATT )
                {
                    g_dmac_link_count = USB_HAUDIO_LINK_COUNT * 8;
                }
            }
        }
        g_usb0_buf_interrupt_index_0 = -1;

        g_usb0_buf_start_0   = buf_adr;
        g_usb0_buf_end_0     = (buf_adr + size);

        request_factor       = DMAC_REQ_USB0_DMA0_TX;         /* USB channel 0 receive D0FIFO empty              */

        trans_info.src_size  = DMAC_TRANS_SIZE_32;            /* Transfer source transfer size                   */
        trans_info.dst_size  = DMAC_TRANS_SIZE_32;            /* Transfer destination transfer size              */
        trans_info.saddr_dir = DMAC_TRANS_ADR_INC;            /* Transfer source address counting direction      */
        trans_info.daddr_dir = DMAC_TRANS_ADR_NO_INC;         /* Transfer destination address counting direction */
        trans_info.dst_addr  = (uint32_t)(&USB200.D0FIFO.UINT32);

        half_size  = (size / DMAC_LINK_CHANGE);
        trans_size = (half_size / g_dmac_link_count);

        for( link_change = 0; link_change < (DMAC_LINK_CHANGE - 1); link_change++ )
        {
            /* [link_change][0] - [link_change][g_dmac_link_count - 2] */
            trans_size_total = 0;
            for( link_num = 0; link_num < (g_dmac_link_count - 1); link_num++ )
            {
                trans_info.count    = trans_size;             /* Total transfer byte count                       */
                trans_info.src_addr = (uint32_t)buf_adrs_set; /* Start address of transfer source                */
                usb0_host_DMAC1_LinkRegInit(&trans_info,
                                            request_factor,
                                            &g_usb0_link_register_0[link_change][link_num],
                                            0,
                                            1,
                                            (uint32_t)&g_usb0_link_register_0[link_change][link_num + 1]);
                buf_adrs_set     += trans_info.count;
                trans_size_total += trans_info.count;
            }

            /* [link_change][g_dmac_link_count - 1] */
            trans_info.count    = (half_size - trans_size_total);    /* Total transfer byte count                */
            trans_info.src_addr = (uint32_t)buf_adrs_set;            /* Start address of transfer source         */
            usb0_host_DMAC1_LinkRegInit(&trans_info,
                                        request_factor,
                                        &g_usb0_link_register_0[link_change][link_num],
                                        0,
                                        0,
                                        (uint32_t)&g_usb0_link_register_0[link_change + 1][0]);
            buf_adrs_set += trans_info.count;
        }

        /* [DMAC_LINK_CHANGE - 1][0] - [DMAC_LINK_CHANGE - 1][g_dmac_link_count - 2] */
        trans_size_total = 0;
        for( link_num = 0; link_num < (g_dmac_link_count - 1); link_num++ )
        {
            trans_info.count    = trans_size;                 /* Total transfer byte count                       */
            trans_info.src_addr = (uint32_t)buf_adrs_set;     /* Start address of transfer source                */
            usb0_host_DMAC1_LinkRegInit(&trans_info,
                                        request_factor,
                                        &g_usb0_link_register_0[link_change][link_num],
                                        0,
                                        1,
                                        (uint32_t)&g_usb0_link_register_0[link_change][link_num + 1]);
            buf_adrs_set     += trans_info.count;
            trans_size_total += trans_info.count;
        }

        /* [DMAC_LINK_CHANGE - 1][g_dmac_link_count - 1] */
        trans_info.count    = (half_size - trans_size_total); /* Total transfer byte count                       */
        trans_info.src_addr = (uint32_t)buf_adrs_set;         /* Start address of transfer source                */
        usb0_host_DMAC1_LinkRegInit(&trans_info,
                                    request_factor,
                                    &g_usb0_link_register_0[link_change][link_num],
                                    0,
                                    0,
                                    (uint32_t)&g_usb0_link_register_0[0][0]);
        buf_adrs_set += trans_info.count;

        g_usb0_buf_current_0 = buf_adrs_set;
    }

    buf_adr += size;
    if( buf_adrs_set > buf_adr )
    {
        printf("DMAC1 Link Make error!!\n");
    }
}

/*******************************************************************************
* Function Name: usb0_make_dmac1_link_table
* Description  : Enables DMA transfer on the information specified by the argument.
* Outline      : setting DMA for D0FIFO
* Argument     : uint32_t dir        : direction
*              : uint8_t  *buf_adr   : buffer address
*              : uint32_t size       : buffer size
*              : uint32_t total_size : total size
* Return Value : none
*******************************************************************************/
void usb0_make_dmac1_link_table (uint32_t dir, uint8_t *buf_adr, uint32_t size, uint32_t total_size)
{
    dmac_transinfo_t    trans_info;
    uint32_t            request_factor;
    uint8_t             *buf_adrs_set;
    int32_t             link_num;
    uint32_t            trans_size;
    uint32_t            trans_size_total;
    uint32_t            half_size;
    int32_t             link_change;
    uint32_t            loopcount;

    buf_adrs_set = buf_adr;
    g_dmac_link_count = USB_HAUDIO_LINK_COUNT;
    if (dir == USB_FIFO2BUF)
    {
        g_usb0_buf_interrupt_index_1 = -1;

        request_factor       = DMAC_REQ_USB0_DMA1_RX;         /* USB channel 0 receive D1FIFO full               */

        trans_info.src_size  = DMAC_TRANS_SIZE_32;            /* Transfer source transfer size                   */
        trans_info.dst_size  = DMAC_TRANS_SIZE_32;            /* Transfer destination transfer size              */
        trans_info.saddr_dir = DMAC_TRANS_ADR_NO_INC;         /* Transfer source address counting direction      */
        trans_info.daddr_dir = DMAC_TRANS_ADR_INC;            /* Transfer destination address counting direction */
        trans_info.src_addr  = (uint32_t)(&USB200.D1FIFO.UINT32);

        half_size = (size / DMAC_LINK_CHANGE);

        for( link_change = 0; link_change < (DMAC_LINK_CHANGE - 1); link_change++ )
        {
            trans_info.count    = half_size;                  /* Total transfer byte count                       */
            trans_info.dst_addr = (uint32_t)buf_adrs_set;     /* Start address of transfer destination           */
            usb0_host_DMAC2_LinkRegInit(&trans_info,
                                        request_factor,
                                        &g_usb0_link_register_1[link_change][0],
                                        0,
                                        0,
                                        (uint32_t)&g_usb0_link_register_1[link_change + 1][0]);
            buf_adrs_set += trans_info.count;
        }

        trans_info.count    = half_size;                      /* Total transfer byte count                       */
        trans_info.dst_addr = (uint32_t)buf_adrs_set;         /* Start address of transfer destination           */
        usb0_host_DMAC2_LinkRegInit(&trans_info,
                                    request_factor,
                                    &g_usb0_link_register_1[link_change][0],
                                    0,
                                    0,
                                    (uint32_t)&g_usb0_link_register_1[0][0]);
        buf_adrs_set += trans_info.count;
    }
    else
    {
        for(loopcount = 0;
          (loopcount <= USB_HAUDIO_MAX_DEVADDRESS) && (g_dmac_link_count == USB_HAUDIO_LINK_COUNT);
          loopcount++)
        {
            if(g_usb0_haudio[ loopcount ].StreamOut.FifoPort == USB_D1DMA)
            {
                if( g_usb0_haudio[ loopcount ].audio_dev_status == USB_HAUDIO_DEV_HIGH_ATT )
                {
                    g_dmac_link_count = USB_HAUDIO_LINK_COUNT * 8;
                }
            }
        }
        g_usb0_buf_interrupt_index_1 = -1;

        g_usb0_buf_start_1   = buf_adr;
        g_usb0_buf_end_1     = (buf_adr + size);

        request_factor       = DMAC_REQ_USB0_DMA1_TX;         /* USB channel 0 receive D1FIFO empty              */

        trans_info.src_size  = DMAC_TRANS_SIZE_32;            /* Transfer source transfer size                   */
        trans_info.dst_size  = DMAC_TRANS_SIZE_32;            /* Transfer destination transfer size              */
        trans_info.saddr_dir = DMAC_TRANS_ADR_INC;            /* Transfer source address counting direction      */
        trans_info.daddr_dir = DMAC_TRANS_ADR_NO_INC;         /* Transfer destination address counting direction */
        trans_info.dst_addr  = (uint32_t)(&USB200.D1FIFO.UINT32);

        half_size  = (size / DMAC_LINK_CHANGE);
        trans_size = (half_size / g_dmac_link_count);

        for( link_change = 0; link_change < (DMAC_LINK_CHANGE - 1); link_change++ )
        {
            /* [link_change][0] - [link_change][g_dmac_link_count - 2] */
            trans_size_total = 0;
            for( link_num = 0; link_num < (g_dmac_link_count - 1); link_num++ )
            {
                trans_info.count    = trans_size;             /* Total transfer byte count                       */
                trans_info.src_addr = (uint32_t)buf_adrs_set; /* Start address of transfer source                */
                usb0_host_DMAC2_LinkRegInit(&trans_info,
                                            request_factor,
                                            &g_usb0_link_register_1[link_change][link_num],
                                            0,
                                            1,
                                            (uint32_t)&g_usb0_link_register_1[link_change][link_num + 1]);
                buf_adrs_set     += trans_info.count;
                trans_size_total += trans_info.count;
            }

            /* [link_change][g_dmac_link_count - 1] */
            trans_info.count    = (half_size - trans_size_total);    /* Total transfer byte count                */
            trans_info.src_addr = (uint32_t)buf_adrs_set;            /* Start address of transfer source         */
            usb0_host_DMAC2_LinkRegInit(&trans_info,
                                        request_factor,
                                        &g_usb0_link_register_1[link_change][link_num],
                                        0,
                                        0,
                                        (uint32_t)&g_usb0_link_register_1[link_change + 1][0]);
            buf_adrs_set += trans_info.count;
        }

        /* [DMAC_LINK_CHANGE - 1][0] - [DMAC_LINK_CHANGE - 1][g_dmac_link_count - 2] */
        trans_size_total = 0;
        for( link_num = 0; link_num < (g_dmac_link_count - 1); link_num++ )
        {
            trans_info.count    = trans_size;                 /* Total transfer byte count                       */
            trans_info.src_addr = (uint32_t)buf_adrs_set;     /* Start address of transfer source                */
            usb0_host_DMAC2_LinkRegInit(&trans_info,
                                        request_factor,
                                        &g_usb0_link_register_1[link_change][link_num],
                                        0,
                                        1,
                                        (uint32_t)&g_usb0_link_register_1[link_change][link_num + 1]);
            buf_adrs_set     += trans_info.count;
            trans_size_total += trans_info.count;
        }

        /* [DMAC_LINK_CHANGE - 1][g_dmac_link_count - 1] */
        trans_info.count    = (half_size - trans_size_total); /* Total transfer byte count                       */
        trans_info.src_addr = (uint32_t)buf_adrs_set;         /* Start address of transfer source                */
        usb0_host_DMAC2_LinkRegInit(&trans_info,
                                    request_factor,
                                    &g_usb0_link_register_1[link_change][link_num],
                                    0,
                                    0,
                                    (uint32_t)&g_usb0_link_register_1[0][0]);
        buf_adrs_set += trans_info.count;

        g_usb0_buf_current_1 = buf_adrs_set;
    }

    buf_adr += size;
    if( buf_adrs_set > buf_adr )
    {
        printf("DMAC2 Link Make error!!\n");
    }
}

/*******************************************************************************
* Function Name: usb0_userdef_set_usbclk
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
void usb0_userdef_set_usbclk( void )
{
    volatile uint8_t     dummy_buf;
    uint16_t             clockmode;

    CPG.STBCR7  &= 0xfd;         /*The clock of USB0 modules is permitted */
    dummy_buf   = CPG.STBCR7;    /* (Dummy read)                          */

#ifndef    CLK_USB_EXTAL
    clockmode = USBHCLOCK_X1_48MHZ;
#else
    clockmode = USBHCLOCK_EXTAL_12MHZ;
#endif

    if (RZA_IO_RegRead_16(&USB200.SYSCFG0,
                           USB_SYSCFG_UPLLE_SHIFT,
                           USB_SYSCFG_UPLLE) == 1)
    {
        if ((USB200.SYSCFG0 & USB_HOST_BITUCKSEL) != clockmode)
        {
            RZA_IO_RegWrite_16(&USB200.SUSPMODE,
                                0,
                                USB_SUSPMODE_SUSPM_SHIFT,
                                USB_SUSPMODE_SUSPM);
            USB200.SYSCFG0 = 0;
            USB200.SYSCFG0 = (USB_HOST_BITUPLLE | clockmode);
            usb0_userdef_wait_xms(1);
            RZA_IO_RegWrite_16(&USB200.SUSPMODE,
                                1,
                                USB_SUSPMODE_SUSPM_SHIFT,
                                USB_SUSPMODE_SUSPM);
        }
        else
        {
            RZA_IO_RegWrite_16(&USB200.SUSPMODE,
                                0,
                                USB_SUSPMODE_SUSPM_SHIFT,
                                USB_SUSPMODE_SUSPM);
            usb0_userdef_wait_xms(1);
            RZA_IO_RegWrite_16(&USB200.SUSPMODE,
                                1,
                                USB_SUSPMODE_SUSPM_SHIFT,
                                USB_SUSPMODE_SUSPM);
        }
    }
    else
    {
        RZA_IO_RegWrite_16(&USB200.SUSPMODE,
                            0,
                            USB_SUSPMODE_SUSPM_SHIFT,
                            USB_SUSPMODE_SUSPM);
        USB200.SYSCFG0 = 0;
        USB200.SYSCFG0 = (USB_HOST_BITUPLLE | clockmode);
        usb0_userdef_wait_xms(1);
        RZA_IO_RegWrite_16(&USB200.SUSPMODE,
                            1,
                            USB_SUSPMODE_SUSPM_SHIFT,
                            USB_SUSPMODE_SUSPM);
    }
}

/*******************************************************************************
* Function Name: usb0_userdef_set_usbacswr1
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
void usb0_userdef_set_usbacswr1( void )
{
    /* nothing to do */
}

/*******************************************************************************
* Function Name: usb0_host_get_dma_adrs
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static void usb0_host_get_dma_adrs(USB_HOST_DMA_t *dma, uint16_t *dfacc, uint32_t *src, uint32_t *dst)
{
    uint32_t    size;
    uint32_t    dir;

    dir = dma->dir;
    if (dir == USB_FIFO2BUF)
    {
        /* DxFIFO determination */
        *dst = dma->buffer;
        size = dma->size;
        if (size == 2)
        {
            /* 32bit access */
            if (*dfacc == 2)
            {
                /* 32byte access */
                if ( (dma->fifo == USB_D0FIFO) || (dma->fifo == USB_D0FIFO_BIG) )
                {
                    *src = (uint32_t)(&USB200.D0FIFOB0);
                }
                else
                {
                    *src = (uint32_t)(&USB200.D1FIFOB0);
                }
            }
            else if (*dfacc == 1)
            {
                /* 16byte access */
                if ( (dma->fifo == USB_D0FIFO) || (dma->fifo == USB_D0FIFO_BIG) )
                {
                    *src = (uint32_t)(&USB200.D0FIFOB0);
                }
                else
                {
                    *src = (uint32_t)(&USB200.D1FIFOB0);
                }
            }
            else
            {
                /* normal access */
                if ( (dma->fifo == USB_D0FIFO) || (dma->fifo == USB_D0FIFO_BIG) )
                {
                    *src = (uint32_t)(&USB200.D0FIFO.UINT32);
                }
                else
                {
                    *src = (uint32_t)(&USB200.D1FIFO.UINT32);
                }
            }
        }
        else if (size == 1)
        {
            /* 16bit access */
            *dfacc = 0;      /* force normal access */

            if ( (dma->fifo == USB_D0FIFO) || (dma->fifo == USB_D0FIFO_BIG) )
            {
                *src = (uint32_t)(&USB200.D0FIFO.UINT32);
            }
            else
            {
                *src = (uint32_t)(&USB200.D1FIFO.UINT32);
            }
            *src += 2;       /* short access */
        }
        else
        {
            /* 8bit access */
            *dfacc = 0;      /* force normal access */

            if ( (dma->fifo == USB_D0FIFO) || (dma->fifo == USB_D0FIFO_BIG) )
            {
                *src = (uint32_t)(&USB200.D0FIFO.UINT32);
            }
            else
            {
                *src = (uint32_t)(&USB200.D1FIFO.UINT32);
            }
            *src += 3;       /* byte access  */
        }
    }
    else
    {
        /* DxFIFO determination */
        *src = dma->buffer;
        size = dma->size;
        if (size == 2)
        {
            /* 32bit access */
            if (*dfacc == 2)
            {
                /* 32byte access */
                if ( (dma->fifo == USB_D0FIFO) || (dma->fifo == USB_D0FIFO_BIG) )
                {
                    *dst = (uint32_t)(&USB200.D0FIFOB0);
                }
                else
                {
                    *dst = (uint32_t)(&USB200.D1FIFOB0);
                }
            }
            else if (*dfacc == 1)
            {
                /* 16byte access */
                if ( (dma->fifo == USB_D0FIFO) || (dma->fifo == USB_D0FIFO_BIG) )
                {
                    *dst = (uint32_t)(&USB200.D0FIFOB0);
                }
                else
                {
                    *dst = (uint32_t)(&USB200.D1FIFOB0);
                }
            }
            else
            {
                /* normal access */
                if ( (dma->fifo == USB_D0FIFO) || (dma->fifo == USB_D0FIFO_BIG) )
                {
                    *dst = (uint32_t)(&USB200.D0FIFO.UINT32);
                }
                else
                {
                    *dst = (uint32_t)(&USB200.D1FIFO.UINT32);
                }
            }
        }
        else if (size == 1)
        {
            /* 16bit access */
            *dfacc = 0;      /* force normal access */

            if ( (dma->fifo == USB_D0FIFO) || (dma->fifo == USB_D0FIFO_BIG) )
            {
                *dst = (uint32_t)(&USB200.D0FIFO.UINT32);
            }
            else
            {
                *dst = (uint32_t)(&USB200.D1FIFO.UINT32);
            }
            *dst += 2;       /* short access */
        }
        else
        {
            /* 8bit access */
            *dfacc = 0;      /* force normal access */

            if ( (dma->fifo == USB_D0FIFO) || (dma->fifo == USB_D0FIFO_BIG) )
            {
                *dst = (uint32_t)(&USB200.D0FIFO.UINT32);
            }
            else
            {
                *dst = (uint32_t)(&USB200.D1FIFO.UINT32);
            }
            *dst += 3;       /* byte access  */
        }
    }
}

/*******************************************************************************
* Function Name: usb0_userdef_start_dma
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
void usb0_userdef_start_dma(USB_HOST_DMA_t *dma, uint16_t dfacc)
{
    uint32_t    src;
    uint32_t    dst;

    usb0_host_get_dma_adrs(dma, &dfacc, &src, &dst);

    if ( (dma->fifo == USB_D0FIFO) || (dma->fifo == USB_D0FIFO_BIG) )
    {
        usb0_enable_dmac0(src, dst, dma->bytes, dma->size, dma->dir, dma->fifo, dfacc);
    }
    else
    {
        usb0_enable_dmac1(src, dst, dma->bytes, dma->size, dma->dir, dma->fifo, dfacc);
    }
}

/*******************************************************************************
* Function Name: usb0_userdef_start_dma_link
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
void usb0_userdef_start_dma_link(USB_HOST_DMA_t *dma)
{
    uint32_t src;
    uint32_t dst;

    if (dma->dir == USB_FIFO2BUF)
    {
        /* DxFIFO determination */
        dst = dma->buffer;      /* dummy */
        if (dma->fifo == USB_D0FIFO)
        {
            src = (uint32_t)(&USB200.D0FIFO.UINT32);
        }
        else
        {
            src = (uint32_t)(&USB200.D1FIFO.UINT32);
        }
    }
    else
    {
        /* DxFIFO determination */
        src = dma->buffer;      /* dummy */
        if (dma->fifo == USB_D0FIFO)
        {
            dst = (uint32_t)(&USB200.D0FIFO.UINT32);
        }
        else
        {
            dst = (uint32_t)(&USB200.D1FIFO.UINT32);
        }
    }

    if (dma->fifo == USB_D0FIFO)
    {
        usb0_enable_dmac0_iso(src, dst, dma->dir);
    }
    else
    {
        usb0_enable_dmac1_iso(src, dst, dma->dir);
    }
}

/*******************************************************************************
* Function Name: usb0_userdef_stop_dma
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
uint32_t usb0_userdef_stop_dma(uint16_t fifo)
{
    uint32_t    remain;

    if ( (fifo == USB_D0FIFO) || (fifo == USB_D0FIFO_BIG) )
    {
        usb0_host_DMAC1_Close(&remain);
    }
    else
    {
        usb0_host_DMAC2_Close(&remain);
    }

    return remain;
}

/*******************************************************************************
* Function Name: usb0_userdef_reload_dma
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
void usb0_userdef_reload_dma(USB_HOST_DMA_t *dma, uint16_t dfacc)
{
    uint32_t    src;
    uint32_t    dst;

    usb0_host_get_dma_adrs(dma, &dfacc, &src, &dst);

    if ( (dma->fifo == USB_D0FIFO) || (dma->fifo == USB_D0FIFO_BIG) )
    {
        usb0_host_DMAC1_Load_Set(src, dst, dma->bytes);
    }
    else
    {
        usb0_host_DMAC2_Load_Set(src, dst, dma->bytes);
    }
}

/*******************************************************************************
* Function Name: usb0_enable_dmac0
* Description  :
* Arguments    :
* Return Value : none
*******************************************************************************/
static void usb0_enable_dmac0( uint32_t src, uint32_t dst, uint32_t count,
                               uint32_t size, uint32_t dir, uint32_t fifo, uint16_t dfacc)
{
    dmac_transinfo_t    trans_info;
    uint32_t            request_factor = 0;
    int32_t             ret;

    /* ==== Variable setting for DMAC initialization ==== */
    trans_info.src_addr  = (uint32_t)src;               /* Start address of transfer source      */
    trans_info.dst_addr  = (uint32_t)dst;               /* Start address of transfer destination */
    trans_info.count     = (uint32_t)count;             /* Total byte count to be transferred    */
    if (dfacc == 2)
    {
        /* 32byte access */
        trans_info.src_size = DMAC_TRANS_SIZE_256;      /* Transfer source transfer size      */
        trans_info.dst_size = DMAC_TRANS_SIZE_256;      /* Transfer destination transfer size */
    }
    else if (dfacc == 1)
    {
        /* 16byte access */
        trans_info.src_size = DMAC_TRANS_SIZE_128;      /* Transfer source transfer size      */
        trans_info.dst_size = DMAC_TRANS_SIZE_128;      /* Transfer destination transfer size */
    }
    else
    {
        /* normal access */
        if (size == 0)
        {
            trans_info.src_size = DMAC_TRANS_SIZE_8;    /* Transfer source transfer size      */
            trans_info.dst_size = DMAC_TRANS_SIZE_8;    /* Transfer destination transfer size */
        }
        else if (size == 1)
        {
            trans_info.src_size = DMAC_TRANS_SIZE_16;   /* Transfer source transfer size      */
            trans_info.dst_size = DMAC_TRANS_SIZE_16;   /* Transfer destination transfer size */
        }
        else if (size == 2)
        {
            trans_info.src_size = DMAC_TRANS_SIZE_32;   /* Transfer source transfer size      */
            trans_info.dst_size = DMAC_TRANS_SIZE_32;   /* Transfer destination transfer size */
        }
        else
        {
            printf("size error!!\n");
        }
    }

    if (dir == USB_FIFO2BUF)
    {
        request_factor       = DMAC_REQ_USB0_DMA0_RX;   /* USB channel 0 receive D0FIFO full               */
        trans_info.saddr_dir = DMAC_TRANS_ADR_NO_INC;   /* Count direction of transfer source address      */
        trans_info.daddr_dir = DMAC_TRANS_ADR_INC;      /* Count direction of transfer destination address */
    }
    else if (dir == USB_BUF2FIFO)
    {
        request_factor       = DMAC_REQ_USB0_DMA0_TX;   /* USB channel 0 receive D0FIFO empty              */
        trans_info.saddr_dir = DMAC_TRANS_ADR_INC;      /* Count direction of transfer source address      */
        trans_info.daddr_dir = DMAC_TRANS_ADR_NO_INC;   /* Count direction of transfer destination address */
    }
    else
    {
        /* Do Nothing */
    }

    /* ==== DMAC initialization ==== */
    usb0_host_DMAC1_PeriReqInit((const dmac_transinfo_t *)&trans_info,
                                DMAC_MODE_REGISTER,
                                DMAC_SAMPLE_SINGLE,
                                request_factor,
                                0);    /* Dont care DMAC_REQ_REQD is setting in usb0_host_DMAC1_PeriReqInit() */

    /* ==== DMAC startup ==== */
    ret = usb0_host_DMAC1_Open(DMAC_REQ_MODE_PERI);
    if (ret != 0)
    {
        printf("DMAC1 Open error!!\n");
    }

    return;
}

/*******************************************************************************
* Function Name: usb0_enable_dmac0_iso
* Description  : Enables DMA transfer on the information specified by the argument.
* Outline      : setting DMA for D0FIFO
* Argument     : uint32_t src   : src address
*              : uint32_t dst   : dst address
*              : uint32_t dir   : direction
* Return Value : none
*******************************************************************************/
static void usb0_enable_dmac0_iso(uint32_t src, uint32_t dst, uint32_t dir)
{
    dmac_transinfo_t    trans_info;
    uint32_t            request_factor;
    int32_t             ret;

    if (dir == USB_FIFO2BUF)
    {
        request_factor       = DMAC_REQ_USB0_DMA0_RX;     /* USB channel 0 receive D0FIFO full               */
        trans_info.saddr_dir = DMAC_TRANS_ADR_NO_INC;     /* Transfer source address counting direction      */
        trans_info.daddr_dir = DMAC_TRANS_ADR_INC;        /* Transfer destination address counting direction */
    }
    else
    {
        request_factor       = DMAC_REQ_USB0_DMA0_TX;     /* USB channel 0 receive D0FIFO empty              */
        trans_info.saddr_dir = DMAC_TRANS_ADR_INC;        /* Transfer source address counting direction      */
        trans_info.daddr_dir = DMAC_TRANS_ADR_NO_INC;     /* Transfer destination address counting direction */
    }

    /* ==== Variable setting for DMAC initialization ==== */
    g_usb0_buf_index_0   = 0;
    trans_info.count     = (uint32_t)0;                   /* dummy */
    trans_info.src_addr  = (uint32_t)&g_usb0_link_register_0[0][0];
    trans_info.dst_addr  = (uint32_t)0;                   /* dummy */
    trans_info.src_size  = DMAC_TRANS_SIZE_32;            /* Transfer source transfer size      */
    trans_info.dst_size  = DMAC_TRANS_SIZE_32;            /* Transfer destination transfer size */

    /* ==== DMAC initialization ==== */
    usb0_host_DMAC1_PeriReqInit((const dmac_transinfo_t *)&trans_info,
                                DMAC_MODE_LINK,
                                DMAC_SAMPLE_SINGLE,
                                request_factor,
                                0);    /* Dont care DMAC_REQ_REQD is setting in usb0_host_DMAC1_PeriReqInit() */

    /* ==== DMAC startup ==== */
    ret = usb0_host_DMAC1_Open(DMAC_REQ_MODE_PERI);
    if (ret != 0)
    {
        printf("DMAC1_ISO Open error!!\n");
    }
}

/*******************************************************************************
* Function Name: usb0_enable_dmac1
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static void usb0_enable_dmac1( uint32_t src, uint32_t dst, uint32_t count,
                               uint32_t size, uint32_t dir, uint32_t fifo, uint16_t dfacc)
{
    dmac_transinfo_t    trans_info;
    uint32_t            request_factor = 0;
    int32_t             ret;

    /* ==== Variable setting for DMAC initialization ==== */
    trans_info.src_addr  = (uint32_t)src;               /* Start address of transfer source      */
    trans_info.dst_addr  = (uint32_t)dst;               /* Start address of transfer destination */
    trans_info.count     = (uint32_t)count;             /* Total byte count to be transferred    */
    if (dfacc == 2)
    {
        /* 32byte access */
        trans_info.src_size = DMAC_TRANS_SIZE_256;      /* Transfer source transfer size      */
        trans_info.dst_size = DMAC_TRANS_SIZE_256;      /* Transfer destination transfer size */
    }
    else if (dfacc == 1)
    {
        /* 16byte access */
        trans_info.src_size = DMAC_TRANS_SIZE_128;      /* Transfer source transfer size      */
        trans_info.dst_size = DMAC_TRANS_SIZE_128;      /* Transfer destination transfer size */
    }
    else
    {
        /* normal access */
        if (size == 0)
        {
            trans_info.src_size = DMAC_TRANS_SIZE_8;    /* Transfer source transfer size      */
            trans_info.dst_size = DMAC_TRANS_SIZE_8;    /* Transfer destination transfer size */
        }
        else if (size == 1)
        {
            trans_info.src_size = DMAC_TRANS_SIZE_16;   /* Transfer source transfer size      */
            trans_info.dst_size = DMAC_TRANS_SIZE_16;   /* Transfer destination transfer size */
        }
        else if (size == 2)
        {
            trans_info.src_size = DMAC_TRANS_SIZE_32;   /* Transfer source transfer size      */
            trans_info.dst_size = DMAC_TRANS_SIZE_32;   /* Transfer destination transfer size */
        }
        else
        {
            printf("size error!!\n");
        }
    }

    if (dir == USB_FIFO2BUF)
    {
        request_factor     = DMAC_REQ_USB0_DMA1_RX;     /* USB channel 0 receive D1FIFO full               */
        trans_info.saddr_dir = DMAC_TRANS_ADR_NO_INC;   /* Count direction of transfer source address      */
        trans_info.daddr_dir = DMAC_TRANS_ADR_INC;      /* Count direction of transfer destination address */
    }
    else if (dir == USB_BUF2FIFO)
    {
        request_factor     = DMAC_REQ_USB0_DMA1_TX;     /* USB channel 0 receive D1FIFO empty              */
        trans_info.saddr_dir = DMAC_TRANS_ADR_INC;      /* Count direction of transfer source address      */
        trans_info.daddr_dir = DMAC_TRANS_ADR_NO_INC;   /* Count direction of transfer destination address */
    }
    else
    {
        /* Do Nothing */
    }

    /* ==== DMAC initialization ==== */
    usb0_host_DMAC2_PeriReqInit((const dmac_transinfo_t *)&trans_info,
                                DMAC_MODE_REGISTER,
                                DMAC_SAMPLE_SINGLE,
                                request_factor,
                                0);    /* Don't care DMAC_REQ_REQD is setting in usb0_host_DMAC2_PeriReqInit() */

    /* ==== DMAC startup ==== */
    ret = usb0_host_DMAC2_Open(DMAC_REQ_MODE_PERI);
    if (ret != 0)
    {
        printf("DMAC2 Open error!!\n");
    }

    return;
}

/*******************************************************************************
* Function Name: usb0_enable_dmac1_iso
* Description  : Enables DMA transfer on the information specified by the argument.
* Outline      : setting DMA for D0FIFO
* Argument     : uint32_t src   : src address
*              : uint32_t dst   : dst address
*              : uint32_t dir   : direction
* Return Value : none
*******************************************************************************/
static void usb0_enable_dmac1_iso(uint32_t src, uint32_t dst, uint32_t dir)
{
    dmac_transinfo_t    trans_info;
    uint32_t            request_factor;
    int32_t             ret;

    if (dir == USB_FIFO2BUF)
    {
        request_factor       = DMAC_REQ_USB0_DMA1_RX;     /* USB channel 0 receive D1FIFO full               */
        trans_info.saddr_dir = DMAC_TRANS_ADR_NO_INC;     /* Transfer source address counting direction      */
        trans_info.daddr_dir = DMAC_TRANS_ADR_INC;        /* Transfer destination address counting direction */
    }
    else
    {
        request_factor       = DMAC_REQ_USB0_DMA1_TX;     /* USB channel 0 receive D1FIFO empty              */
        trans_info.saddr_dir = DMAC_TRANS_ADR_INC;        /* Transfer source address counting direction      */
        trans_info.daddr_dir = DMAC_TRANS_ADR_NO_INC;     /* Transfer destination address counting direction */
    }

    /* ==== Variable setting for DMAC initialization ==== */
    g_usb0_buf_index_1   = 0;
    trans_info.count     = (uint32_t)0;                   /* dummy */
    trans_info.src_addr  = (uint32_t)&g_usb0_link_register_1[0][0];
    trans_info.dst_addr  = (uint32_t)0;                   /* dummy */
    trans_info.src_size  = DMAC_TRANS_SIZE_32;            /* Transfer source transfer size      */
    trans_info.dst_size  = DMAC_TRANS_SIZE_32;            /* Transfer destination transfer size */

    /* ==== DMAC initialization ==== */
    usb0_host_DMAC2_PeriReqInit((const dmac_transinfo_t *)&trans_info,
                                DMAC_MODE_LINK,
                                DMAC_SAMPLE_SINGLE,
                                request_factor,
                                0);    /* Dont care DMAC_REQ_REQD is setting in usb0_host_DMAC2_PeriReqInit() */

    /* ==== DMAC startup ==== */
    ret = usb0_host_DMAC2_Open(DMAC_REQ_MODE_PERI);
    if (ret != 0)
    {
        printf("DMAC2_ISO Open error!!\n");
    }
}

/*******************************************************************************
* Function Name: usb0_userdef_wait_1us
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
void usb0_userdef_wait_1us( uint32_t time )
{
    do
    {
        usb0_userdef_wait_1us_2();
        time--;
    } while(time > 0);
}

/*******************************************************************************
* Function Name: usb0_userdef_wait_1us_2
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static void usb0_userdef_wait_1us_2(void)
{
    volatile uint16_t    tmp;

    /* DUMMY_ACCESS tasks 67ns */
    tmp = DUMMY_ACCESS;
    tmp = DUMMY_ACCESS;
    tmp = DUMMY_ACCESS;
    tmp = DUMMY_ACCESS;
    tmp = DUMMY_ACCESS;

    tmp = DUMMY_ACCESS;
    tmp = DUMMY_ACCESS;
    tmp = DUMMY_ACCESS;
    tmp = DUMMY_ACCESS;
    tmp = DUMMY_ACCESS;

    tmp = DUMMY_ACCESS;
    tmp = DUMMY_ACCESS;
    tmp = DUMMY_ACCESS;
    tmp = DUMMY_ACCESS;
    tmp = DUMMY_ACCESS;
}

/*******************************************************************************
* Function Name: usb0_userdef_wait_xms
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
void usb0_userdef_wait_xms( uint32_t time )
{
    /*
     * Wait ms ( Please change for your MCU )
     */
    osDelay(time);
}

/*******************************************************************************
* Function Name: usb0_userdef_wait_100ns
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
void usb0_userdef_wait_100ns(uint32_t time)
{
    do
    {
        usb0_userdef_wait_100ns_2();
        time--;
    } while(time > 0);
}

/*******************************************************************************
* Function Name: usb0_userdef_wait_100ns_2
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static void usb0_userdef_wait_100ns_2(void)
{
    volatile uint16_t    tmp;

    /* DUMMY_ACCESS tasks 67ns */
    tmp = DUMMY_ACCESS;
    tmp = DUMMY_ACCESS;
}

/* End of File */
