/*****************************************************************************
* Copyright (c) 2005 Digi International Inc., All Rights Reserved
* 
* This software contains proprietary and confidential information of Digi
* International Inc.  By accepting transfer of this copy, Recipient agrees
* to retain this software in confidence, to prevent disclosure to others,
* and to make no use of this software other than that for which it was
* delivered.  This is an unpublished copyrighted work of Digi International
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
* prohibited.
* 
* Restricted Rights Legend
*
* Use, duplication, or disclosure by the Government is subject to
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
* Technical Data and Computer Software clause at DFARS 252.227-7031 or
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -
* Restricted Rights at 48 CFR 52.227-19, as applicable.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
*
*****************************************************************************/

#include <string.h>
#include "nastatus.h"
#include "tx_api.h"
#include "iop_api.h"
#include "sdioPic.h"
#include "sdioApi.h"
#include "sdio_def.h"

/*ad*
 *
 * NET+OS supports SDIO on platforms with the required hardware support.
 *
 * The SDIO driver provides initialization services and an applications
 * interface for NET+OS to manage SD memory cards and SD IO cards. 
 * The driver API allows higher level application software to access
 * the functions of an SD card through the command set specified in 
 * SD Specifications Part 1 Physical Layer Specification Version 2.00 and
 * SD Specifications Part E1 SDIO Specification Version 2.00.
 *
 * The general semantics for the API are:
 *
 * #define BSP_INCLUDE_SDIO_DRIVER TRUE in bsp_drivers.h
 *
 * This causes netosStartup() to call naSdioInitialize(), which 
 * loads the I/O processor (called the flexible interface module, or FIM)
 * with the microcode, configures the GPIO pins,
 * and starts the FIM. The microcode supports the SD specifications listed
 * above. It support only single block reads and writes; multiple block
 * reads and writes are not supported.
 *
 * From the SD application call naSdioCardInitialize, which establishes 
 * communication with the SD card, identifies the card's capabilities, and
 * brings the card into operational state. From that point on it is up to the
 * application to communicate with the card and to exchange whatever data
 * is required for the application to work.
 *
 * @external
 * @category BSP:Device_Drivers:SDIO
 * @include     <sdioApi.h>
 * @library     bsp
 * @platform connectcore9p9215_a
 * @since 7.3
 * @overview SDIO Driver
 *
 * @see @link NaSdioTable_t
 *
 *ad*/

naSdioCard_t sdioCardCtl[NA_SDIO_MAX_INTERFACES];

extern const unsigned naSdioTableSize;
extern unsigned int naSdioBytesRcvd[];
extern unsigned int naSdioBytesSent[];

static void naSdioSetCapabilities(naSdioCard_t *pC)
{
	if (pC->memoryPresent & pC->IOcapable)
	{
		if (pC->CCS)
			pC->type = SDIO_CARD_TYPE_HC_COMBO;
		else
			pC->type = SDIO_CARD_TYPE_SC_COMBO;
	}
	else if (pC->memoryPresent & !pC->IOcapable)
	{
		if (pC->CCS)
			pC->type = SDIO_CARD_TYPE_HC_MEM;
		else
			pC->type = SDIO_CARD_TYPE_SC_MEM;
	}
	else if (!pC->memoryPresent && pC->IOcapable)
	{
		pC->type = SDIO_CARD_TYPE_IO;
	}
	else
	{
		pC->type = SDIO_CARD_TYPE_NONE;
	}
}

static NaStatus naSdioInitiateCommand(unsigned interface, NaSdioCommand_t *initCmd, unsigned cmd, unsigned arg, NaSdioResponse_t rsp)
{
	NaStatus rc;

#ifdef	SDIO_VERBOSE
	printf("sending CMD%d - %08x\n\r", cmd, arg);
#endif
	initCmd->commandIndex = cmd;
	/* assemble the command word for cmd 52. write to function 0
	 * register 6 a value of 8  */
	initCmd->commandArgument = arg;
	initCmd->responseType = rsp;
	initCmd->transmitDataLength = 0;
	initCmd->transmitData = NULL;
	initCmd->receiveDataLength = 0;
	initCmd->receiveData = NULL;
	initCmd->timeout = NA_SDIO_CMD_TIMEOUT;

	rc = naSdioCardExecuteCommand(interface, initCmd);
#ifdef	SDIO_ERROR
	if (rc != NASTATUS_SUCCESS)
		printf("C%d result for cmd %d rc 0x%08x\n\r", interface,
						cmd, (unsigned int) rc);
#endif
	return rc;
}

