/*
 * Copyright (c) 2016, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
/***
 *\file : USBhost.c
 *\brief: Source file for USB host controller
 *
 *\author: Antriksh
 *\version: 01a,10Sep2016,An taken reference from Dm814x and updated for Dm8127
 */

/*
 *=====================
 * Includes
 *=====================
 */
#include "dm8127_evm.h"
#include "dm8127_platform.h"
#include "stdio.h"
#include <string.h>
#include "usb_host.h"
#include "musb_regs.h"

/*
 *=====================
 * Defines
 *=====================
 */
#define USBSS_CORE			0x0
#define L3_TI816X_BASE      0x44000000
#define L4_FAST_TI816X_BASE 0x4a000000
#define L4_SLOW_TI816X_BASE 0x48000000

#define TI816X_SCM_BASE     0x48140000
#define TI816X_CTRL_BASE    TI816X_SCM_BASE
#define TI816X_PRCM_BASE    0x48180000

#define TI816X_ARM_INTC_BASE    0x48200000
#define TI816X_GPMC_BASE    0x50000000

#define TI816X_PCIE_REG_BASE    0x51000000
#define TI816X_PCIE_MEM_BASE    0x20000000
#define TI816X_PCIE_IO_BASE     0x40000000  /* Using 3MB reserved space */

#define TI816X_USBSS_BASE   0x47400000
#define TI816X_USBSS_LEN    0xFFF
#define TI816X_USB0_BASE    0x47401000
#define TI816X_USB1_BASE    0x47401800
#define TI816X_USB_CPPIDMA_BASE 0x47402000
#define TI816X_USB_CPPIDMA_LEN  0x5FFF

/* TI814X specific definitions */
#define TI814X_USBCTRL0     0x0620
#define TI814X_USBSTS0      0x0624
#define TI814X_USBCTRL1     0x0628
#define TI814X_USBSTS1      0x062c
#define USB_0               (0u)

/* PHY controls bits */
#define TI814X_USBPHY_CM_PWRDN      (1 << 0)
#define TI814X_USBPHY_OTG_PWRDN     (1 << 1)
#define TI814X_USBPHY_CHGDET_DIS    (1 << 2)
#define TI814X_USBPHY_CHGDET_RSTRT  (1 << 3)
#define TI814X_USBPHY_SRCONDM       (1 << 4)
#define TI814X_USBPHY_SINKONDP      (1 << 5)
#define TI814X_USBPHY_CHGISINK_EN   (1 << 6)
#define TI814X_USBPHY_CHGVSRC_EN    (1 << 7)
#define TI814X_USBPHY_DMPULLUP      (1 << 8)
#define TI814X_USBPHY_DPPULLUP      (1 << 9)
#define TI814X_USBPHY_CDET_EXTCTL   (1 << 10)
#define TI814X_USBPHY_GPIO_MODE     (1 << 12)
#define TI814X_USBPHY_DPOPBUFCTL    (1 << 13)
#define TI814X_USBPHY_DMOPBUFCTL    (1 << 14)
#define TI814X_USBPHY_DPINPUT       (1 << 15)
#define TI814X_USBPHY_DMINPUT       (1 << 16)
#define TI814X_USBPHY_DPGPIO_PD     (1 << 17)
#define TI814X_USBPHY_DMGPIO_PD     (1 << 18)
#define TI814X_USBPHY_OTGVDET_EN    (1 << 19)
#define TI814X_USBPHY_OTGSESSEND_EN (1 << 20)
#define TI814X_USBPHY_DATA_POLARITY (1 << 23)

#define USB_TX_EP_MASK  0xffff      /* EP0 + 15 Tx EPs */
#define USB_RX_EP_MASK  0xfffe      /* 15 Rx EPs */

#define USB_TX_INTR_MASK    (USB_TX_EP_MASK << USB_INTR_TX_SHIFT)
#define USB_RX_INTR_MASK    (USB_RX_EP_MASK << USB_INTR_RX_SHIFT)

#define A_WAIT_BCON_TIMEOUT 1100        /* in ms */

#define USBSS_INTR_RX_STARV 0x00000001
#define USBSS_INTR_PD_CMPL  0x00000004
#define USBSS_INTR_TX_CMPL  0x00000500
#define USBSS_INTR_RX_CMPL  0x00000A00
#define USBSS_INTR_FLAGS    (USBSS_INTR_PD_CMPL | USBSS_INTR_TX_CMPL \
                                                | USBSS_INTR_RX_CMPL)

