/*
 * mmc.c
 * by Mauro Grassi August 2007
 * Driver for MMC card interface
 *
 * Interface for 89C51SND1C chip MMC card interface...
 *    
*/

//*********************************************************
//*                Header Files
//*********************************************************

#include "at89c51snd1c.h"
#include "integer.h"
#include "mmc.h"

//*********************************************************
//*               Function Prototypes
//*********************************************************

void init_mmc(void);
INT initbus_mmc(void);
BYTE* getcsd_mmc(void);
BYTE* getcid_mmc(void);
BYTE* getocr_mmc(void);

//*********************************************************
//*               Code Implementation
//*********************************************************
volatile xdata BYTE resp[17];
volatile xdata BYTE ocr[4];
volatile xdata BYTE cid[16];
volatile xdata BYTE csd[16];
volatile xdata BYTE istatus;        // internal status
volatile xdata BYTE buffer[1024];
// istatus bit 0=1 means that initbus_mmc succeeded;
// istatus bit 1=1 means ocr present
// istatus bit 2=1 means cid present
// istatus bit 3=1 means csd present

void init_mmc(void)
{
// initializes the MMC to a 10MHz clock speed
// and enables it and enables flow control
  
  Mmc_set_clock(MMC_CLOCK_SLOW);  // set the (divisor-1) with fundamental at 20MHz (slow at first <400kHz to begin with)...
  Mmc_enable();                   // enable the MMC controller module... (Master Enable)...
  mmc_delay();
  // now ready...
  mmc_send_scmd(MMC_GO_IDLE_STATE, MMC_NO_RESP);      // CMD 0 put all cards in IDLE state to begin with...
  istatus=0;                      // in idle state...
}

/****************************************************************************
* NAME: init_mmc_csd
*  returns 0 if success or -1 if error
*****************************************************************************/
BYTE init_mmc_csd(void)
{
BYTE  tmp;
ULONG mmc_mem_size;

  ACC = Mmc_rd_cmd();                       /* dummy read of header */
  ACC = Mmc_rd_cmd();                       /* dummy read of CSD[0] */
  ACC = Mmc_rd_cmd();                       /* dummy read */
  ACC = Mmc_rd_cmd();                       /* dummy read */
  ACC = Mmc_rd_cmd();                       /* dummy read */
  ACC = Mmc_rd_cmd();                       /* dummy read */

  if ((Mmc_rd_cmd() & CSD_MSK_RBL) != CSD_BLEN_512) /* check block length */
  { /* block size != 512 bytes not supported by firmware */
    Mmc_reset_cmd_fifos();                  /* reset pointers */
    return -1;
  }
  else
  {
    Mmc_set_block_size(CSD_BLEN_512);       /* init controller */
  }
  /* compute card size in number of block (block size = 512) */
  /* read C_SIZE */
  ((BYTE*)&mmc_mem_size)[0] = 0;
  ((BYTE*)&mmc_mem_size)[1] = 0;
  ((BYTE*)&mmc_mem_size)[2] = Mmc_rd_cmd() & CSD_MSK_CSH;
  ((BYTE*)&mmc_mem_size)[3] = Mmc_rd_cmd();
  mmc_mem_size = mmc_mem_size << 2;
  ((BYTE*)&mmc_mem_size)[3] |= ((Mmc_rd_cmd() & CSD_MSK_CSL) >> 6);
  /* read C_SIZE_MULT */
  tmp = Mmc_rd_cmd() << 1;
  tmp = (tmp | (Mmc_rd_cmd() >> 7)) & CSD_MSK_CSM;
  mmc_mem_size = ((mmc_mem_size + 1) << (tmp + 2));
  mmc_mem_size--;
  Mmc_set_single_block();                   /* single block mode transfer */
  Mmc_enable_flow_ctrl();                   /* start flow control */
  Mmc_reset_cmd_fifos();                    /* reset pointers */
  return 0;
}

