/*
 * 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:    Wlan.c
 * \brief :  sorce file for WLAN controller.
 *
 * \n        This file initialize the WLAN controller and it will access the
 * \n        WLAN card for future application.
 */
 
/*
 *====================
 *  Includes
 *====================
 */
#include "stdio.h"
#include "sdio.h"
#include "Wlan.h"
#include "DM8127_types.h"
#include "Dm8127_Platform.h"
#include "gpio.h"


/*
 *====================
 *  Define
 *====================
 */
#define WLAN_EN_GPIO		(27u)

/*
 *=====================
 * Function definitions
 *=====================
 */

/***
 * \brief : WLAN chip Enable routine.
 *
 * \Descr :Enables the wlan chip by sending by driving wlan_enable pin high
 *
 * \param : VOID
 * \return: VOID
 */
void enable_wlanchip
(
	void
)
{
	UINT32 regval;
	
	/* WLAN_EN_GPIO is GPIO 27th in Bank 0 */
	
	/* Reset GPIO Subsystem */
	GPIO0_SYSCONFIG = 0x00000020; /* Software Reset */
	delay(0x90000);
	GPIO0_SYSCONFIG = 0x100; /* no-idle */
	
	/* Output Enable GPIO27 in Bank 1 */
	regval = GPIO1_OE;
	regval = regval & ~(1 << WLAN_EN_GPIO);  
	GPIO0_OE = regval;

	/* Set WLAN_ENABLE High */
	GPIO0_DATAOUT |= (1 << WLAN_EN_GPIO);
	GPIO0_SETDATAOUT |= (1 << WLAN_EN_GPIO);
	delay(100000);
	
	/* Set WLAN_ENABLE Low */
	regval = GPIO0_DATAOUT;
	regval = regval & ~(1 << WLAN_EN_GPIO);
	GPIO0_DATAOUT = regval;
	regval = GPIO0_SETDATAOUT;
	regval = regval & ~(1 << WLAN_EN_GPIO);
	GPIO0_SETDATAOUT = regval;
	delay(100000);

	printf ("\tTrying to enable wlan chip\n");		
	/* Set WLAN_ENABLE High */
	GPIO0_DATAOUT |= (1 << WLAN_EN_GPIO);
	GPIO0_SETDATAOUT |= (1 << WLAN_EN_GPIO);
	
	/* Wait for chip to settle */
    delay(200000); 

}
#if 0
/*
 * Enables Wlan clock
 * 
 * Parameters:
 * Sdio_base:	Base Address of the Wlan controller
 * clock:       Clock Frequency
 * 
 * Returns:
 *   -1			On Failure
 *    0         On Success
 */
SINT32 set_Wlan_clock(SDIO_controller_t *Sdio_base, UINT32 clock)
{
	UINT32 val;
	UINT32 regval;
	
	/* disables, cen and set data timeout value */
	regval = readl(&Sdio_base->sysctl);
	regval &= ~(0xF0007); 
	regval |= (0xe << 16); 

	switch (clock) {
	case CLK_INITSEQ:
		val = (SDWlan_REF_CLK * 1000 / (160) / 2);
		break;
	case CLK_400KHZ:
		val = (SDWlan_REF_CLK * 1000) / (1600); /* 1.6MHZ */
		break;
	case CLK_12MHZ:
		val = (SDWlan_REF_CLK * 1000 / (12000));
	    break;
	case CLK_24MHZ:
		val = (SDWlan_REF_CLK * 1000 / (24000));
		break;
	case CLK_4MHZ:
		val = (SDWlan_REF_CLK * 1000 / (4000));
		break;
	case CLK_2MHZ:
		val = (SDWlan_REF_CLK * 1000 / (2000));
		break;
	default:
		printf ("Clock Frequency not supported\n");
		return FAILURE;
	}

	/* Select clock frequency */
	regval &= ~(0xffc0);
	regval |= (val << 6);

	regval |= (0x1); /* enable clock */
	writel(regval, &Sdio_base->sysctl);
	delay(4000);
	
	/* Wait until stable? */
	while ((readl(&Sdio_base->sysctl) & 0x2) == 0x0);

	writel(readl(&Sdio_base->sysctl) | 0x4, &Sdio_base->sysctl);
	delay(4000);

	return SUCCESS;
}


/*
 *\brief : Initialize the WLAN Controller
 *\Descr : The routine will enable the wifi device and it wil configure
 *         the the WLAN module.
 *
 *@Param : Sdio_base [IN/OUT]  Base Address of the SDIO controller
 *
 *@Returns: VOID
 * 
 */ 