/* USB 2.0 PHY Control */
#define CONF2_PHY_GPIOMODE     (1 << 23)
#define CONF2_OTGMODE          (3 << 14)
#define CONF2_SESENDEN         (1 << 13)       /* Vsess_end comparator */
#define CONF2_VBDTCTEN         (1 << 12)       /* Vbus comparator */
#define CONF2_REFFREQ_24MHZ    (2 << 8)
#define CONF2_REFFREQ_26MHZ    (7 << 8)
#define CONF2_REFFREQ_13MHZ    (6 << 8)
#define CONF2_REFFREQ          (0xf << 8)
#define CONF2_PHYCLKGD         (1 << 7)
#define CONF2_VBUSSENSE        (1 << 6)
#define CONF2_PHY_PLLON        (1 << 5)        /* override PLL suspend */
#define CONF2_RESET            (1 << 4)
#define CONF2_PHYPWRDN         (1 << 3)
#define CONF2_OTGPWRDN         (1 << 2)
#define CONF2_DATPOL           (1 << 1)


#define USB_TX_EP_MASK  0xffff      /* EP0 + 15 Tx EPs */
#define USB_RX_EP_MASK  0xfffe      /* 15 Rx EPs */

#define USB_TX_INTR_MASK    (USB_TX_EP_MASK << USB_INTR_TX_SHIFT)
#define USB_RX_INTR_MASK    (USB_RX_EP_MASK << USB_INTR_RX_SHIFT)

#define A_WAIT_BCON_TIMEOUT 1100        /* in ms */

#define USBSS_INTR_RX_STARV 0x00000001
#define USBSS_INTR_PD_CMPL  0x00000004
#define USBSS_INTR_TX_CMPL  0x00000500
#define USBSS_INTR_RX_CMPL  0x00000A00
#define USBSS_INTR_FLAGS    (USBSS_INTR_PD_CMPL | USBSS_INTR_TX_CMPL \
                                                | USBSS_INTR_RX_CMPL)


#define is_peripheral_enabled(musb_board_mode) (musb_board_mode != MUSB_HOST)
#define is_host_enabled(musb_board_mode)       (musb_board_mode != MUSB_PERIPHERAL)
#define is_otg_enabled(musb_board_mode)        (musb_board_mode == MUSB_OTG)

#define OMAP_CTRL_REGADDR(reg)      (TI816X_CTRL_BASE + (reg))

#define HOST_CSR0 0x0012

struct usbotg_ss {
    void        *base;
	void        *intc_base;
    int     	init_done;
};

/*USB enumeration */

/* mentor is at offset of 0x400 in am3517/ti816x */
void *musb_mregs;
void *musb_ctrl_base;
UINT32 musb_is_active  = 0;
void *reg_base;
UINT16 usb_num;
#define RXSTALL 		(1 << 2)
#define ERROR   		(1 << 4)
#define NAK_TIMEOUT 	(1 << 7)
#define ALL_ERROR 		(RXSTALL | ERROR | NAK_TIMEOUT)
/* Endpoint defines */
#define EP_FIFO_OFFSET  0x20
#define EP_SELECT		0x0E

#define STRING 	1
#define DEVICE  2
#define CONFIG  3
#define USB_BASE	0x47401000
#define USB0_FIFO0   *( volatile UINT32* )( USB_BASE + 0x420 )
#define USB1_FIFO0   *( volatile UINT32* )( USB_BASE + 0xC20 )
#define CM_ALWON2_USB_CLKCTRL			(PRCM_BASE_ADDR + 0x0558)
#define readl(a) 		(*(volatile unsigned int *)(a))
#define readb(a)        (*(volatile unsigned char *)(a))
#define TX_EP0_INTR_REG  0x30
#define RXCOUNT			 0x18
#define EP0_INTR_STAT   (1 << 0)
#define INTR_CLR		(1 << 0)
#define RXPCKTRDY_SET	(1 << 0)
#define RXPCKTRDY_CLR	(0 << 0)

static UINT8 buffer[100];
static UINT8 fifo_data[100];

void write_fifo( int , UINT16 , UINT8 * );
void process_fifo_data(UINT8 * ,int );


enum musb_mode {
    MUSB_UNDEFINED = 0,
    MUSB_HOST,      /* A or Mini-A connector */
    MUSB_PERIPHERAL,    /* B or Mini-B connector */
    MUSB_OTG        /* Mini-AB connector */
};

struct usbotg_ss usbss;
/****
 *\brief: USB subsystem read routine.
 *
 *\descr: This routine will read the value from the a register depends on
 *        the offset given as input.
 *
 *\param : offs [IN]  USB subsystem offset
 *\return: Read value from the register
 *
 */
UINT32 usbss_read
(
	UINT32 offs
)
{
    UINT32 val = 0;
    val = *(volatile UINT32 *)((UINT32)usbss.base + offs);
    return val;
}
/****
 *\brief: USB subsystem wite function.
 *
 *\descr: This routine writes the given value on a register followed by the
 *        register offsets.
 *
 *\param: offs  [IN]  Register offset
 *        val   [IN]  Value to be written
 *\return:
 *
 */