//******************************************************************************
//*   Send a command on the bus
//*****************************************************************************/
void mmc_send_cmd (BYTE index, ULONG argument, BYTE response)
{
  
  while((MMSTA & MSK_CFLCK)==MSK_CFLCK);        // wait for lock...
    
  MMCON0|=0xF0;
  MMCON0&=0x0F;             // reset all FIFO pointers...
  
  MMCMD = index;
  MMCMD = ((BYTE*)&argument)[0];
  MMCMD = ((BYTE*)&argument)[1];
  MMCMD = ((BYTE*)&argument)[2];
  MMCMD = ((BYTE*)&argument)[3];

  switch (response)
  {
    case MMC_RESP_R1:
    case MMC_RESP_R4:
    case MMC_RESP_R5:
    {
      MMCON0 |= MSK_RFMT;                   /* set 48 bits response */
      MMCON0 &= ~MSK_CRCDIS;                /* set response with CRC7 */
      MMCON1 |= (MSK_CMDEN|MSK_RESPEN);
      MMCON1 &= ~(MSK_CMDEN|MSK_RESPEN);    /* send command with response */
      break;
    }
    case MMC_RESP_R2:
    {
      MMCON0 &= ~(MSK_RFMT | MSK_CRCDIS);   /* set 136 bits response with CRC7 */
      MMCON1 |= (MSK_CMDEN|MSK_RESPEN);
      MMCON1 &= ~(MSK_CMDEN|MSK_RESPEN);    /* send command with response */
      break;
    }
    case MMC_RESP_R3:
    {
      MMCON0 |= (MSK_RFMT | MSK_CRCDIS);    /* set 48 bits response without CRC7 */
      MMCON1 |= (MSK_CMDEN|MSK_RESPEN);
      MMCON1 &= ~(MSK_CMDEN|MSK_RESPEN);    /* send command with response */
      break;
    }
    case MMC_NO_RESP:
    default:
    {
      MMCON1 |= MSK_CMDEN;
      MMCON1 &= ~MSK_CMDEN;                 /* send command without response */
      break;
    }
  }
  
  while((MMINT&MSK_EOCI)==0);
  
}

/****************************************************************************
* NAME: mmc_send_scmd
*****************************************************************************/
void mmc_send_scmd (BYTE index, BYTE response)
{
  
  while((MMSTA & MSK_CFLCK)==MSK_CFLCK);        // wait for lock...
  MMCON0|=0xF0;
  MMCON0&=0x0F;             // reset all FIFO pointers...
  
  
  MMCMD = index;
  MMCMD = (BYTE)MMC_NO_ARG;
  MMCMD = (BYTE)MMC_NO_ARG;
  MMCMD = (BYTE)MMC_NO_ARG;
  MMCMD = (BYTE)MMC_NO_ARG;

  switch (response)
  {
    case MMC_RESP_R1:
    case MMC_RESP_R4:
    case MMC_RESP_R5:
    {
      MMCON0 |= MSK_RFMT;                   /* set 48 bits response */
      MMCON0 &= ~MSK_CRCDIS;                /* set response with CRC7 */
      MMCON1 |= (MSK_CMDEN|MSK_RESPEN);
      MMCON1 &= ~(MSK_CMDEN|MSK_RESPEN);    /* send command with response */
      break;
    }
    case MMC_RESP_R2:
    {
      MMCON0 &= ~(MSK_RFMT | MSK_CRCDIS);   /* set 136 bits response with CRC7 */
      MMCON1 |= (MSK_CMDEN|MSK_RESPEN);
      MMCON1 &= ~(MSK_CMDEN|MSK_RESPEN);    /* send command with response */
      break;
    }
    case MMC_RESP_R3:
    {
      MMCON0 |= (MSK_RFMT | MSK_CRCDIS);    /* set 48 bits response without CRC7 */
      MMCON1 |= (MSK_CMDEN|MSK_RESPEN);
      MMCON1 &= ~(MSK_CMDEN|MSK_RESPEN);    /* send command with response */
      break;
    }
    case MMC_NO_RESP:
    default:
    {
      MMCON1 |= MSK_CMDEN;
      MMCON1 &= ~MSK_CMDEN;                 /* send command without response */
      break;
    }
  }
  while((MMINT&MSK_EOCI)==0);
  
}

