/* ---------------------------------------------------------------------------
 * mmc_transfer.c                                       (c) 2008 Micro-key bv
 * ---------------------------------------------------------------------------
 * Micro-key bv
 * Industrieweg 28, 9804 TG Noordhorn
 * Postbus 92, 9800 AB Zuidhorn
 * The Netherlands
 * Tel: +31 594 503020
 * Fax: +31 594 505825
 * Email: support@microkey.nl  
 * Web: www.microkey.nl
 * ---------------------------------------------------------------------------
 * Description: 
 * ---------------------------------------------------------------------------
 * Version(s):  0.1, May 08, 2008, MMi
 *                   Creation.
 * ---------------------------------------------------------------------------
 */

/* ---------------------------------------------------------------------------
 * System include files
 * ---------------------------------------------------------------------------
 */
#include <string.h>

#include "LPC23xx.h"
#include "types.h"
/* ---------------------------------------------------------------------------
 * Application include files
 * ---------------------------------------------------------------------------
 */ 
#include "mmc_transfer.h"
#include "mmc.h"
#include "dio.h"
/* ---------------------------------------------------------------------------
 * Local constant and macro definitions
 * ---------------------------------------------------------------------------
 */
#define Card_Page_Size  512
/* ---------------------------------------------------------------------------
 * Global variable definitions
 * ---------------------------------------------------------------------------
 */
extern UINT32 Tnac;
extern UINT32 Twr;
extern DiskCtrlBlk_t MmcDskCtrlBlk;

UINT32 dlycnt;
/* ---------------------------------------------------------------------------
 * Local variable definitions
 * ---------------------------------------------------------------------------
 */
UINT8 ReadFromCard[512] __attribute__ ((section (".dmaram")));
UINT8 WriteToCard[512] __attribute__ ((section (".dmaram")));
/* ---------------------------------------------------------------------------
 * Local function definitions
 * ---------------------------------------------------------------------------
 */

MmcState_t CardRead (pUINT8 pData, UINT32 Address, UINT32 Length)
{
    UINT32 ArrayIndex = 0;
    UINT32 loopcnt;
    MmcState_t Status = MmcOk;
    
    for (loopcnt = 0; loopcnt < Card_Page_Size; loopcnt++)/* Prepare Buffer */
        { 
        ReadFromCard[loopcnt] = 0;      /* Set all Positions to Zero        */
        }
    
    /* Error or Wrong Argument Handle Block                                 */
    if (MmcPresent() == FALSE)          /* Check if Card is present         */
        {
        return (MmcNoPresent);          /* Return NoCardPresent State       */
        }
    if (checkLength(Length) == FALSE)   /* Check Read Length for validity   */
        {
        return (MmcMiscompare);         /* Return WrongLength State         */
        }
    
    Address = ((Address / Card_Page_Size) * Card_Page_Size);
                                        /* Calculate first Address of Page  */ 
    
    do                                  /* Loop to handle multible Reads    */
        {
        Status = MmcReadBlock((pUINT8)ReadFromCard, Address);
                                        /* Read 512 from Card               */
        memcpy (&(pData[ArrayIndex]), ReadFromCard, Card_Page_Size);
                                        /* Copy read Content to User Buffer */
        
        /* Recalculate Loop Values                                          */
        Length      -= Card_Page_Size;
        Address     += Card_Page_Size;
        ArrayIndex  += Card_Page_Size;
        } while (Length > 0);
    
    return (Status);
}