void usbss_write
(
	UINT32 offs,
	UINT32 val
)
{
	 if (!usbss.init_done)
        return;

    *(volatile UINT32 *)((UINT32)usbss.base + offs) = val;
}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
UINT32 omap_ctrl_readl
(
	UINT16 offset
)
{
	UINT32 val = 0;
    val = *(volatile UINT32 *)(OMAP_CTRL_REGADDR(offset));
    return val;
}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
void omap_ctrl_writel
(
	UINT32 val,
	UINT16 offset
)
{
    *(volatile UINT32 *)(OMAP_CTRL_REGADDR(offset)) = val;
}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
UINT32 musb_readl(const void *addr, unsigned offset)
{
	UINT32 val = 0;
    val = *(volatile UINT32 *)((UINT32)addr + offset);
    return val;
}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
void musb_writel(void *addr, unsigned offset, UINT32 data){
	*(volatile UINT32 *)((UINT32)addr + offset) = data;
}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
UINT16 musb_readw(const void *addr, unsigned offset)
{
    UINT32 tmp;
    UINT16 val;

    tmp = musb_readl(addr, (offset & ~3));

    switch (offset & 0x3) {
    case 0:
        val = (tmp & 0xffff);
        break;
    case 1:
        val = (tmp >> 8) & 0xffff;
        break;
    case 2:
    case 3:
    default:
        val = (tmp >> 16) & 0xffff;
        break;
    }
    return val;
}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
void musb_writew (void *addr, unsigned offset, UINT16 data)
{
    *(volatile UINT16 *)((UINT32)addr + offset) = data;
}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
UINT8 musb_readb(const void *addr, unsigned offset)
{
    UINT32 tmp;
    UINT8 val;

    tmp = musb_readl(addr, (offset & ~3));

    switch (offset & 0x3) {
    case 0:
        val = tmp & 0xff;
        break;
    case 1:
        val = (tmp >> 8);
        break;
    case 2:
        val = (tmp >> 16);
        break;
    case 3:
    default:
        val = (tmp >> 24);
        break;
    }
    return val;
}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
void musb_writeb(void *addr, unsigned offset, UINT8 data)
{
    *(volatile UINT8 *)((UINT32)addr + offset) = data;
}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */

int usbotg_ss_init(void)
{
    int status = 0;

    if (!usbss.init_done) {
        usbss.base = (void *)TI816X_USBSS_BASE;
        usbss.intc_base = (void *)0x50000000;
        usbss.init_done = 1;
        /* eoi to usbss */
        usbss_write(USBSS_IRQ_EOI, 0);
        /* clear any USBSS interrupts */
        usbss_write(USBSS_IRQ_STATUS, usbss_read(USBSS_IRQ_STATUS));
    }
    return status;
}

/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
void phy_on(UINT8 id)
{
    UINT32 usbphycfg;

#ifdef DEBUG
    printf("phy_on..\n");
#else
    Uart_stringSend("\r\nPHY ON\r\n");
#endif
    /*
     * Start the on-chip PHY and its PLL.
     */
	if (id == 0)
        usbphycfg = omap_ctrl_readl(TI814X_USBCTRL0);
    else if (id == 1)
        usbphycfg = omap_ctrl_readl(TI814X_USBCTRL1);

    usbphycfg &= ~(TI814X_USBPHY_CM_PWRDN | TI814X_USBPHY_OTG_PWRDN
        | TI814X_USBPHY_DMPULLUP | TI814X_USBPHY_DPPULLUP
        | TI814X_USBPHY_DPINPUT | TI814X_USBPHY_DMINPUT
        | TI814X_USBPHY_DATA_POLARITY);
    usbphycfg |= (TI814X_USBPHY_SRCONDM | TI814X_USBPHY_SINKONDP
        | TI814X_USBPHY_CHGISINK_EN | TI814X_USBPHY_CHGVSRC_EN
        | TI814X_USBPHY_CDET_EXTCTL | TI814X_USBPHY_DPOPBUFCTL
        | TI814X_USBPHY_DMOPBUFCTL | TI814X_USBPHY_DPGPIO_PD
        | TI814X_USBPHY_DMGPIO_PD | TI814X_USBPHY_OTGVDET_EN
        | TI814X_USBPHY_OTGSESSEND_EN);

    if (id == 0)
        omap_ctrl_writel(usbphycfg, TI814X_USBCTRL0);
    else if (id == 1)
        omap_ctrl_writel(usbphycfg, TI814X_USBCTRL1);
}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
void phy_off(UINT8 id)
{
    UINT32 usbphycfg;

    printf("phy_off..\n");

    if (id == 0)
        usbphycfg = omap_ctrl_readl(TI814X_USBCTRL0);
    else if (id == 1)
        usbphycfg = omap_ctrl_readl(TI814X_USBCTRL1);
     usbphycfg |= TI814X_USBPHY_CM_PWRDN | TI814X_USBPHY_OTG_PWRDN;
     if (id == 0)
        omap_ctrl_writel(usbphycfg, TI814X_USBCTRL0);
    else if (id == 1)
        omap_ctrl_writel(usbphycfg, TI814X_USBCTRL1);
}