/****************************************************************************
* NAME: mmc_check_response
*****************************************************************************/
BYTE mmc_check_response(void)
{
ULONG t;

    t=0;
    while (t<MMC_TIMEOUT)
    {
    if(Mmc_response_received())break;    
    t++;
    }
    
    if(t>=MMC_TIMEOUT)
    {
    writestring_ezlcd("(CrT)");
    return (MMC_ERR_RESP);      // timeout occurred
    }
    
    if ((MMCON0 & MSK_CRCDIS) != 0)
    {                                         /* CRC7 not computed */
      if ((MMSTA & MSK_RESPFS) != 0)
      {
        return (MMC_RESP_OK);
      }
      else
      {
        writestring_ezlcd("(Cr0)");
        return (MMC_ERR_RESP);                /* format error */
      }
    }
    else
    {                                         /* CRC7 computed */
      if ((MMSTA & (MSK_RESPFS | MSK_CRC7S)) == (MSK_RESPFS | MSK_CRC7S))
      {
        return (MMC_RESP_OK);
      }
      else
      {
        writestring_ezlcd("(Cr1)");
        return (MMC_ERR_RESP);                /* format or CRC7 error */
      }
    }
}

/****************************************************************************
* mmc_read_response
* return:
*   4-byte argument of the response
*****************************************************************************/
ULONG mmc_read_response (void)
{
static ULONG arg;
  ((BYTE*)&arg)[0] = MMCMD;    /* dummy index read */
  ((BYTE*)&arg)[0] = MMCMD;
  ((BYTE*)&arg)[1] = MMCMD;
  ((BYTE*)&arg)[2] = MMCMD;
  ((BYTE*)&arg)[3] = MMCMD;
  return arg;
}

void mmc_delay(void)
{
ULONG x;
for (x=0; x< MMC_DELAY; x++);
}

/****************************************************************************
*  initbus_mmc()
*   MMC installation routine
*****************************************************************************/
INT initbus_mmc(void)
{
BYTE func_state;
ULONG response;

    writestring_ezlcd("Initializing...");
    
    init_mmc();
    
    mmc_delay();

    writestring_ezlcd("(1)");
    
    mmc_send_cmd(MMC_SEND_OP_COND, MMC_OCR_INIT, MMC_RESP_R3);
    
    if (mmc_check_response() == MMC_ERR_RESP)return (MMC_ERROR); 

    writestring_ezlcd("(2)");
    
    response=mmc_read_response();
    
    if ((((BYTE*)&response)[0]&MMC_BUSY_OCR)!=MMC_BUSY_OCR)
    {    
    
    do
    {    
    mmc_send_cmd(MMC_SEND_OP_COND, MMC_OCR_INIT, MMC_RESP_R3);
    response = mmc_read_response();
    disdword(response);
    mmc_delay();
    } while ((((BYTE*)&response)[0] & MMC_BUSY_OCR) != MMC_BUSY_OCR);
    }
    
    mmc_send_scmd(MMC_ALL_SEND_CID, MMC_RESP_R2);
    if (mmc_check_response() == MMC_ERR_RESP)return (MMC_ERROR);
    
    writestring_ezlcd("(3)");
    
    mmc_send_cmd(MMC_SET_RELATIVE_ADDRESS, MMC_RCA, MMC_RESP_R1);       // set the relative address now...
      
    if (mmc_check_response() == MMC_ERR_RESP)return (MMC_ERROR);
  
    writestring_ezlcd("(4)");
    
    mmc_send_cmd(MMC_SEND_CSD, MMC_RCA, MMC_RESP_R2);
    if (mmc_check_response() == MMC_ERR_RESP)return (MMC_ERROR);
    
    writestring_ezlcd("(5)");
    
    Mmc_set_clock(MMC_CLOCK_FAST);      /* set MMC clock to transfer */
    Mmc_set_single_block();             /* single block transfer */
    
    
    if (init_mmc_csd()!=0)return MMC_ERROR;
    
    writestring_ezlcd("(6)");
    
    Mmc_reset_data();             
    istatus|=MSK_SETUP;
    return MMC_BUSY;
}

BYTE* getocr_mmc()
{
if ((istatus&MSK_OCR_VALID)!=0)return &ocr[0]; else return 0;
}

BYTE* getcid_mmc()
{
if ((istatus&MSK_CID_VALID)!=0)return &cid[0]; else return 0;
}

BYTE* getcsd_mmc()
{
if ((istatus&MSK_CSD_VALID)!=0)return &csd[0]; else return 0;
}