static NaStatus naSdioSetBusWidth(unsigned interface, unsigned option)
{
	naSdioCard_t *sdioCard = &sdioCardCtl[interface];
	NaSdioCommand_t initCmd;
	NaStatus rc = 0;

	if ((option != NA_SDIO_BUS_WIDTH4) && (option != NA_SDIO_BUS_WIDTH1))
	{
		return NASTATUS_SDIO_BAD_ARGUMENT;
	}
	/* SD MEM cards do it with ACMD6 */
	if (sdioCard->memoryPresent != 0)
	{
		/* send ACMD6 to set bus width */
		rc = naSdioInitiateCommand(interface, &initCmd, NA_SDIO_ACMD6,
					   	NA_SDIO_BUS_WIDTH4, NA_SDIO_R1);
		if (rc != NASTATUS_SUCCESS)
		{
#ifdef	SDIO_ERROR
			printf("ACMD6 result rc 0x%08x\n\r", (unsigned int) rc);
#endif
		}
		else
		{
			if ((initCmd.response[0] & SDIO_HOST_CMD_MASK) ==
					   	(NA_SDIO_ACMD6 & SDIO_HOST_CMD_MASK))
			{
				sdioCard->cardStatus = ((initCmd.response[1] << 24) |
					   	(initCmd.response[2] << 16) |
						(initCmd.response[3] << 8) |
					   	(initCmd.response[4]));

				/* check out-of-range bit in status register */
				if ((sdioCard->cardStatus & 0x80000000) == 1)
				{
					/* bus width was rejected? */
					rc = NASTATUS_ERROR;
				}
				/* take note of card state in bits 12:9 */
				sdioCard->cardState = (sdioCard->cardStatus & 0x1e00) >> 9;
			}
		}
	}
	/* SD IO cards require setting the control bits in the CCCR */
	if (sdioCard->IOcapable == 1)
	{
		/* send CMD52 to set bus width */

		rc = naSdioInitiateCommand(interface, &initCmd, NA_SDIO_CMD52,
			SDIO_CMD52_WRT_FLG | (7 << SDIO_CMD52_ADDR_SHFT) |
		   	NA_SDIO_BUS_WIDTH4, NA_SDIO_R5);
		if (rc != NASTATUS_SUCCESS)
		{
#ifdef	SDIO_ERROR
			printf("CMD52 result rc 0x%08x\n\r", (unsigned int) rc);
#endif
		}
		else
		{
			if ((initCmd.response[0] & SDIO_HOST_CMD_MASK) != NA_SDIO_CMD52)
			{
				rc = NASTATUS_BAD_RSP_CODE;
			}
			else
			{
				unsigned char status = initCmd.response[3];
				sdioCard->ioState = (status >> 4) & 0x3;
#ifdef	SDIO_ERROR
				/* TODO name these error bits */
				if (status & 0xcb)
				{
					printf("response to CMD52 shows error %02x\n\r", status);
				}
#endif
			}
		}
	}
	if (rc == NASTATUS_SUCCESS)
	{
		sdioCard->busWidth = (NaSdioBusWidth_t) option;
	}
	return rc;
}

/* API functions */

/* This parsing function has ample opportunity for increasing its granularity */

NaStatus naSdioCardParseResponse(unsigned commandIndex,
		NaSdioResponse_t responseType, unsigned char *rawResponse,
				NaSdioCookedResponse_t *cookedResponse)
{
	NaStatus rc = NASTATUS_SUCCESS;
#ifdef SDIO_APP_VERBOSE
	printf("Parse cmd %d rsp %d (%02x %02x %02x %02x %02x %02x %02x)\n\r",
			commandIndex, responseType,
		   	rawResponse[0], rawResponse[1], rawResponse[2], rawResponse[3],
		   	rawResponse[4], rawResponse[5], rawResponse[6]);
#endif
	if (responseType == NA_SDIO_R1)
	{
		if ((rawResponse[0] & SDIO_HOST_CMD_MASK) ==
					   	(unsigned char) commandIndex)
		{
			cookedResponse->r1.cardStatus =
				  ((rawResponse[1] << 24) | (rawResponse[2] << 16) |
						(rawResponse[3] << 8) | (rawResponse[4]));
		}
		else
		{
			rc = NASTATUS_BAD_RSP_CODE;
		}
	}
	else if (responseType ==  NA_SDIO_R2)
	{
		/* commands 2, 10 for CID, 9 for CSD	*/
		if ((rawResponse[0] & SDIO_HOST_CMD_MASK) != SDIO_HOST_CMD_MASK)
		{
			rc = NASTATUS_BAD_RSP_CODE;
		}
		if ((commandIndex == NA_SDIO_CMD2) || (commandIndex == NA_SDIO_CMD10))
		{
			/* decode CID */
			strncpy((char *) &cookedResponse->r2.cidCsd[0],
						(char *) &rawResponse[1], NA_SDIO_R2_RESPONSE_LENGTH);
		}
		else if (commandIndex == NA_SDIO_CMD9)
		{
			/* decode CSD */
			strncpy((char *)  &cookedResponse->r2.cidCsd[0],
						(char *) &rawResponse[1], NA_SDIO_R2_RESPONSE_LENGTH);
		}
		else
		{
			rc = NASTATUS_SDIO_BAD_ARGUMENT;
		}
	}
	else if (responseType ==  NA_SDIO_R3)
	{
		if ((rawResponse[0] & SDIO_HOST_CMD_MASK) == SDIO_HOST_CMD_MASK)
		{
			/* bits 15-23 are the voltage ranges. We support 0xfc0000 --
			 * 3.0 to 3.6 volts */
			cookedResponse->r3.ocr =
				   	((rawResponse[1] << 24) | (rawResponse[2] << 16) |
						(rawResponse[3] << 8) | (rawResponse[4]));
		}
		else
		{
			rc = NASTATUS_BAD_RSP_CODE;
		}
	}
	else if (responseType ==  NA_SDIO_R4)
	{
		if ((commandIndex == NA_SDIO_CMD5) && 
				((rawResponse[0] & SDIO_HOST_CMD_MASK) == SDIO_HOST_CMD_MASK))
		{
			cookedResponse->r4.numberOfIoFunctions =
				   	(rawResponse[1] & 0x70) >> 4;
			cookedResponse->r4.isMemoryPresent = (rawResponse[1] & 8);

			cookedResponse->r4.ioOcr = (rawResponse[2] << 16) |
						(rawResponse[3] << 8);
		}
		else
		{
			rc = NASTATUS_SDIO_BAD_ARGUMENT;
		}
	}
	else if (responseType ==  NA_SDIO_R5)
	{
		if ((rawResponse[0] & SDIO_HOST_CMD_MASK) == NA_SDIO_CMD52)
		{
			cookedResponse->r5.responseFlags = rawResponse[3];
			cookedResponse->r5.rwData = rawResponse[4];
		}
		else
		{
			rc = NASTATUS_BAD_RSP_CODE;
		}
	}
	else if (responseType ==  NA_SDIO_R6)
	{
		if ((rawResponse[0] & SDIO_HOST_CMD_MASK) == NA_SDIO_CMD3)
		{
			cookedResponse->r6.rca = (rawResponse[1] << 8) | (rawResponse[2]);
			cookedResponse->r6.cardStatus =
				   	(rawResponse[3] << 8) | (rawResponse[4]);
		}
		else
		{
			rc = NASTATUS_BAD_RSP_CODE;
		}
	}
	else if (responseType ==  NA_SDIO_R7)
	{
		if ((rawResponse[0] & SDIO_HOST_CMD_MASK) == NA_SDIO_CMD8)
		{
			cookedResponse->r7.echoPattern = rawResponse[4];
			cookedResponse->r7.voltageAccepted =
				   	(NaSdioVoltageWindow_t) (rawResponse[3] & 0xf);
		}
		else
		{
			rc = NASTATUS_BAD_RSP_CODE;
		}
	}
	else
	{
		rc = NASTATUS_SDIO_BAD_ARGUMENT;
	}
	return rc;
}