void Wlaninit (SDIO_controller_t *Sdio_base)
{
	UINT32 regval;

//	enable_wlanchip();;
	
	/**
	 *  software reset is given to HSSdio_base0 (HSWlan module) to work with
	 *  thw WLAN module.
	 */

	regval = readl(&Sdio_base->sysconfig);
	writel(regval | 0x2, &Sdio_base->sysconfig);

	/* reset done? */
	while ((readl(&Sdio_base->sysstatus) & 0x1) == 0);

	/* reset the host controller */
	regval = readl(&Sdio_base->sysctl);
	regval |= (1 << 24); /* SRA */
	writel(regval, &Sdio_base->sysctl);
	
	/* reset done? */
	while ((readl(&Sdio_base->sysctl) & (1 << 24)) != 0x0);

	/* Support for 1.8V and 3V */
	//writel(0x6000000, &Sdio_base->capa);
	writel(0x6E10080, &Sdio_base->capa);
	
	/* 1 Bit Mode, 3V Bus Voltage (SVDS) and Bus Pwr Off */
	writel(0x00000C00, &Sdio_base->hctl);
	
	/* Set Block Size to 512 Bytes */
	writel(0x200, &Sdio_base->blk);
	
	/* Support for 1.8V, 3V cards */
	writel(readl(&Sdio_base->capa) | 0x6000000, &Sdio_base->capa);

	/* Functional Mode, No INIT Sequence */
	regval = readl(&Sdio_base->con) & (0x3 << 9);
	writel(regval, &Sdio_base->con);
	delay(10);

	device_clock(Sdio_base, CLK_INITSEQ_WLAN);
	
	/* Switch on Bus Power */
	writel(readl(&Sdio_base->hctl) | (1 << 8), &Sdio_base->hctl);

	/* Enable Interrupts */
	writel(0x307F0033, &Sdio_base->ie);
	
	/* Send Initialisation Sequence for 80 clock cycles */
	writel(0x00000602, &Sdio_base->con);
	delay(10);
	
	/* Send CMD0 */
	writel(CMD0, &Sdio_base->cmd);
	while( (readl(&Sdio_base->stat) & 0x1) == 0);
	writel(0x1, &Sdio_base->stat);

	/* Send CMD0 */
	writel(CMD0, &Sdio_base->cmd);
	while( (readl(&Sdio_base->stat) & 0x1) == 0);
	writel(0x1, &Sdio_base->stat);
	
	/* End Init sequence */
	regval = readl(&Sdio_base->con) & ~0x2;
	writel(regval, &Sdio_base->con);
}
#endif
/***
 * \brief : Command send routine for the WLAN.
 *
 * \Param : Sdio_base:  Base Address of the SDIO controller
 * \n       cmd      :  command to be sent
 * \n       arg      :  arguments to the command
 * 
 * 
 * \return:  SUCCESS                       On Success
 * \n   	 Contents of HS_STAT register  On Failure
 */ 
#if 0
SINT32 send_cmd(SDIO_controller_t * Sdio_base, UINT32 cmd, UINT32 arg)
{
	UINT32 regval;
	UINT32 status;

	/* Software reset cmd and dat lines */
	regval = readl(&Sdio_base->sysctl);
	regval |= 0x6000000;
	writel(regval, &Sdio_base->sysctl);

	while( (readl(&Sdio_base->sysctl) & 0x6000000) != 0);

	/* wait until you are allowed to send cmd */
	while ((readl(&Sdio_base->pstate) & 0x2) == 0x2);
	
	writel(0x307F0037, &Sdio_base->stat);
	writel(0x200, &Sdio_base->blk);
	
	writel(arg, &Sdio_base->arg);
	writel(cmd, &Sdio_base->cmd);
	delay(0x1000);

	for ( ;; ) {
		do {
			status = readl(&Sdio_base->stat);
		} while (status == 0);

		if (status & STAT_ERRI)
			return status;
		
		if (status & STAT_CC) { /* command complete */
			writel(STAT_CC, &Sdio_base->stat);
			return SUCCESS;
		}
	}
}

/*
 *\brief : Detect an SDIO (WLAN) Card
 * 
 *@Param : Sdio_base           Base Address of the SD/SDIO controller
 *         high_capacity_card  Returns 1 if a High Capacity SD card is found.
 * 
 *@Return: The Type of Card
 * 
 *         (1):	SDIO Card is Found
 *         (2):	SD Card is Found   (SD_CARD)
 *         (3):	Wlan Card is Found
 *         (-1):	Card Not Found (UNKNOWN_CARD)
 */ 