MmcState_t CardWrite (pUINT8 pData, UINT32 Address, UINT32 Length)
{
    UINT32 ArrayIndex = 0;
    UINT32 loopcnt;
    MmcState_t Status = MmcOk;

    for (loopcnt = 0; loopcnt < Card_Page_Size; loopcnt++)/* Prepare Buffer */
        { 
        WriteToCard[loopcnt] = 0;       /* Set all Positions to Zero        */
        }
    
    /* Error or Wrong Argument Handle Block                                 */
    if (MmcPresent() == FALSE)          /* Check if Card is Present         */
        {
        return (MmcNoPresent);          /* Return NoCardPresent State       */
        }
    if (MmcWriteProtect() == TRUE)      /* Check if Card is Writeprotected  */
        {
        return (MmcProtect);            /* Return WriteProtect State        */
        }
    if (checkLength(Length) == FALSE)   /* Check Read Length for validity   */
        {
        return (MmcMiscompare);         /* Return WrongLength State         */
        }
    if (Address < 0x200)                /* Defend Master Boot Sector        */
        {
        return (MmcCardError);          /* Return Error State               */
        }

    
    Address = ((Address / Card_Page_Size) * Card_Page_Size);
                                        /* Calculate first Address of Page  */ 
    
    do
        {
        memcpy (WriteToCard, &(pData[ArrayIndex]), Card_Page_Size);
                                        /* Copy 512Bytes in send-Buffer     */
        Status = MmcWriteBlock((pUINT8)WriteToCard, Address);
                                        /* Write 512 Bytes to Card          */
        
        /* Recalculate Loop Values                                          */
        Length      -= Card_Page_Size;
        Address     += Card_Page_Size;
        ArrayIndex  += Card_Page_Size;
        } while (Length > 0);
    
    return (Status);
}


inline BOOLEAN checkLength (UINT32 Length)
{
    if (Length == 0)
        {
        return (FALSE);
        }
    /* Check data length - Must be divisible of Card Block Size             */
    if ((Length % MmcDskCtrlBlk.BlockSize) != 0)
        {
        return (FALSE);
        }
    return (TRUE);
}