/*
 * Returns a copy of the card's CSD in the location pointed to by pCsd.
 * Assumes sufficient space at that location (16 bytes) to hold the CSD.
 * Returns failure if the card is not memory-capable or is not initialized.
 */

NaStatus naSdioQueryCSD(unsigned interface, unsigned char *pCsd)
{
	naSdioCard_t *pQ = &sdioCardCtl[interface];

	if ((pQ->memoryPresent) && ( pQ->memoryInited))
	{
		memcpy(pCsd, pQ->CSD, SDIO_CSD_LEN);
		return NASTATUS_SUCCESS;
	}
	else
		return NASTATUS_ERROR;
	
}

/*ad*
 *
 * The application uses this function to instruct the driver to
 * send the command to the card, collect the response, and transfer 
 * the response back to the caller.
 * Application specific commands, which are "escaped" two-command sequences
 * consisting of a CMD55 followed by another command, are indicated by
 * or'ing in the 0x1000 bit to the command value. This has been done
 * in the commandIndex definitions.
 *
 * @category BSP:Device_Drivers:SDIO
 * @since 7.3
 * @external
 *
 * @param interface	Index of the FIM in the naSdioTable
 * @param command	Pointer to command structure of type NaSdioCommand_t.
 *
 * @return NASTATUS_SUCCESS	The command was sent to the card, and the card
 * responded as requested. If a response was desired, the response is in 
 * the command structure.
 * @return NASTATUS_ERROR	The card did not respond, responded incorrectly
 * to the command, or the transmission to the card failed.
 *
 * @see @link NaSdioCommand_t
 *
 *ad*/

NaStatus naSdioCardExecuteCommand(unsigned interface, NaSdioCommand_t * command)
{
	naSdioCard_t *sdioCard = &sdioCardCtl[interface];
	int dataRead = 0;
	int dataWrt = 0;
	int rspLen = 0;
	unsigned cmd = command->commandIndex;
	unsigned char opCtl = (sdioCard->busWidth == NA_SDIO_BUS_WIDTH4) ?
		   	SDIO_FIFO_TX_BW4 : 0;
	unsigned int blkSz = 0;
	unsigned int arg = command->commandArgument;
	NaStatus rc;
#ifdef	SDIO_APP_VERBOSE
	printf("ExCmd: %d %04x\n\r",
				   	command->commandIndex, command->commandArgument);
#endif
	/* consider the disable crc option setting */
	if (sdioCard->disableCrc)
		opCtl |= SDIO_FIFO_TX_DISCRC;

	if (cmd & NA_SDIO_APP_SPECIFIC)
	{
		/* precede with CMD55 "escape" code */
		NaSdioCommand_t cmd55;

		cmd55.commandIndex = NA_SDIO_CMD55;
		cmd55.commandArgument = sdioCard->sdRca << 16;
		cmd55.responseType = NA_SDIO_R1;	/* R1 */
		cmd55.transmitDataLength = 0;
		cmd55.transmitData = NULL;
		cmd55.receiveDataLength = 0;
		cmd55.receiveData = NULL;
		cmd55.timeout = NA_SDIO_CMD_TIMEOUT;

#ifdef	SDIO_VERBOSE
		printf("sending NA_SDIO_CMD55 %x\n\r", cmd55.commandArgument);
#endif

		/* slightly recursive */
		if ((rc = naSdioCardExecuteCommand(interface, &cmd55))
					   	!= NASTATUS_SUCCESS)
		{
			return rc;
		}
		/* TODO check R1 cardStatus for errors */
		/* if successful, cmd55.response is R1 with card status  */
		if ((cmd55.response[0] & SDIO_HOST_CMD_MASK) == NA_SDIO_CMD55)
		{
			sdioCard->cardStatus = ((cmd55.response[1] << 24) |
					   	(cmd55.response[2] << 16) |
						(cmd55.response[3] << 8) |
					   	(cmd55.response[4]));
			/* take note of card state in bits 12:9 */
			sdioCard->cardState = (sdioCard->cardStatus & 0x1e00) >> 9;
		}
	}
	if ((cmd == NA_SDIO_CMD2) || (cmd == NA_SDIO_CMD9) ||
				   	(cmd == NA_SDIO_CMD10))
	{
		opCtl |= SDIO_FIFO_TX_136RSP; /* response is R2 -- 136 bits */
		rspLen = 19;
	}
	else if (cmd != NA_SDIO_CMD0)	/* expect no response to reset */
	{
		opCtl |= SDIO_FIFO_TX_48RSP;
		rspLen = 8;
	}
	/* initiate a DMA transaction through the IOP driver */
	if (cmd == NA_SDIO_CMD24)	/* CMD24 write single block */
	{
		blkSz = command->transmitDataLength;
		opCtl |= SDIO_FIFO_TX_BLKWR;	/* block write */
		arg = command->commandArgument; /* block address */
		dataWrt = 1;
	}
	if ((cmd == NA_SDIO_CMD17)	/* CMD17 read single block */
		|| (cmd == NA_SDIO_CMD18)	/* CMD18 read multiple block */
		|| (cmd == NA_SDIO_CMD30)	/* CMD30 send write protect */
		|| (cmd == NA_SDIO_ACMD51)	/* ACMD51 send SCR */
		|| (cmd == NA_SDIO_ACMD56))	/* ACMD56 general cmd in read mode */
	{
		dataRead = 1;
		blkSz = command->receiveDataLength;
		opCtl |= SDIO_FIFO_TX_BLKRD;	/* block read */
	}
	if ((rc = NaSdioSendCmd(interface, (unsigned char) (cmd & 0xff),
				blkSz, command->transmitData,
				opCtl, arg)) != NASTATUS_SUCCESS)
	{
		return rc;
	}
	/* for a block read, we must get response and data block both, but
	 * they can come in any order.
	 * The data block is accompanied by CRC and FIM calculated CRC (for
	 * comparison).
	 * The CRC is 8 bytes for a 4 bit bus, and 2 bytes for a 1 bit bus. */
	if (dataRead == 1)
	{
		if ((rc = NaSdioGatherResponseAndData(interface, command,
				blkSz, rspLen)) != NASTATUS_SUCCESS)
		{
			return rc;
		}
	}
	else if (dataWrt == 1)	/* get 8 byte response plus CRC status */
	{
		if ((rc = NaSdioGatherResponse(interface, command->response,
									   	rspLen + 1)) != NASTATUS_SUCCESS)
		{
			return rc;
		}
		if (command->response[7] != SDIO_CARD_CRC_STATUS_OK)
		{
#ifdef	SDIO_ERROR
			printf("xmt block transfer failed crc check 0x%02x\n\r",
						   	(int) command->response[7]);
#endif
			return NASTATUS_SDIO_CARD_CRCERR; /* card crc check failed */
		}
	}
	/* we could avoid this check by setting the "expect response" bit
	 * for CMD0 above. Either do not look for response, or do not ask for it.
	 */
	else if (command->responseType)
	{
		if ((rc = NaSdioGatherResponse(interface, command->response, rspLen))
				!= NASTATUS_SUCCESS)
		{
			return rc;
		}
	}
#if	0
	/* FIM includes this in the buffer with the response */
	if (cmd == NA_SDIO_CMD24)	/* crc status follows R1 response */
	{
		if ((rc = NaSdioGetXmtCRCStatus(interface)) != NASTATUS_SUCCESS)
			return rc;
	}
#endif
	return NASTATUS_SUCCESS;
}