#if 0
/*
 * Because we don't set CTRL.UINT, it's "important" to:
 *  - not read/write INTRUSB/INTRUSBE (except during
 *    initial setup, as a workaround);
 *  - use INTSET/INTCLR instead.
 */

/**
 * musb_platform_enable - enable interrupts
 */
void musb_platform_enable(struct musb *musb)
{
    void __iomem *reg_base = musb->ctrl_base;
    UINT32 epmask, coremask;

    /* Workaround: setup IRQs through both register sets. */
    epmask = ((musb->epmask & USB_TX_EP_MASK) << USB_INTR_TX_SHIFT) |
           ((musb->epmask & USB_RX_EP_MASK) << USB_INTR_RX_SHIFT);
    coremask = (0x01ff << USB_INTR_USB_SHIFT);

    coremask &= ~0x8; /* disable the SOF */
    coremask |= 0x8;

    musb_writel(reg_base, USB_EP_INTR_SET_REG, epmask);
    musb_writel(reg_base, USB_CORE_INTR_SET_REG, coremask);
    /* Force the DRVVBUS IRQ so we can start polling for ID change. */
    if (is_otg_enabled(musb))
        musb_writel(reg_base, USB_CORE_INTR_SET_REG,
                USB_INTR_DRVVBUS << USB_INTR_USB_SHIFT);
}

/**
 * musb_platform_disable - disable HDRC and flush interrupts
 */
void musb_platform_disable(struct musb *musb)
{
    void __iomem *reg_base = musb->ctrl_base;

    musb_writel(reg_base, USB_CORE_INTR_CLEAR_REG, USB_INTR_USB_MASK);
    musb_writel(reg_base, USB_EP_INTR_CLEAR_REG,
             USB_TX_INTR_MASK | USB_RX_INTR_MASK);
    musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
    musb_writel(reg_base, USB_IRQ_EOI, 0);
}

#endif

static int vbus_state = -1;
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
static void ti816x_source_power(int is_on, int immediate)
{
    if (is_on)
        is_on = 1;

    if (vbus_state == is_on)
        return;
    vbus_state = is_on;     /* 0/1 vs "-1 == unknown/init" */
}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
void ti816x_set_vbus(int is_on)
{
    ti816x_source_power(is_on, 0);
}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
void musb_platform_enable(void)
{
    void *reg_base = musb_ctrl_base;
    UINT32 epmask, coremask;

    /* Workaround: setup IRQs through both register sets. */
    epmask = ((0xFFFF & USB_TX_EP_MASK) << USB_INTR_TX_SHIFT) |
           ((0xFFFF & USB_RX_EP_MASK) << USB_INTR_RX_SHIFT);
    coremask = (0x03ff << USB_INTR_USB_SHIFT);

    coremask &= ~0x8; /* disable the SOF */
    coremask |= 0x8;

    musb_writel(reg_base, USB_EP_INTR_SET_REG, epmask);
    musb_writel(reg_base, USB_CORE_INTR_SET_REG, coremask);

    /* Force the DRVVBUS IRQ so we can start polling for ID change. */
    if (is_otg_enabled(MUSB_HOST))
        musb_writel(reg_base, USB_CORE_INTR_SET_REG,
                USB_INTR_DRVVBUS << USB_INTR_USB_SHIFT);

}

/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
/**
 * musb_platform_disable - disable HDRC and flush interrupts
 */
void musb_platform_disable(void)
{
    void *reg_base = musb_ctrl_base;

    musb_writel(reg_base, USB_CORE_INTR_CLEAR_REG, USB_INTR_USB_MASK);
    musb_writel(reg_base, USB_EP_INTR_CLEAR_REG,
             USB_TX_INTR_MASK | USB_RX_INTR_MASK);
    musb_writeb(musb_mregs, MUSB_DEVCTL, 0);
    musb_writel(reg_base, USB_IRQ_EOI, 0);
}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
int musb_platform_set_mode(UINT8 musb_mode)
{
    void *reg_base = musb_ctrl_base;

    /* TODO: implement this using CONF0 */
    if (musb_mode == MUSB_HOST) {
        musb_writel(reg_base, USB_MODE_REG, 0);
        musb_writel(musb_ctrl_base, USB_PHY_UTMI_REG, 0x02);
     } else if (musb_mode == MUSB_PERIPHERAL) {
        /* TODO commmented writing 8 to USB_MODE_REG device
            mode is not working */
        musb_writel(reg_base, USB_MODE_REG, 0x100);
    }
    return -1;
}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
void musb_start(void)
{
    void *regs = musb_mregs;
    UINT8   devctl = musb_readb(regs, MUSB_DEVCTL);


    musb_writeb(regs, MUSB_TESTMODE, 0);

    /* put into basic highspeed mode and start session */
    musb_writeb(regs, MUSB_POWER, MUSB_POWER_ISOUPDATE
                        | MUSB_POWER_SOFTCONN
                        | MUSB_POWER_HSENAB
                        /* ENSUSPEND wedges tusb */
                        | MUSB_POWER_ENSUSPEND
                        );

    devctl = musb_readb(regs, MUSB_DEVCTL);
    devctl &= ~MUSB_DEVCTL_SESSION;

    if (is_host_enabled(MUSB_HOST)) {
        /* assume ID pin is hard-wired to ground */
        devctl |= MUSB_DEVCTL_SESSION;
    } else /* peripheral is enabled */ {
        if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
            musb_is_active = 1;
    }
     /*  flush pending interrupts */
    musb_readb(regs, MUSB_INTRUSB);
    musb_readw(regs, MUSB_INTRTX);
    musb_readw(regs, MUSB_INTRRX);


    musb_writew(regs, MUSB_INTRTXE, 0xFFFF);
    musb_writew(regs, MUSB_INTRRXE, 0xFFFF);
    musb_writeb(regs, MUSB_INTRUSBE, 0xf7);

    musb_platform_enable();
    musb_writeb(regs, MUSB_DEVCTL, devctl);

}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */

int musb_platform_init(SINT16 musb)
{
    void *reg_base;// = (void *)TI816X_USB1_BASE;
    UINT32 rev;
    UINT8 mode;

    if (musb)
    {
    	reg_base = (void *)TI816X_USB1_BASE;
    }
    else
    {
    	reg_base = (void *)TI816X_USB0_BASE;
    }
    /* usb subsystem init */
    usbotg_ss_init();

    rev = musb_readl(reg_base, USB_REVISION_REG);
    if (!rev)
        return -1;
#ifdef DEBUG
    printf("MUSB revision: 0x%X\n", rev);
#else
    platform_write("\r\nMUSB revision: 0x%X\n", rev);
#endif


    ti816x_source_power(0, 1);

    /* set musb controller to host mode */
    if (is_host_enabled(MUSB_HOST))
    {
    	musb_platform_set_mode(MUSB_HOST);
    }
    else
    {
    	musb_platform_set_mode(MUSB_PERIPHERAL);
    }
    /* follow recommended reset procedure */
    /* Reset the controller */
    musb_writel(reg_base, USB_CTRL_REG, USB_SOFT_RESET_MASK);

    /* wait till reset bit clears */
    while ((musb_readl(reg_base, USB_CTRL_REG) & 0x1));

    /* clock disable */
    *(volatile UINT32 *)CM_ALWON2_USB_CLKCTRL = 0; /* Disable USB Clock*/
    while((*(volatile UINT32 *)CM_ALWON2_USB_CLKCTRL ) & 0x1 );

    /* Start the on-chip PHY and its PLL. */
    phy_on(musb);
        /* clock enable */
   *(volatile UINT32 *)CM_ALWON2_USB_CLKCTRL = 0x2; /* Enable USB Clock*/
	while((*(volatile UINT32 *)CM_ALWON2_USB_CLKCTRL & 0x0F) != 0x2);		/* Poll for Module is functional */

    /* set musb controller to host mode */
    if (is_host_enabled(MUSB_HOST))
    {
    	mode = MUSB_HOST;
    }
    else
    {
    	mode = MUSB_PERIPHERAL;
    }
	musb_platform_set_mode(mode);

    musb_writel(reg_base, USB_IRQ_EOI, 0);

    return 0;
}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */



void read_fifo(UINT8 ep,UINT32 descriptor_type,int count)
{
	void *regs = musb_mregs;
	UINT8 *data = (UINT8 *)fifo_data;

    UINT32 length,temp_len;
    int i;
    UINT32 val;
    UINT32 ep_fifo0 ;
    ep_fifo0=(UINT32 )musb_mregs + EP_FIFO_OFFSET ;

    /* select the endpoint index */
    musb_writeb(regs,EP_SELECT,ep);

    /*get the number of bytes in the FIFO by reading RXCOUNT reg*/
    length=musb_readw(regs,RXCOUNT);
    temp_len=length;

    /* read the data from the fifo */

    if (length >= 4) {
        for (i = 0; i < (length >> 2); i++) {
            val = readl(ep_fifo0);
            memcpy(data, &val, 4);
            data += 4;
        }
        length %= 4;
    }
    if (length > 0) {
        val = readl(ep_fifo0);
        memcpy(data, &val, length);
        data += 1;
       }
    else
    {
    	data-=1;
    }

    for (i=0;i<temp_len;i++)
    	*data--;

    /*compensating the last byte decrement */
    data+=1;

    /*buffer contains the data received from the device*/
    for (i=count;i<temp_len+count;i++)
    	buffer[i]=*data++;

}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
/*The data read from the FIFO is parsed here */
void process_fifo_data(UINT8 * data,int type)
{
	int i,j;
	int vendorid;
	int productid;

	char c[20] = {  };

	productid = 0;
	vendorid = 0;
	j = 0;

	int stringlen;
	switch (type)
	{
	case (STRING):
	{

		stringlen = data[0];

#ifdef DEBUG
		printf("Manufacturer Name:: ");
		/*first two bytes are neglected */

			for (i=2;i<stringlen;i++)
				printf("%c",data[i]);
			printf("\r\n");

#else
			Uart_stringSend("\r\nManufacturer Name::  ");
						for (i=2;i<stringlen;i++)
						{
							if (data[i] != (0x00) || data[i] != (0x00))
							{
								c[j] = data[i];
								j ++;
							}
						}
//						c[i + 1] = '\0';
			Uart_stringSend((const char*)c);


#endif


		break;
	}
	case (DEVICE):
	{

		vendorid=(data[9] *256)+data[8];
#ifdef DEBUG
		printf("VENDOR ID :: 0x%x\n\r",vendorid);
#else
		platform_write("\r\nVENDOR ID :: 0x%x\n\r",vendorid);
#endif
		productid=(data[11] *256)+data[10];
#ifdef DEBUG
		printf("PRODUCT ID :: 0x%x\n",productid);
#else
		platform_write("\rPRODUCT ID :: 0x%x\n\r",productid);
#endif
		break;
	}
	case (CONFIG):
	{
		/*device class ID number taken from the http://www.usb.org/developers/defined_class*/
		platform_write("DEVICE class ID :: 0x%x\n",data[14]);
		if (data[14]== 0x03)
#ifdef DEBUG
			printf("Device Type :: HUMAN INTERFACE DEVICE\n");
#else
		platform_write("Device Type :: HUMAN INTERFACE DEVICE\n\r");
#endif
		if (data[14]== 0x08)
#ifdef DEBUG
		printf("Device Type :: MASS STORAGE\n");
#else
		platform_write("Device Type :: MASS STORAGE\n\r");
#endif

		break;
	}
	default:
		break;
	}

}
/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
void in_data_phase(int descriptor_type)
{
	void *regs = musb_mregs;
	UINT16 host_csr0_val_in;
	UINT32 length;
	int flag =1;
	int count =0;
	UINT8 device_type=0;

	/*In data phase ,read the devctl register to determine whether the device is HS or LS
	 *for High speed devices, 64 bytes are read at a time but in case of low speed devices
	 *only 8 bytes are read at a time ,so the read process is repeated to read the entire data
	 *sent by the device */
	device_type = musb_readb(musb_mregs, MUSB_DEVCTL);

	while(flag)
	{
		/*set the REQPKT */
		musb_writew(regs,USB_HOST_CSR0,REQPKT);

		delay(10000);

		if( musb_readl(reg_base,TX_EP0_INTR_REG)& EP0_INTR_STAT)
		{
			/*INTR is cleared */
			musb_writel(reg_base,TX_EP0_INTR_REG,INTR_CLR);
			/*check HOST_CSR0 for any errors*/
			host_csr0_val_in=musb_readw(regs,HOST_CSR0);
			if (!((host_csr0_val_in & RXSTALL)|| (host_csr0_val_in & ERROR) || (host_csr0_val_in & NAK_TIMEOUT)))
			{
				/*wait for RXPCKTRDY*/
				while (!(musb_readw(regs,HOST_CSR0)& RXPCKTRDY_SET));
				/*get the number of bytes in the FIFO by reading RXCOUNT register*/
				 length=musb_readw(regs,RXCOUNT);
				 if (length > 0)
				 {
				  read_fifo(0,descriptor_type,count);
				  /*Clear RXPCKTRDY*/
				  musb_writew(regs,HOST_CSR0,RXPCKTRDY_CLR);
				  count=count+8;
				 }
				 /*if the device is a HS device then read_fifo can read up to 64 bytes of data ,
				  * so the flag is cleared ,if it is a Low speed device ,then data is read 8 bytes
				  * at a time .
				  */
				 if( (length ==0) || (device_type & MUSB_DEVCTL_FSDEV) || (length < 8))
				 {
					 flag=0;
					 /*Clear RXPCKTRDY*/
					  musb_writew(regs,HOST_CSR0,RXPCKTRDY_CLR);
				 }
			}
			else
			{
				if (host_csr0_val_in & RXSTALL)
					printf("in INDATAPHASE -target did not accept the command\n");
				if (host_csr0_val_in & ERROR)
					printf("in INDATAPHASE  -data packet three times without getting any response\n");
				if (host_csr0_val_in & NAK_TIMEOUT)
					printf("in INDATAPHASE -NAK_TIMEOUT\n");
			}
		}
		else
			printf("EP0 interrupt is not active in IN_STATUS_PHASE \n");
	}/*End of while*/
	process_fifo_data(buffer,descriptor_type);
}

//
//void delay(int delay_count)
//{
//	while (delay_count --);
//	return;
//}
/*get_descriptor_packet*/
static UINT8 get_descriptor_config[8] = {0x80,0x06,0x00,0x02,0x00,0x00,0x10,0x00};
static UINT8 get_descriptor_string[8] = {0x80,0x06,0x02,0x03,0x09,0x04,0xFF,0x00};
static UINT8 get_descriptor_device[8] = {0x80,0x06,0x00,0x01,0x00,0x00,0x12,0x00};