inline MmcState_t MmcReadBlock(pUINT8 pData, UINT32 Add)
{
    UINT32 Data;
    MmcState_t Status = MmcOk;
    
    PCONP |= (1 << 29);                 /* Turn on MCI Power                */
    MCI_DATA_TMR = Tnac;                /* Set Read Timeout                 */
    MCI_CLEAR = 0x000007FF;             /* Clear all Interrupt Flags        */
    
    /* General Purpose DMA (GPDMA) Control Settings for Reading             */
    GPDMA_CONFIG = 0x00;                /* Disable DMA                      */
    GPDMA_CH0_LLI = 0;                  /* No link List available           */
    GPDMA_CH0_CTRL = 0x00;              /* Reset DMA Control Block          */
    GPDMA_CH0_SRC = (UINT32)&MCI_FIFO;  /* Set MCI FIFO as Source Adress    */
    
    GPDMA_CH0_CTRL =  (Card_Page_Size & 0x0FFF) /* 512 Byte Transfer Size   */
                    | (0x04 << 12)      /* 32 Bit Source Burst Size         */
                    | (0x04 << 15)      /* 32 Bit Destination Burst Size    */
                    | (0x02 << 18)      /* 32 Bit Source width size         */
                    | (0x00 << 21)      /* 8 Bit Destination width size     */
                    | (1 << 27)         /* Destination index increment      */
                    | (1U << 31);       /* Terminal count interrupt         */

    MCI_DATA_LEN = MmcDskCtrlBlk.BlockSize; /* Set Transfer Blocksize       */
    Data = Add;                         /* Hand over Adress                 */
    Status = MmcSendCmd(CMD17, &Data);  /* Send Write Command to Card       */

    
    if (MmcOk != Status)                /* If Command Response is not OK:   */
        {
        return (Status);                /* return Status and exit Function  */
        }
     
    if (!(Data & READY_FOR_DATA) 
        ||((Data & CURRENT_STATE) != CARD_TRAN))           
        {                               /* Check Command Response           */
        MmcSendCmd(CMD12, NULL);
        return (MmcCardError);
        }
     
    GPDMA_CH0_DEST = (UINT32)pData;     /* Set DMA Destination Adress       */

    /* DMA Channel Configuration Block                                      */
    GPDMA_CH0_CFG = 0x00;               /* Reset DMA Channel Configuration  */
    GPDMA_CH0_CFG =   (0x10000)
                    | (0x04 << 1)       /* Source Device is Peripheral      */
                    | (0x00 << 6)       /* Destination Device is Memory     */
                    | (0x06 << 11);     /* Peripheral to Memory Mode        */

    GPDMA_INT_TCCLR = 0x01;             /* Clear all Terminal Interrupts    */
    GPDMA_CONFIG = 0x01;                /* enable GPDMA after setup Channel */
    while ((GPDMA_CONFIG & 0x01)!= 0x01); /* Poll Enable bit until set      */
    GPDMA_CH0_CFG |= (1 << 0);          /* enable DMA Channel               */
     
    MCI_DATA_CTRL =   (0x0001)          /* Data Transfer enable             */
                    | (0x01 << 1)       /* From Card to Controller          */
                    | (0x00 << 2)       /* Block Data Transfer              */
                    | (0x01 << 3)       /* DMA enabled                      */
                    | (0x09 << 4);      /* BlockSize 2^9 = 512 Byte         */
    for(dlycnt = 10; dlycnt > 0; dlycnt--);

    while (MCI_STATUS & (1 << 13));     /* Poll RxActive until read finish  */
    GPDMA_CH0_CFG &=~(1 << 0);          /* Disable DMA channel              */
    MCI_DATA_CTRL = 0;                  /* Reset Data Control Register      */
    for(dlycnt = 10; dlycnt > 0; dlycnt--);
     
    /* Interrupt Handling Block                                             */
    if (GPDMA_RAW_INT_ERR_STAT & (1 << 0))  /* Channel requests Interrupt    */
        {
        GPDMA_INT_ERR_CLR |= (1 << 0);  /* clear Flag, terminate transfer   */
        return (MmcDmaError);
        }

    /* Error Handling Block                                                 */
    if (MCI_STATUS & (1 << 3))          /* Timeout Error                    */
        {
        MCI_CLEAR = 1UL << 3;           /* Clear Timeout Bit                */
        return (MmcNoResponse);         /* Return with Error Message        */
        }
    if (MCI_STATUS & (1 << 9))          /* StartBit not detected in Wide    */
        {                               /* Bus Mode on every four Lines     */
        MCI_CLEAR = 1UL << 9;           /* Clear StartBitError bit          */
        return (MmcCardError);          /* Return with Error Message        */
        }
    if (MCI_STATUS & (1 << 1))          /* DATA CRC Error                   */
        {
        MCI_CLEAR = 1UL << 1;           /* Clear Data CRC Error Bit         */
        return (MmcCardError);          /* Return with Error Message        */
        }
    if (MCI_STATUS & (1 << 5))          /* Read FIFO Overrun Error          */
        {
        MCI_CLEAR = 1UL << 5;           /* Clean FIFO Overrun Error Bit     */
        return (MmcCardError);          /* Return with Error Message        */
        }

    MCI_CLEAR = (1UL << 8)              /* Clear DATA END and DATA BLOCK END*/                
                | (1UL << 10);

    return (Status);
}