/* these are for display only */
char *buswidth_str[] =
{
	"ONE BIT",
	"INVALID",
	"FOUR BIT"
};

char *type_str[] =
{
	"NONE",
	"HC_MEM",
	"SC_MEM",
	"IO",
	"HC_COMBO",
	"SC_COMBO"
};

char *cardState_str[] =
{
	"idle",
	"ready",
	"ident",
	"stby",
	"tran",
	"data",
	"rcv",
	"prg",
	"dis",
	"res9",
	"res10",
	"res11",
	"res12",
	"res13",
	"res14",
	"iomode"
};

char *ioState_str[] =
{
	"DIS",
	"CMD",
	"TRN",
	"RFU"	/* should not see this */
};

/*
 * Returns a text block describing the status of the SDIO interfaces.
 * Needs several hundred bytes of buffer space to put it in.
 */

NaStatus NaSdioGetStatus(unsigned char *buffer, unsigned int bufsize)
{
	int ndx;
	naSdioCard_t *pQ;
	int len = 0;

	for (ndx = 0; ndx < NA_SDIO_MAX_INTERFACES; ndx++)
	{
		pQ = &sdioCardCtl[ndx];
		len += snprintf((char *) buffer + len, bufsize - len,
			"-------------------------------------\n\r");
		len += snprintf((char *) buffer + len, bufsize - len,
			"%d: %s. F8 %d HCS %d.\n\rIO %d (init %d) Rca %x OCR %x.\n\r"
			"Mem %d (init %d) Rca %x OCR %x.\n\r",
				ndx, type_str[pQ->type], pQ->F8, pQ->CCS,
				pQ->IOcapable ? 1 : 0, pQ->IOinited, pQ->sdioRca, pQ->sdioOCR,
				pQ->memoryPresent ? 1 : 0, pQ->memoryInited,
			   	pQ->sdRca, pQ->sdOCR);

		len += snprintf((char *) buffer + len, bufsize - len,
				"WV %x busWdth %s stat %x cardState %s (IO %s) numIO %d\n\r",
				pQ->workingVoltage,
			   	buswidth_str[pQ->busWidth],
			   	pQ->cardStatus, cardState_str[pQ->cardState],
			   	pQ->IOcapable ? ioState_str[pQ->ioState] : "n/a",
			   	pQ->numIOFuns);
		/* decoding the CID could be also */
		if (pQ->memoryPresent)
		{
			len += snprintf((char *) buffer + len, bufsize - len,
				"CSD: %02x %02x %02x %02x %02x %02x %02x %02x"
				" %02x %02x %02x %02x %02x %02x %02x %02x\n\r",
				pQ->CSD[0], pQ->CSD[1], pQ->CSD[2], pQ->CSD[3],
				pQ->CSD[4], pQ->CSD[5], pQ->CSD[6], pQ->CSD[7],
				pQ->CSD[8], pQ->CSD[9], pQ->CSD[10], pQ->CSD[11],
				pQ->CSD[12], pQ->CSD[13], pQ->CSD[14], pQ->CSD[15]);
		}
		len += snprintf((char *) buffer + len, bufsize - len,
				"rcv crc %s disabled\n\r", pQ->disableCrc ? "IS" : "is NOT");
		len += snprintf((char *) buffer + len, bufsize - len,
				"xmt bytes %d rcv bytes %d\n\r",
			   	naSdioBytesRcvd[ndx], naSdioBytesSent[ndx]);
	}
	return NASTATUS_SUCCESS;
}