/*get_descriptor sends the string packet to get the Manufacturer name ,
 * config packet to get the device class and device descriptor is to get
 * Product ID and Vendor ID
 */

/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
SINT32 get_descriptor (int descriptor_type)
{
	void *regs = musb_mregs;
	UINT16 host_csr0_val;

	/* select the end point index as 0*/
	musb_writeb(regs,EP_SELECT,0x00);

	if (descriptor_type == STRING)
		write_fifo(0, sizeof(get_descriptor_string), get_descriptor_string);
	else if(descriptor_type == DEVICE)
		write_fifo(0, sizeof(get_descriptor_device), get_descriptor_device);
	else if(descriptor_type == CONFIG)
		write_fifo(0, sizeof(get_descriptor_config), get_descriptor_config);

	/*Set SETUPPKT and TXPKTRDY*/
	musb_writew(regs,USB_HOST_CSR0,SETUPPKT|TXPKTRDY);

	delay(1000);
	/*Ensure that TX_EP0_INTR is set */
	if((musb_readl(reg_base,TX_EP0_INTR_REG) & EP0_INTR_STAT))
	{
		/*clear the INTR by writing 1*/
		musb_writel(reg_base,TX_EP0_INTR_REG,INTR_CLR);
		host_csr0_val=musb_readw(regs,HOST_CSR0);

		if (!((host_csr0_val & RXSTALL)|| (host_csr0_val & ERROR) || (host_csr0_val & NAK_TIMEOUT)))
		{
			/*If none of the error occur which implies that SETUP phase is completed successfully
			 *Moving to in_data_phase where the device sends data in response to the command sent
			 * during SETUP phase
			 */
			in_data_phase(descriptor_type);
		}
		else
		{
			if (host_csr0_val & RXSTALL)
				printf("target did not accept the command\n");
			if (host_csr0_val & ERROR)
				printf("data packet three times without getting any response\n");
			if (host_csr0_val & NAK_TIMEOUT)
				printf("NAK_TIMEOUT\n");
		}
		return(SUCCESS);
	}
	else
	{
//		printf("EP0 interrupt is not active after SETUP_command is transmitted\n");
		platform_write("EP0 interrupt is not active after SETUP_command is transmitted\n");
		Uart_stringSend("\r\n-------------------------X-----------------------------");
		return(-1);
	}
}


/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
/*write_fifo manages to load the FIFO with the data */
void write_fifo( int ep_num, UINT16 len, UINT8 *src )
{
    int i;
    volatile UINT32 *ep_fifo ;



     if (usb_num == 0)
     	ep_fifo = &USB0_FIFO0;

     else if (usb_num == 1 )
    	 ep_fifo = &USB1_FIFO0;

    ep_fifo += ep_num;

    /* we can't assume unaligned writes work */
    /* best case is 32bit-aligned source address */
    if( ((UINT32)src & 0x03) == 0 && (len & 0x03) == 0 )
    {
        volatile UINT32 *wr_fifo32 = ep_fifo;
        UINT32 *buf32 = (UINT32 *)src;
        for( i=0; i < len; i+=4 )
        	*wr_fifo32 = *buf32++;
    }
    else if( ((UINT32)src & 0x01) == 0 && (len & 0x01) == 0 )
    {
        volatile UINT16 *wr_fifo16 = (volatile UINT16 *)ep_fifo;
        UINT16 *buf16 = (UINT16 *)src;
        for( i=0; i < len; i+=2 )
        	*wr_fifo16 = *buf16++;
     }
    else
    {
        volatile UINT8 *wr_fifo8 = (volatile UINT8 *)ep_fifo;
        UINT8 *buf8 = src;
        for( i=0; i < len; i++ )
        	*wr_fifo8 = *buf8++;
    }
}

/****
 *\brief:
 *
 *
 *\descr:
 *
 *
 *\param:
 *\return:
 *
 */