SINT32 detect_card (SDIO_controller_t *Sdio_base, SINT32 * high_capacity_card)
{
	UINT32 ocr, ocr_rcvd;
	UINT32 rca;
	SINT32 cardtype ;
	SINT32 err;
	SINT32 retry;
	SINT32 io_funcs;
	SINT32 ver2_card = 0;

	/* Variable initialization */
	ocr      = INIT_VALUE;
	ocr_rcvd = INIT_VALUE;
	rca      = INIT_VALUE;
	retry    = INIT_VALUE;
	io_funcs = INIT_VALUE;
	ver2_card= INIT_VALUE;
	cardtype = UNKNOWN_CARD;
	err      = FAILED;

	set_Wlan_clock(Sdio_base, CLK_400KHZ);

	err = send_cmd(Sdio_base, SDWlan_CMD5, 0x0000);
	if (!err) {
		ocr_rcvd =  readl(&Sdio_base->rsp10);
		io_funcs = (ocr_rcvd >> 28) & 0x7;
		
		do {
			err = send_cmd(Sdio_base, SDWlan_CMD5, ((ocr_rcvd >> 8) & 0xffffff));
			if (err)
				return -err;
	
		} while ( (readl(&Sdio_base->rsp10) & 0x80000000) == 0); /* Is card Ready? */
		
		printf ("\tDetected an SDIO Card with %d functions\n", io_funcs);
		cardtype = SDIO_CARD;
	} else {
		/* Re-Init Card */
		writel(SDWlan_CMD0, &Sdio_base->cmd);
		while( (readl(&Sdio_base->stat) & 0x1) == 0);
		writel(0x1, &Sdio_base->stat);
	
		err = send_cmd(Sdio_base, SDWlan_CMD8, 0x000001AA);
		if (!err)
			ver2_card = 1;
		else
			ver2_card = 0;

		ocr = 0x1FF << 15;
		err = send_cmd(Sdio_base, SDWlan_CMD55, 0x00000000);
		if (!err) {
			cardtype = SD_CARD;
			ocr = ocr | (ver2_card << 30);
			err = send_cmd(Sdio_base, SDWlan_ACMD41, ocr);
		} else {
			cardtype = Wlan_CARD;
			err = send_cmd(Sdio_base, SDWlan_CMD1, ocr);
		}

		if (err)
			return -err;

		ocr_rcvd =  readl(&Sdio_base->rsp10);
		if (ver2_card && (ocr_rcvd & 0x40000000))
			*high_capacity_card = 1;
		else
			*high_capacity_card = 0;

		/* is card is ready? */
		for (retry = 0; retry < 1000 && ((ocr_rcvd & 0x80000000) == 0); retry++) {
			if (cardtype == SD_CARD)
				send_cmd(Sdio_base, SDWlan_CMD55, 0x00000000);

			err = send_cmd(Sdio_base, SDWlan_ACMD41, ocr);
			if (err)
				return -err;

			ocr_rcvd =  readl(&Sdio_base->rsp10);
		}

		if (ocr_rcvd & (1 << 31) == 0)
			return -1; /* card not ready yet */

		err = send_cmd(Sdio_base, SDWlan_CMD2, ocr_rcvd);
		if (err)
			return -err;
	
	}

	if (cardtype == SD_CARD || cardtype == SDIO_CARD) {
		err = send_cmd(Sdio_base, SDWlan_CMD3, 0x00000000); /* Get RCA */
		rca =  readl(&Sdio_base->rsp10) >> 16;
	} else if (cardtype == Wlan_CARD) {
		rca = 0x1234 << 16;
		err = send_cmd(Sdio_base, Wlan_CMD3, rca); /* Set RCA */
	}
	if (err)
		return -err;
	
	err = send_cmd(Sdio_base, SDWlan_CMD7, rca << 16); /* set rca */
	if (err)
		return -err;

		/* The WL1271 does not respond to any higher freq than 4MHZ during ID reading Phase */
		/* Setting the clock to 2MHZ during the ID reading Phase as at higher frequency card does not respond */
	err = set_Wlan_clock(Sdio_base, CLK_24MHZ);
	if (err)
		return -err;

	return cardtype;
}
#endif
#ifdef NOLONGER_VALID
/*
 * Read a Block
 * 
 * Parameters:
 * Sdio_base:			Base Address of the SD/SDIO controller
 * high_capacity_card:	1 if it is a High Capacity Card
 * block:				Block Address
 * nBytes:				size of the block
 * buf:					buffer to be read into
 * 
 * Returns:		
 *  < 0					On Error
 *   0					On Success	
 */ 