/*
 * function number mask is 0x70000000
 * rwFlag is 0x8000000 (bit 27)
 * address field is 17 bits left shifted by 9
 */

NaStatus NaSdioCommand52(unsigned interface, unsigned int fNo,
			unsigned int rwFlag, unsigned int cisAddr, unsigned char *cisData)
{
	naSdioCard_t *sdioCard = &sdioCardCtl[interface];
	NaSdioCommand_t initCmd;
	NaStatus rc = NASTATUS_ERROR;

	if (sdioCard->IOcapable == 1)
	{
		/* send CMD52 to set bus width */

		rc = naSdioInitiateCommand(interface, &initCmd, NA_SDIO_CMD52,
					((fNo & SDIO_CMD52_FCN_MSK) << SDIO_CMD52_FCN_SHFT) |
					(rwFlag << SDIO_CMD52_RW_SHFT) |
					(cisAddr << SDIO_CMD52_ADDR_SHFT) | *cisData,
					NA_SDIO_R5);
		if (rc != NASTATUS_SUCCESS)
		{
#ifdef	SDIO_ERROR
			printf("CMD52 addr %x result rc 0x%08x\n\r",
							cisAddr, (unsigned int) rc);
#endif
		}
		else
		{
			if ((initCmd.response[0] & SDIO_HOST_CMD_MASK) == NA_SDIO_CMD52)
			{
				/* status bits in [3], data in [4] */
#ifdef	SDIO_VERBOSE
				unsigned char data = initCmd.response[4];
#endif
				unsigned char status = initCmd.response[3];
				sdioCard->ioState = (status >> 4) & 0x3;
#ifdef	SDIO_ERROR
				/* TODO name these error bits */
				if (status & 0xcb)
				{
					printf("response to CMD52 shows error %02x\n\r", status);
				}
#endif
#ifdef	SDIO_VERBOSE
				printf("CCCR addr %03x: %02x status %x\n\r",
							   cisAddr, data, status);
#endif
			}
		}
	}
	return rc;
}

/*ad*
 *
 * The application uses this function to turn on or off the CRC computation for
 * receive data in the FIM.
 *
 * @category BSP:Device_Drivers:SDIO
 * @since 7.3
 * @external
 *
 * @param interface	Index of the FIM in the naSdioTable
 * @param crcOnOff	non-zero to turn on, 0 to turn off.
 *
 * @return NASTATUS_SUCCESS	The function always succeeds.
 *
 *ad*/


NaStatus naSdioSetCrcSupport(unsigned interface, unsigned crcOnOff)
{
	naSdioCard_t *pQ = &sdioCardCtl[interface];
	pQ->disableCrc = crcOnOff;
	return NASTATUS_SUCCESS;
}

NaStatus naSdioIsrUninstall(unsigned interface, NaSdioIsrCallback_t callback,
			   	void *context)
{
	NA_UNUSED_PARAM(interface);
	NA_UNUSED_PARAM(callback);
	NA_UNUSED_PARAM(context);
	return NASTATUS_SUCCESS;
}

NaStatus naSdioIsrInstall(unsigned interface, NaSdioIsrCallback_t callback,
			   	void *context)
{
	NA_UNUSED_PARAM(interface);
	NA_UNUSED_PARAM(callback);
	NA_UNUSED_PARAM(context);
	return NASTATUS_SUCCESS;
}

NaStatus naSdioInterruptEnable(unsigned interface)
{
	NA_UNUSED_PARAM(interface);
	return NASTATUS_SUCCESS;
}

/* restarts the FIM for the given interface */

NaStatus naSdioInterfaceRestart(unsigned interface)
{
	NA_UNUSED_PARAM(interface);
	return NASTATUS_SUCCESS;
}

NaStatus naSdioInterfaceGetCount(unsigned *count)
{
	*count = naSdioTableSize;
	return NASTATUS_SUCCESS;
}

/*ad*
 *
 * The application uses this function to change the bus width used during
 * data transfers. The two choices are to use a one bit bus or a four bit
 * bus. The Driver will initialize the card in four bit mode.
 *
 * @category BSP:Device_Drivers:SDIO
 * @since 7.3
 * @external
 *
 * @param interface	Index of the FIM in the naSdioTable
 * @param width		User supplied enum value of bus width otion to configure.
 *
 * @return NASTATUS_SUCCESS	The bus width was changed successfully.
 * @return NASTATUS_ERROR	The change sequence failed, and the bus width is
 * not changed.
 *
 *ad*/

NaStatus naSdioInterfaceBusSetWidth(unsigned interface, NaSdioBusWidth_t width)
{
	if (naSdioSetBusWidth(interface, width) != NASTATUS_SUCCESS)
	{
		return NASTATUS_ERROR;
	}
	return NASTATUS_SUCCESS;
}

NaStatus naSdioSetDriverBusWidth(unsigned interface, unsigned width)
{
	NaStatus rc = NASTATUS_SUCCESS;
	naSdioCard_t *sdioCard = &sdioCardCtl[interface];

	if (width == 1)
	{
		sdioCard->busWidth = NA_SDIO_BUS_WIDTH1;
		*naSdioTable2[interface].clockDivisor =
		   	NA_SDIO_RECOMMENDED_INITIAL_CLOCK_DIVISOR;
	}
	else if (width == 4)
	{
		sdioCard->busWidth = NA_SDIO_BUS_WIDTH4;
		*naSdioTable2[interface].clockDivisor =
		   	NA_SDIO_RECOMMENDED_FULL_SPEED_CLOCK_DIVISOR;
	}
	else
		rc = NASTATUS_ERROR;
	return rc;
}