STATUS usb_test
(
	void *testargs
)
{
	void *regs ;

	UINT8 reg_val = 0;
    STATUS s32status;
    static int s32init = 0;

	 Uart_stringSend("\r\n==================================="
					 "\r\n            USB Test               "
					 "\r\n===================================\r\n");
    if (s32init == 0)
    {
    s32status = FAILED;
	if ((( (RD_MEM_32(USB_PLL_BASE+0x24)) & 0x00000600) != 0x00000600))
	{
#ifdef DEBUG
		printf ("Enable USB PLL from GEL Script\r\n ");
#else
		Uart_stringSend("Enable USB PLL from GEL Script\r\n ");
#endif
	}

	/* Check if USB Clocks are enabled */
	if ((RD_MEM_32(CM_ALWON2_USB_CLKCTRL) & 0x0F) !=0x2)
	{
#ifdef DEBUG
		printf ("Enable USB Clock from GEL Script\r\n ");
#else
		Uart_stringSend ("Enable USB Clock from GEL Script\r\n ");
#endif
	}

		musb_mregs = (void *)(TI816X_USB0_BASE + USB_MENTOR_CORE_OFFSET);
	    musb_ctrl_base = (void *)TI816X_USB0_BASE;
	    reg_base =  (void *)TI816X_USB0_BASE;
	    usb_num=0;

	regs = musb_mregs;
	musb_platform_init(USB_0);
	musb_start();
	/*	The above two steps puts the controller in session,
		After this its required to poll for session req interrupt
		and determine the type of cable/speed of the device
	*/

#ifdef DEBUG
	printf("Waiting for connect\n");
#else
	Uart_stringSend("Waiting for connect\n\r");
#endif


	while ((musb_readl(reg_base , USB_CORE_INTR_STATUS_REG) & MUSB_INTR_CONNECT) != MUSB_INTR_CONNECT);
	musb_writel(reg_base, USB_CORE_INTR_STATUS_REG, MUSB_INTR_CONNECT);
#ifdef DEBUG
	printf("Connected\n");
#else
	Uart_stringSend("Connected\n\r");
#endif
	delay(1000);

	/*Reset signal to make the USB OTG to DEFAULT state */
	musb_writeb(regs, MUSB_POWER, MUSB_POWER_RESET);
	//at least 20 ms to ensure correct resetting of the target device
	delay(100000);


	/*Software clearing the RESET command*/
	musb_writeb(regs, MUSB_POWER,0x00);
	delay(1000);
	s32init ++;
    }

	/*Manufacturer Name*/
    s32status =	get_descriptor(STRING);
    if(s32status !=	SUCCESS)
    {
    	return(-1);
    }
	/*for PID and VID */
    s32status =	get_descriptor(DEVICE);
    if(s32status !=	SUCCESS)
	{
		return(-1);
	}
	/*for device class*/
    s32status =	get_descriptor(CONFIG);
    if(s32status !=	SUCCESS)
	{
		return(-1);
	}

#ifdef DEBUG
	printf("Checking Speed and cable\n");
#else
	Uart_stringSend("Checking Speed and cable\n\r");
#endif

	reg_val = musb_readb(musb_mregs, MUSB_DEVCTL);

	if ((reg_val & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
	{
#ifdef DEBUG
		printf("USBSS VBUS Stable\n");
#else
		Uart_stringSend("USBSS VBUS Stable\n\r");
#endif
	}

	if (reg_val & MUSB_DEVCTL_HM)
	{
#ifdef DEBUG
		printf("USBSS is a Host\n");
#else
		Uart_stringSend("USBSS is a Host\n\r");

#endif
	}
	else
	{
#ifdef DEBUG
		printf("USBSS is a Device\n");
#else
		Uart_stringSend("USBSS is a Device\n\r");
#endif

	}

	if (reg_val & MUSB_DEVCTL_BDEVICE)
	{
#ifdef DEBUG
		printf("Cable is Type B\n");
#else
		Uart_stringSend("Cable is Type B\n\r");
		s32status = SUCCESS;
#endif
	}
	else
	{
#ifdef DEBUG
		printf("Cable is Type A\n");
#else
		Uart_stringSend("Cable is Type A\n\r");
		s32status = SUCCESS;
#endif
	}

	if (reg_val & MUSB_DEVCTL_FSDEV)
	{
#ifdef DEBUG
		printf("USBSS is a High/Full Speed Device\n");
#else
		Uart_stringSend("USBSS is a High/Full Speed Device\n\r");
#endif
	}
	else if (reg_val & MUSB_DEVCTL_LSDEV)
	{
#ifdef DEBUG
		printf("USBSS is a Low Speed Device\n");
#else
		Uart_stringSend("USBSS is a Low Speed Device\n\r");
#endif
	}
	else
	{
#ifdef DEBUG
		printf("USBSS Unknown device speed\n");
#else
		Uart_stringSend("USBSS Unknown device speed\n\r");
#endif
	}
#ifdef DEBUG
		printf("Waiting for disconnect\n");
#else
//		Uart_stringSend("Waiting for disconnect\n\r");
#endif

#ifdef NO_LONGER_VALIID_IN_LONG_RUN_TEST
	while ((musb_readl(reg_base, USB_CORE_INTR_STATUS_REG) & MUSB_INTR_DISCONNECT) != MUSB_INTR_DISCONNECT);
	musb_writel(reg_base, USB_CORE_INTR_STATUS_REG, MUSB_INTR_DISCONNECT);
#ifdef DEBUG
		printf("Disconnected\n");
#else
		Uart_stringSend("Disconnected\n\n\r");
#endif
#endif

#if 1
		if (s32init > 1)
	musb_writeb(musb_mregs, MUSB_DEVCTL, (reg_val & ~MUSB_DEVCTL_SESSION));

#endif
		Uart_stringSend("\r\n-------------------------X-----------------------------");
	return (s32status);
}