INT32 bread (SDIO_controller_t *Sdio_base, INT32 high_capacity, \
				INT32 block_num, UINT32 nbytes, \
				UINT8 *buf)
{
	UINT32 arg;
	UINT32 status;
	INT32 err;
	INT32 i;

	if (high_capacity)
		arg = block_num; /* sector mode */
	else
		arg = block_num * SECTOR_SIZE; /* byte mode */

	err = send_cmd(Sdio_base, SDWlan_CMD17, arg);
	if (err)
		return -err;
	
	do {
		status = readl(&Sdio_base->stat);
	} while ( (status & STAT_BRR) == 0); /* ready to read? */

	for (i = 0; i < SECTOR_SIZE / 4; i++) {
		do {
			status = readl(&Sdio_base->stat);
		} while ( (status & STAT_BRR) == 0); /* ready to read? */

		*(UINT32 *)buf = readl(&Sdio_base->data);
		buf = buf + 4;
	}
	
	writel(readl(&Sdio_base->stat) | STAT_BRR, &Sdio_base->stat);
	
	return SUCCESS;
}

/*
 * Write a Block
 * 
 * Parameters:
 * Sdio_base:			Base Address of the SD/SDIO controller
 * high_capacity_card:	1 if it is a High Capacity Card else, 0.
 * block:				Block Address
 * nBytes:				size of the block
 * buf:					data to be written
 * 
 * Returns:		
 *  < 0					On Error
 *   0					On Success	
 */ 
INT32 bwrite (SDIO_controller_t *Sdio_base, INT32 high_capacity, \
			INT32 block_num, UINT32 nbytes, UINT8 *buf)
{
	UINT32 arg;
	UINT32 status;
	INT32 err;
	INT32 i;

	if (high_capacity)
		arg = block_num; /* sector mode */
	else
		arg = block_num * SECTOR_SIZE; /* byte mode */

	err = send_cmd(Sdio_base, SDWlan_CMD24, arg);
	if (err)
		return -err;

	do {
		status = readl(&Sdio_base->stat);
	} while ( (status & STAT_BWR) == 0); /* ready to write? */

	for (i = 0; i < SECTOR_SIZE / 4; i++) {
			writel( *(UINT32 *)buf, &Sdio_base->data);
			buf = buf + 4;
	}
	
	writel(readl(&Sdio_base->stat) | STAT_BWR, &Sdio_base->stat);

	return SUCCESS;
}

#endif
/***
 * \brief: chip ID detection routine.
 *
 * \b \descr: This function takes sdio base address as input and it send command
 * \n         to the sdio device top get the vendor information from the chip.
 *
 * \n         Once it collect the vendor ID from the device it update the same
 * \n         to the second parameter (as output).
 *
 * \param:    Sdio_base  [IN]  base address of the SDIO device
 * \n         vendor     [OUT] CHip ID
 *
 * \return:   SUCCESS  successfully id detection
 * \n         FAILURE  failed to detect the chip ID
 */
int get_sdio_chipid
(
	SDIO_controller_t * Sdio_base,
	UINT32 *vendor
)
{
	
	UINT32 loop, cis_offset;
	UINT32 arg;
	SINT32 err;

	cis_offset = 0;
	
	for(loop = 0xb; loop >= 0x9; loop--) {
		arg = (loop & 0x1FFFF) << 9; /* CIS Pointer Offset 9-0xB (3 Bytes) */	

		/* Put if else condition here*/
		err = send_cmd(Sdio_base, WLAN_CMD52, arg);
 		if (err)
 			return -err;
 
		cis_offset = (cis_offset << 8) +  (readl(&Sdio_base->rsp10) & 0xff);
	}
	
	for(loop = 0; loop < 50; loop++)
   	{
		arg = (cis_offset & 0x1FFFF) << 9;
		/* Put if else condition here*/
		err = send_cmd(Sdio_base, WLAN_CMD52, arg);
		if (err)
 			return -err;
 			
 		if ( (readl(&Sdio_base->rsp10) & 0xff) == 0x20) /* manfacturer tuple found */
      		break;
      
      // next tuple
 		cis_offset++;
		arg = (cis_offset & 0x1FFFF) << 9;

		/* Put if else condition here*/
		err = send_cmd(Sdio_base, WLAN_CMD52, arg);
		if (err)
 			return -err;
 			
		cis_offset = cis_offset + (readl(&Sdio_base->rsp10) & 0xff) + 1;
	}
   
	if (loop < 50)
	{
		// Manufacture tuple found
		cis_offset += 2;

		*vendor = 0;
		for(loop = 0; loop < 4; loop++)
		{
			arg = (cis_offset & 0x1FFFF) << 9;
			/* Put if else condition here*/
			err = send_cmd(Sdio_base, WLAN_CMD52, arg);
			if (err)
				return -err;
				
			*vendor += ((readl(&Sdio_base->rsp10) & 0xff)  << (loop * 8));
			cis_offset++;
		}
		
		return SUCCESS;	
   } else
   		printf ("UNABLE to read Manufacturer tuple\n");
   
   return FAILURE;
}
