/** 
 * \file sdcard.c
 * \brief sdcard source file
 * \details File contains functions definitions for physical layout of SD card communication
 *
 * \author Juraj Golej
 * \version 1.0
 */

#include "sdcard.h"

struct SDCARD_module SDCard;

void SDCARD_init(const uint8_t clkDiv)
{
	/* Enable peripheral clock and interrupt */
	PMC_peripheral_clock_enable(ID_HSMCI);
	NVIC_EnableIRQ(ID_HSMCI);

	/* Set the maximum number of Master Clock cycles that the HSMCI waits between two data block transfers. It equals (DTOCYC x Multiplier).  */
	HSMCI->HSMCI_DTOR = HSMCI_DTOR_DTOCYC(2) | HSMCI_DTOR_DTOMUL_1048576;
	HSMCI->HSMCI_DTOR = HSMCI_CSTOR_CSTOCYC(2) | HSMCI_CSTOR_CSTOMUL_1048576;

	/* Set clock divider, power save divider and stop clock when internal FIFO is full */
	HSMCI->HSMCI_MR = HSMCI_MR_CLKDIV(clkDiv) | HSMCI_MR_PWSDIV(7) | HSMCI_MR_RDPROOF_Msk;

	/* Enable HSMCI clock and power save mode */
	HSMCI->HSMCI_CR = HSMCI_CR_MCIEN_Msk | HSMCI_CR_PWSEN_Msk;
}

struct SDCARD_module* SDCARD_identify()
{
	/* Send command 0 (go to idle state) */
	SDCARD_sendCmd(SDCARD_CMD0, 0);
	
	/* Send command 8 (check v2 or other) */
	SDCARD_sendCmd(SDCARD_CMD8, 0x1AAul);

	/* No responding => SDv1 or MMC */
	if(HSMCI->HSMCI_SR & HSMCI_SR_RTOE_Msk) {
		do {
			/* Send command 55+41 (check v1) */
			SDCARD_sendCmd(SDCARD_CMD55, 0);
			SDCARD_sendCmd(SDCARD_ACMD41, 0x00000000);

			/* If still no responding, return */
			if(HSMCI->HSMCI_SR & HSMCI_SR_RTOE_Msk) {
				return NULL;
			}
		  /* Resend command, while is busy */
		} while(HSMCI->HSMCI_RSPR[0] & 0x80000000);							
 
		SDCard.type = SDCARD_TYPE_SDv1;									
	}
	else if(HSMCI->HSMCI_RSPR[0] != 0x1AAul) {
		/* Bad SDv2 or SDHCv2 memory card, return */
		return NULL;
	}
	else {
		/* Responding => SDv2 or SDHCv2 */
		do {
			/* Send command 55+41 (check v2) */
			SDCARD_sendCmd(SDCARD_CMD55, 0);
			SDCARD_sendCmd(SDCARD_ACMD41, 0x40000000);

			/* If no responding, return */
			if(HSMCI->HSMCI_SR & HSMCI_SR_RTOE_Msk) {
				return NULL;
			}
		  /* Resend command, while is busy */
		} while(!(HSMCI->HSMCI_RSPR[0] & 0x80000000));

		if(HSMCI->HSMCI_RSPR[0] & 0x40000000) {
			SDCard.type = SDCARD_TYPE_SDHCv2;
		}
		else {
			SDCard.type = SDCARD_TYPE_SDv2;
		}
	}

	/* Go to identification state */
	SDCARD_sendCmd(SDCARD_CMD2, 0);

	/* Go to stand-by state */
	Try(SDCARD_sendCmd(SDCARD_CMD3, 0), NULL);

	/* Save relative address */
	SDCard.adress = SDCARD_getResponse48() & 0xFFFF0000;

	/* Get CSD register */
	uint32_t CSD[4] = {0};
	SDCARD_sendCmd(SDCARD_CMD9, SDCard.adress);
	SDCARD_getResponse136(CSD);

	/* Save another data */
	if(CSD[3] & 0x00000C00) {
		SDCARD_sendCmd(SDCARD_CMD0, 0);
		return NULL;
	}

	/* Go to transfer state */
	Try(SDCARD_sendCmd(SDCARD_CMD7, SDCard.adress), NULL);
	while(!(HSMCI->HSMCI_SR & HSMCI_SR_NOTBUSY_Msk));

	/* Disable power save mode */
	HSMCI->HSMCI_CR = HSMCI_CR_PWSDIS_Msk;

	/* Set block length and block count */
	HSMCI->HSMCI_BLKR = HSMCI_BLKR_BLKLEN(512) | HSMCI_BLKR_BCNT(1);

	return &SDCard;
}

bool SDCARD_isReady()
{
	Try(SDCARD_sendCmd(SDCARD_CMD13, SDCard.adress), false);
	Try((SDCARD_getResponse48() & 0x000001E0) != 0x0000080, false);

	return true;
}

enum retCode SDCARD_read(uint32_t address, uint8_t* buffer)
{
	/* Check buffer NULL pointer*/
	Try(!buffer, ENullPtr);

	/* Address must be a multiple of 512 */
	Try(address % 512, EInvalidParameter);

	/* Start transfer (read) */
	SDCARD_sendCmd(SDCARD_CMD17, address);

	uint32_t* readData = (uint32_t*)buffer;
	while(!(HSMCI->HSMCI_SR & HSMCI_SR_XFRDONE_Msk)) {
		if(HSMCI->HSMCI_SR & HSMCI_SR_RXRDY_Msk) {
			*readData = HSMCI->HSMCI_RDR;
			readData++;
		}
	}

	return ENone;
}