inline MmcState_t MmcWriteBlock(const UINT8 * pData, UINT32 Add)
{
    UINT32 Data;


    MmcState_t Status = MmcOk;

    PCONP |= (1 << 29);                 /* Turn on MCI Power                */
    MCI_DATA_TMR = Twr;                 /* Set write Timeout                */
    MCI_CLEAR = 0x000007FF;             /* Clear all related flags          */
    
    /* General Purpose DMA (GPDMA) Control Settings for Writing             */
    GPDMA_CONFIG = 0x00;                /* Disable DMA                      */
    GPDMA_CH0_LLI = 0;                  /* No link List                     */
    GPDMA_CH0_CTRL = 0x00;              /* Reset DMA Control Block          */    
    GPDMA_CH0_DEST = (UINT32)&MCI_FIFO; /* Set MCI FIFO as Destination      */
    
    GPDMA_CH0_CTRL =  (Card_Page_Size & 0x0FFF) /* 512 Byte Transfer Size   */
                    | (0x04 << 12)      /* 32 Bit Source Burst Size         */
                    | (0x04 << 15)      /* 32 Bit Destination Burst Size    */
                    | (0x00 << 18)      /* 8 bit Source width Size          */
                    | (0x02 << 21)      /* 32 Bit Destination width Size    */
                    | (1 << 26)         /* Source index increment           */
                    | (1U << 31);       /* Terminal count interrupt enable  */


    MCI_DATA_LEN = MmcDskCtrlBlk.BlockSize; /* Set Transfer Blocksize       */
    Data = Add;                         /* Hand over Adress                 */
    Status = MmcSendCmd(CMD24, &Data); /* Send Write Command                */
 
    if (MmcOk != Status)                /* If Command Response is NOT OK    */
        {
        return (Status);                /* Return with Error Message        */
        }
        
    if (!(Data & READY_FOR_DATA) 
     || ((Data & CURRENT_STATE) != CARD_TRAN))
        {                               /* Check Command Response           */
        MmcSendCmd(CMD12, NULL);
        return (MmcCardError);
        }

    GPDMA_CH0_SRC = (UINT32)pData;      /* Set DMA Source address           */
    
    /* DMA Channel Configuration Block                                      */
    GPDMA_CH0_CFG = 0x00;               /* Reset DMA Channel Configuration  */
    GPDMA_CH0_CFG =  (0x10000)   
                   | (0x00 << 1)        /* Source Device is Memory          */
                   | (0x04 << 6)        /* Destination is Peripheral        */
                   | (0x05 << 11);      /* Memory to Peripheral Mode        */

    GPDMA_INT_TCCLR = 0x01;             /* Clear all Terminal Interrupts    */
    GPDMA_CONFIG = 0x01;                /* Enable GPDMA after Setup Channel */
    while ((GPDMA_CONFIG & 0x01)!= 0x01); /* Poll Enable Bit                */
    GPDMA_CH0_CFG |= (1 << 0);          /* enable DMA Channel               */
             
    MCI_DATA_CTRL =   (0x0001)          /* Data Transfer enable             */
                    | (0x00 << 1)       /* From Controller to Card          */
                    | (0x00 << 2)       /* Block Data Transfer              */
                    | (0x01 << 3)       /* DMA enabled                      */
                    | (0x09 << 4);      /* BlockSize 2^9 = 512 Byte         */
                        
    for(dlycnt = 10; dlycnt > 0; dlycnt--);

    while (MCI_STATUS & (1 << 12));     /* Poll TxActive until write finish */

    GPDMA_CH0_CFG &=~(1 << 0);          /* Disable DMA Channel              */

    /* Interrupt Handle Block                                               */
    if (GPDMA_RAW_INT_ERR_STAT & (1 << 0)) /* Channel requests Interrupt    */
        {
        GPDMA_INT_ERR_CLR |= (1 << 0);  /* clear Flag, terminate transfer   */
        return (MmcDmaError);
        }
    
    MCI_DATA_CTRL = 0;                  /* Reset DATA Control Register      */
    for(dlycnt = 10; dlycnt > 0; dlycnt--);
    
    /* Error Handling Block                                                 */
    if (MCI_STATUS & (1 << 3))          /* Timeout Error                    */
        {
        MCI_CLEAR = 1UL << 3;           /* Clear Timeout Bit                */
        return (MmcNoResponse);         /* Return with Error Message        */
        }
    if (MCI_STATUS & (1 << 9))          /* StartBit not detected in Wide    */
        {                               /* Bus Mode on every four Lines     */
        MCI_CLEAR = 1UL << 9;           /* Clear StartBitError bit          */
        return (MmcCardError);          /* Return with Error Message        */
        }
    if (MCI_STATUS & (1 << 1))          /* DATA CRC Error                   */
        {
        MCI_CLEAR = 1UL << 1;           /* Clear Data CRC Error Bit         */
        return (MmcCardError);          /* Return with Error Message        */
        }
    if (MCI_STATUS & (1 << 4))          /* Write FIFO Underrun Error        */
        {
        MCI_CLEAR = 1UL << 4;           /* Clean FIFO Underrun Error Bit    */
        return (MmcCardError);          /* Return with Error Message        */
        }

        MCI_CLEAR = (1UL << 8)          /* Clear DATA END and DATA BLOCK END*/
                  | (1UL << 10);

        return (Status);
}