/*ad*
 *
 * The application uses this function to initialize the SD peripheral card.
 * If successful it returns the type of the card, the bus characteristics,
 * and its RCA values.
 * Refer to 4.2 of SD Spec or 3 of SDIO Spec.
 *
 * @category BSP:Device_Drivers:SDIO
 * @since 7.3
 * @external
 *
 * @param interface	Index of the FIM in the naSdioTable
 * @param type		User supplied pointer to location to return card type.
 * @param width		User supplied pointer to location to return bus width.
 * @param sdRca		User supplied pointer to location to return RCA of memory
 * component, if any.
 * @param sdioRca	User supplied pointer to location to return RCA of IO
 * component, if any.
 *
 * @return NASTATUS_SUCCESS	The card is initialized and the requested
 * parameters are returned.
 * @return NASTATUS_ERROR	Initialization failed, and the card is not usable.
 *
 *ad*/

NaStatus naSdioCardInitialize(unsigned interface, NaSdioCardType_t * type, NaSdioBusWidth_t * width, NaSdioRca_t * sdRca, NaSdioRca_t * sdioRca)
{
	naSdioCard_t *sdioCard = &sdioCardCtl[interface];
	NaSdioCommand_t initCmd;
	NaStatus rc = NASTATUS_SUCCESS;
	unsigned int wVoltage = 0;
#ifdef	TEST_EXERCISE
	unsigned int addr;
	int fun;
#endif
	unsigned int loopCtl;
	NaSdioRca_t rspRca = SDIO_CARD_TYPE_NONE;

    /* initialize the driver */
    if ((rc = naSdioInitialize(interface)) != NASTATUS_SUCCESS)
        return rc;

	printf("SDIO init interface %d\n\r", interface);

	if (interface >= naSdioTableSize)
	{
		rc = NASTATUS_SDIO_BAD_INTERFACE;
		goto exitInit;
	}
	if ((type == NULL) || (width == NULL) || (sdioRca == NULL))
	{
		rc = NASTATUS_SDIO_BAD_ARGUMENT;
		goto exitInit;
	}
	naSdioBytesRcvd[interface] = 0;
	naSdioBytesSent[interface] = 0;

	SdioInitializeXmtRing(interface);	/* mark all entries available */

	/* init capability variables */
	memset(sdioCard, 0, sizeof(naSdioCard_t));
	sdioCard->memoryPresent = 1;	/* must be memory unless IO only */
	sdioCard->busWidth = NA_SDIO_BUS_WIDTH1;
	sdioCard->workingVoltage = SDIO_OCR_NETOS_WVR;

	/* send CMD52 to reset IO portion, if there is one */
	/* write to function 0 register 6 a value of 8  */

	rc = naSdioInitiateCommand(interface, &initCmd, NA_SDIO_CMD52,
				   	0x80000000 | (6 << 9) | 8, NA_SDIO_R5);
	if (rc == NASTATUS_SUCCESS)
	{
		if ((initCmd.response[0] & SDIO_HOST_CMD_MASK) == NA_SDIO_CMD52)
		{
			/* status bits in [3], data in [4] */
			unsigned char status = initCmd.response[3];
#ifdef	SDIO_VERBOSE
			unsigned char data = initCmd.response[4];
			printf("CMD52 rsp status %x data %x\n\r", status, data);
#endif
			sdioCard->ioState = (status >> 4) & 0x3;
		}
	}

	/* send GO_IDLE CMD0 to reset the memory portion of the card.
	 * RCA 0, low speed, high current. Reset clock divisor and bus width */

	rc = naSdioInitiateCommand(interface, &initCmd, NA_SDIO_CMD0,
				   	0, (NaSdioResponse_t) 0);
	/*
	 * In case we are doing this from a previously active state,
	 * restore bus width to 1 and the clock divisor to low rate.
	 */
	sdioCard->busWidth = NA_SDIO_BUS_WIDTH1;

	*naSdioTable2[interface].clockDivisor =
	   	NA_SDIO_RECOMMENDED_INITIAL_CLOCK_DIVISOR;

	/* send SEND_IF_CMD CMD8 to check high capacity memory capability */
	/* check pattern 0xaa, 0x100 is voltage range 2.7 to 3.6 */

	rc = naSdioInitiateCommand(interface, &initCmd, NA_SDIO_CMD8, 0x1aa,
				   	NA_SDIO_R7);

	if (rc != NASTATUS_SUCCESS)
	{
		if (rc == NASTATUS_SDIO_TIMEOUT)
		{
			sdioCard->F8 = 0;	/* card is not 2.00 compliant? */
		}
		else
		{
			goto exitInit;
		}
	}
	else
	{
		/* if successful, initCmd.response is R7  */
		if ((initCmd.response[0] & SDIO_HOST_CMD_MASK) == NA_SDIO_CMD8)
		{
			if ((initCmd.response[4]) != 0xaa)	/* echo success */
			{
#ifdef	SDIO_ERROR
				printf("echo check failed\n\r");
#endif
				rc = NASTATUS_SDIO_ECHO_FAILURE;
				goto exitInit;
			}
			if ((initCmd.response[3] & 0xf) != 1) /* voltage OK */
			{
#ifdef	SDIO_ERROR
				printf("voltage check failed\n\r");
#endif
				rc = NASTATUS_SDIO_VOLTRNG_FAIL;
				goto exitInit;
			}
			if (((initCmd.response[4]) == 0xaa) &&
						   	((initCmd.response[3] & 0xf) == 1))
			{
				sdioCard->F8 = 1;	/* card is 2.00 compliant? */
			}
		}
	}

	/* THIS IS THE IO CARD INTERACTION LOOP */

	/* send CMD5 to get IO OCR, if any, to tell card we support version 2.00,
	 * and to set proper voltage. This is an SDIO only command which
	 * is required to activate the IO portion of the card. */

	loopCtl = 10;

	while (loopCtl--)
	{
		/* IO OCR: 24 bits of WV range */
		rc = naSdioInitiateCommand(interface, &initCmd, NA_SDIO_CMD5,
					   	wVoltage, NA_SDIO_R4);
		if (rc != NASTATUS_SUCCESS)
		{
			if (rc == NASTATUS_SDIO_TIMEOUT)
			{
				sdioCard->IOcapable = 0;	/* card is not IO capable */
				break;
			}
			else
			{
				goto exitInit;
			}
		}
		else
		{
			/* if successful, initCmd.response is R4  */
			if ((initCmd.response[0] & SDIO_HOST_CMD_MASK) ==
						   	SDIO_HOST_CMD_MASK)
			{
				unsigned int opCond = (initCmd.response[1] << 24) |
					   	(initCmd.response[2] << 16) |
						(initCmd.response[3] << 8) | (initCmd.response[4]);

				sdioCard->numIOFuns = (opCond & SDIO_IOR_NUMIO) >> 28;
				sdioCard->memoryPresent = (opCond & SDIO_IOR_MEMP) >> 27;

				/* if numIOFuns == 0 and memoryPresent == 1, 2.00 memory only.
		 		 * A 1.01 memory card will not respond to CMD5. */

#ifdef	SDIO_VERBOSE
				printf("R4 opCond: %02x %08x\n\r", initCmd.response[0], opCond);
#endif

				/* host version of working voltage is 24 bits */
				wVoltage = (opCond & SDIO_IOR_WVR);
#if	0
				if (sdioCard->workingVoltage != wVoltage)
				{
					sdioCard->workingVoltage &= wVoltage;
					wVoltage = sdioCard->workingVoltage;
					/* resend CMD5 with new WV if we should limit card range */
				}
				else
				{
#endif
					if ((opCond & SDIO_IOR_RDY) == SDIO_IOR_RDY)
					{
						/* card is ready. else we have to try again */
						sdioCard->IOcapable = 1;	/* card is IO capable */
						printf("Card is IO capable\n\r");
						break;
					}
#if	0
				}
#endif
			}
		}
		tx_thread_sleep(100);
	}

	/* now deal with the memory portion of the card */

	// if (sdioCard->memoryPresent != 0) DEBUG TEST
	{
		/* send SEND_OP ACMD41 (CMD55 followed by ACMD41) */
	 	/* Continue to issue ACMD41 specifying our working voltage range,
		 * until busy bit comes back 1. */

		loopCtl = 10;

		while (loopCtl--)
		{
			rc = naSdioInitiateCommand(interface, &initCmd, NA_SDIO_ACMD41,
					   	SDIO_OCR_CCS | SDIO_OCR_NETOS_WVR, NA_SDIO_R3);
			if (rc != NASTATUS_SUCCESS)
			{
#ifdef	SDIO_ERROR
				printf("result rc 0x%08x\n\r", (unsigned int) rc);
#endif
				if (rc == NASTATUS_SDIO_TIMEOUT)
				{
					sdioCard->memoryPresent = 0;
					goto getIdent;	/* no memory portion there -- IO only */
				}
				else
				{
					goto exitInit;
				}
			}
			/* if successful, initCmd.response is R3 with OCR voltage range  */
			if ((initCmd.response[0] & SDIO_HOST_CMD_MASK) ==
						   	SDIO_HOST_CMD_MASK)
			{
				sdioCard->memoryPresent = 1;

				/* bits 15-23 are the voltage ranges. We support 0xfc0000 --
				 * 3.0 to 3.6 volts */
				sdioCard->sdOCR = ((initCmd.response[1] << 24) |
						   	(initCmd.response[2] << 16) |
							(initCmd.response[3] << 8) |
				  		 	(initCmd.response[4]));
				/* busy is low while card still initializing. Keep sending
				 * ACMD41 until busy == 1 */
				if (sdioCard->sdOCR & SDIO_OCR_CBSY)
				{
					/* if card responded to CMD8 (F8=1) then CCS is valid
					 * in the R3 response to ACMD41 */
					if (sdioCard->F8 == 1)
					{
						sdioCard->CCS = (sdioCard->sdOCR & SDIO_OCR_CCS) >> 30;
					}
					sdioCard->workingVoltage = sdioCard->sdOCR & SDIO_OCR_WVR;
					printf("Card %d is memory capable\n\r", interface);

					break;	/* from "while" */
				}
				tx_thread_sleep(20);
			}
		}	/* end "while" */

		/* send ALL_SEND_CID CMD2 -> ident. Get R2 (CID -- 128 bits) back.
		 * The CID register does not exist on SDIO only card. */

		rc = naSdioInitiateCommand(interface, &initCmd, NA_SDIO_CMD2, 0,
					   	NA_SDIO_R2);

		if (rc != NASTATUS_SUCCESS)
		{
#ifdef	SDIO_ERROR
			printf("C%d CMD2 result 0x%08x\n\r", interface, (unsigned int) rc);
#endif
		}
		else
		{
			/* CMD2 response has all bits set in cmd index field */
			if ((initCmd.response[0] & SDIO_HOST_CMD_MASK) ==
						   	SDIO_HOST_CMD_MASK)
			{
				sdioCard->CID.manID = initCmd.response[1];
				sdioCard->CID.appID[0] = initCmd.response[2];
				sdioCard->CID.appID[1] = initCmd.response[3];
				strncpy((char *) sdioCard->CID.prodName,
							   	(char *) &initCmd.response[4], 5);
				sdioCard->CID.prodRev = initCmd.response[9];
				sdioCard->CID.serNo = ((initCmd.response[10] << 24) |
						(initCmd.response[11] << 16) |
						(initCmd.response[12] << 8) |
						(initCmd.response[13]));
				sdioCard->CID.manYear = ((initCmd.response[14] & 0xf) << 4) |
								(initCmd.response[14] & 0xf0) >> 4;
				sdioCard->CID.manMonth = initCmd.response[15] & 0xf;
			}
		}
	}
getIdent:
	/* send SEND_REL_ADDR CMD3 -> stdby. Get R6 (new RCA) back. */
	if (!sdioCard->IOcapable && !sdioCard->memoryPresent)
	{
		rc = NASTATUS_SDIO_CARD_NOT_CAPABLE;
		goto exitInit;
	}

	rc = naSdioInitiateCommand(interface, &initCmd, NA_SDIO_CMD3, 0,
				   	NA_SDIO_R6);

	if (rc != NASTATUS_SUCCESS)
	{
#ifdef	SDIO_ERROR
		printf("result rc 0x%08x\n\r", (unsigned int) rc);
#endif
		goto exitInit;
	}

	if ((initCmd.response[0] & SDIO_HOST_CMD_MASK) == NA_SDIO_CMD3)
	{
		rspRca = (initCmd.response[1] << 8) | (initCmd.response[2]);

		/* there are selected status bits in bytes 3 and 4 */

#ifdef	SDIO_VERBOSE
		printf("C%d sdRca is 0x%x, status %x %x\n\r",
			interface, rspRca, initCmd.response[3], initCmd.response[4]);
#endif
	}
	/*****************************************************************
	 * check IOcapable, memoryPresent, and CCS to determine
	 * standard/high memory/IO/combo 
	 *****************************************************************/
	naSdioSetCapabilities(sdioCard);

#ifdef	SDIO_VERBOSE
	printf("C%d type is %d\n\r", interface, sdioCard->type);
#endif

	*type = sdioCard->type;
	if (*type == SDIO_CARD_TYPE_IO)
		sdioCard->sdioRca = rspRca;
	else if ((*type == SDIO_CARD_TYPE_HC_MEM) ||
				   	(*type == SDIO_CARD_TYPE_SC_MEM))
		sdioCard->sdRca = rspRca;
	/* if combo type, we have more work TODO */
	else if ((*type == SDIO_CARD_TYPE_HC_COMBO) ||
				(*type == SDIO_CARD_TYPE_SC_COMBO))
	{
		sdioCard->sdRca = rspRca;
		sdioCard->sdioRca = rspRca;
	}

	*sdRca = sdioCard->sdRca;
	*sdioRca = sdioCard->sdioRca;

	/* If it is a memory card, send SEND_CSD CMD9 while still in stby state.
	 * Get R2 with CSD (16 bytes). Format of CSD depends on Spec Version,
	 * identified in first two bits. */
	if (sdioCard->memoryPresent != 0)
	{
		rc = naSdioInitiateCommand(interface, &initCmd, NA_SDIO_CMD9,
					   	rspRca << 16, NA_SDIO_R2);
		if (rc != NASTATUS_SUCCESS)
		{
#ifdef	SDIO_ERROR
			printf("C%d CMD9 result 0x%08x\n\r", interface, (unsigned int) rc);
#endif
			goto exitInit;
		}
		else
		{
			if ((initCmd.response[0] & SDIO_HOST_CMD_MASK) ==
						   	SDIO_HOST_CMD_MASK)
			{
				memcpy(sdioCard->CSD, &initCmd.response[1], SDIO_CSD_LEN);
			}
		}
	}

	/* send SELECT CMD7 -> tran. Get R1b back. */

	rc = naSdioInitiateCommand(interface, &initCmd, NA_SDIO_CMD7,
					   	rspRca << 16, NA_SDIO_R1);
	if (rc != NASTATUS_SUCCESS)
	{
#ifdef	SDIO_ERROR
		printf("C%d result 0x%08x\n\r", interface, (unsigned int) rc);
#endif
		goto exitInit;
	}

	if ((initCmd.response[0] & SDIO_HOST_CMD_MASK) == NA_SDIO_CMD7)
	{
		sdioCard->cardStatus = ((initCmd.response[1] << 24) |
					   	(initCmd.response[2] << 16) |
						(initCmd.response[3] << 8) |
					   	(initCmd.response[4]));

		/* take note of card state in bits 12:9 */
		sdioCard->cardState = (sdioCard->cardStatus & 0x1e00) >> 9;

#ifdef	SDIO_VERBOSE
		printf("C%d card state %d\n\r", interface, sdioCard->cardState);
#endif
	}
	if (sdioCard->memoryPresent)
		sdioCard->memoryInited = 1;
	if (sdioCard->IOcapable)
		sdioCard->IOinited = 1;

	/*
	 * time to set the clock divisor to use during operation with the card.
	 */
	*naSdioTable2[interface].clockDivisor =
		   	NA_SDIO_RECOMMENDED_FULL_SPEED_CLOCK_DIVISOR;

	/* send ACMD6 to get the SCR register to determine bus width support.
	 * ACMD6 is supported only if memoryPresent. SDIO uses CMD52 to write
	 * a 1 to the RES bit of CCCR.
	 * See 3.4.1 of SDIO spec, which says that for SDIO cards the CMD52
	 * must be issued before CMD0 */

	/* card must be unlocked to change bus bandwidth */

	if ((rc = naSdioSetBusWidth(interface, NA_SDIO_BUS_WIDTH4)) !=
							NASTATUS_SUCCESS)
	{
		goto exitInit;
	}

	*width = sdioCard->busWidth;
#ifdef	TEST_EXERCISE
	for (fun = 0; fun <= sdioCard->numIOFuns; fun++)
	{
		for (addr = 0; addr < 0x14; addr++)
		{
			unsigned char rData;

			if (NaSdioCommand52(interface, fun, 0, addr, &rData) ==
						   	NASTATUS_SUCCESS)
			{
				continue;
			}
		}
	}
#endif
exitInit:
	printf("C%d initialize %s (%x)\n\r", interface, 
					(rc == NASTATUS_SUCCESS) ? "SUCCESS" : "FAIL", (int) rc);
	return rc;
}

