/*
 * 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"

#define MMCTIMEOUT 20000
#define MMCRCA 0x55AA55AA

#define MMCNORESPONSE 0
#define MMCRESPONSE 1       // response 

#define MMCLONG 0
#define MMCSHORT 1          // sizes

#define MMCCRC 1
#define MMCNOCRC 0          // CRC?

#define MMCSTREAM 0
#define MMCSINGLEBLOCK 1
#define MMCMULTIBLOCK 2    // block or stream data TxRx

#define MMCNODATA 0         // data indicators
#define MMCDATAREAD 1       // data indicators
#define MMCDATAWRITE 2      // data indicators

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

void init_mmc(void);
INT sendcom_mmc(BYTE, ULONG, BYTE, BYTE, BYTE, BYTE, INT);
INT initbus_mmc(void);
INT waitready_mmc(void);
BYTE* getcsd_mmc(void);
BYTE* getcid_mmc(void);
BYTE* getocr_mmc(void);

//*********************************************************
//*               Code Implementation
//*********************************************************
volatile xdata BYTE buffer[512];
volatile xdata BYTE resp[17];
volatile xdata BYTE ocr[4];
volatile xdata BYTE cid[16];
volatile xdata BYTE csd[16];
volatile xdata BYTE istatus=0;            // internal status
// 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

INT waitready_mmc()
{
// block until card not busy...
// return 0 if ok
// return -1 if timeout...
ULONG t;

t=0;
while((t<MMCTIMEOUT)&&((MMSTA&MSK_CBUSY)!=0))t++;

if (t==MMCTIMEOUT)return -1; else return 0;
}

void init_mmc(void)
{
// initializes the MMC to a 10MHz clock speed
// and enables it and enables flow control

  MMCLK=0x80;       // set the divisor-1 with fundamental at 20MHz (slow at first <400kHz to begin with)...
  MMCON2=0x80;      // with flow control...
}

INT sendcom_mmc(BYTE com, ULONG n, BYTE response, BYTE size, BYTE crc, BYTE dta, INT number)
{
// send command index com with arguments a0,a1,a2,a3 and
INT u, count;
ULONG t;

if(DEBUG){
  writestring_ezlcd("COM"); disafree(com);
        }

//while((MMSTA& MSK_CBUSY)!=0);

count=0;
MMCON2&=0xFE;
MMCON0|=0xF0;
MMCON0&=0x0F;             // reset all FIFO pointers...
MMCON2|=(MSK_CCR | MSK_DCR);
MMCON2&=~(MSK_CCR | MSK_DCR);
        
if (size==MMCSHORT){ MMCON0|=MSK_RFMT; } else { MMCON0&=(~MSK_RFMT); }
if (crc==MMCNOCRC){ MMCON0|=MSK_CRCDIS; } else { MMCON0&=(~MSK_CRCDIS); }

if (dta!=MMCNODATA)
    {
    MMCON0|=MSK_DFMT;     // select block mode
    MMCON0&=(~MSK_MBLOCK); // single block mode
    MMCON1&=0x0F;
    MMCON1|=0x90;         // 512 bytes block length
    if (dta==MMCDATAREAD)MMCON1&=(~MSK_DATDIR); else MMCON1|=MSK_DATDIR; // read or write?
    MMCON2|=0x06;         // set data transmission delay to 9 MMC clocks to make sure to work with slower cards...
   } 

t=0;
while(((MMSTA & MSK_CFLCK)!=0)&&(t<MMCTIMEOUT))t++;

  if (t>=MMCTIMEOUT)
  {
  if(DEBUG)writestring_ezlcd("(-40)");
  return -40; // a timeout occured...
  }

MMCMD=com;
MMCMD=(BYTE)(n>>24);
MMCMD=(BYTE)(n>>16);
MMCMD=(BYTE)(n>>8);
MMCMD=(BYTE)n;

if (response==MMCRESPONSE)
  {
  MMCON1|=MSK_RESPEN;           // CMDEN bit=0
  MMCON1&=~MSK_RESPEN;           // CMDEN bit=0
}

MMCON1|=MSK_CMDEN;              // CMDEN bit=1
MMCON1&=(~MSK_CMDEN);           // CMDEN bit=0

t=0;
while (((MMINT & MSK_EOCI)==0)&&(t<MMCTIMEOUT))t++;
                        
  if (t>=MMCTIMEOUT)
  {
  if(DEBUG)writestring_ezlcd("(-50)");
  return -50; // a timeout occured...
  }

if(response==MMCNORESPONSE)
  {
  if(DEBUG)writestring_ezlcd("(NR)");
  return 0;
  }

t=0;
while (((MMINT & MSK_EORI)==0)&&(t<MMCTIMEOUT))t++;

  if (t>=MMCTIMEOUT)
  {
  if(DEBUG)writestring_ezlcd("(-60)");
  return -60; // a timeout occured...
  }

if ((MMSTA&MSK_RESPFS)==0)
  { 
  if(DEBUG)writestring_ezlcd("(-70)");
  return -70; 
  }

if ((crc!=MMCNOCRC)&&((MMSTA&MSK_CRC7S)==0))
  { 
  if(DEBUG)writestring_ezlcd("(-71)");
  return -71;
  }

t=0;
if(size==MMCSHORT){
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  } else {
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  resp[t++]=MMCMD;
  }
  
if(dta==MMCNODATA){
  if(DEBUG)writestring_ezlcd("(ND)");
  return 0;
  }

if (dta==MMCDATAREAD)
  {
  MMCON2|=0x01;
  MMCON1|=MSK_DATEN;
  MMCON1&=~MSK_DATEN;
  } else {
  MMCON2|=0x01;
  MMDAT=buffer[count++];
  MMDAT=buffer[count++];
  MMDAT=buffer[count++];
  MMDAT=buffer[count++];
  MMDAT=buffer[count++];
  MMDAT=buffer[count++];
  MMDAT=buffer[count++];
  MMDAT=buffer[count++];
  MMDAT=buffer[count++];
  MMDAT=buffer[count++];
  MMDAT=buffer[count++];
  MMDAT=buffer[count++];
  MMDAT=buffer[count++];
  MMDAT=buffer[count++];
  MMDAT=buffer[count++];
  MMDAT=buffer[count++];
  MMCON1|=MSK_DATEN;
  MMCON1&=~MSK_DATEN;
  }
 
t=0;
u=MMINT;
while(((u & MSK_EOFI)==0)&&(t<MMCTIMEOUT))
  {
  u=MMINT;
  if((dta==MMCDATAREAD)&&(count<number)&&((u&(MSK_F2FI|MSK_F1FI))!=0)){
      buffer[count++]=MMDAT;
      buffer[count++]=MMDAT;
      buffer[count++]=MMDAT;
      buffer[count++]=MMDAT;
      buffer[count++]=MMDAT;
      buffer[count++]=MMDAT;
      buffer[count++]=MMDAT;
      buffer[count++]=MMDAT;
    if(DEBUG)writestring_ezlcd("R8");
    t=0;
    }
    
  if((dta==MMCDATAWRITE)&&(count<number)&&((u&(MSK_F2EI|MSK_F1EI))!=0)){
        MMDAT=buffer[count++];
        MMDAT=buffer[count++];
        MMDAT=buffer[count++];
        MMDAT=buffer[count++];
        MMDAT=buffer[count++];
        MMDAT=buffer[count++];
        MMDAT=buffer[count++];
        MMDAT=buffer[count++];
    if(DEBUG)writestring_ezlcd("W8");
    t=0;
    }
  
  if (count>=number)MMCON2&=0xFE;       // disable flow control now...
  t++;
  }
  
if(t>=MMCTIMEOUT)
  {
  if(DEBUG)writestring_ezlcd("(-74)");
  return -74;
  }

if ((MMSTA&MSK_DATFS)==0)
  { 
  if(DEBUG)writestring_ezlcd("(-72)");
  return -72;
  }
  
if ((MMSTA&MSK_CRC16S)==0)
  { 
  if(DEBUG)writestring_ezlcd("(-73)");
  return -73;
  }
  
if(DEBUG)
  {
  writestring_ezlcd("(Ok=");
  disword(count); 
  writestring_ezlcd(")");
  }
  
return count;
}

INT initbus_mmc(void)
{
// returns 0  if all ok
// returns <0 if error occurred eg. no card present...
INT i,t;

init_mmc();

istatus=0;

i=sendcom_mmc(0,0,MMCNORESPONSE,MMCSHORT, MMCNOCRC, MMCNODATA, 0);
if (i<0)return -1;

resp[1]=0x00;
t=0;
while((t<MMCTIMEOUT)&&((resp[1]&0x80)==0x00)){
i=sendcom_mmc(0x01,0x00FF8000,MMCRESPONSE,MMCSHORT,MMCNOCRC,MMCNODATA, 0);
if (i<0)return -2;
t++;
}
if(t==MMCTIMEOUT)return -3;

for(i=0;i<4;i++)ocr[i]=resp[i+1];
istatus|=0x02;

i=sendcom_mmc(2,0,MMCRESPONSE, MMCLONG, MMCNOCRC, MMCNODATA, 0);
if(i<0)return -4;
for(i=0;i<16;i++)cid[i]=resp[i+1];
istatus|=0x04;

i=sendcom_mmc(3,MMCRCA,MMCRESPONSE,MMCSHORT,MMCCRC, MMCNODATA, 0);
if(i<0)return -5;
i=sendcom_mmc(9, MMCRCA, MMCRESPONSE, MMCLONG, MMCNOCRC, MMCNODATA, 0);
if (i<0)return -6;
for(i=0;i<16;i++)csd[i]=resp[i+1];
istatus|=0x09;

i=sendcom_mmc(7, 0, MMCNORESPONSE, MMCSHORT, MMCCRC, MMCNODATA,0);
if(i<0)return -7;           // deselect the card put in StandBy state...

i=sendcom_mmc(7,MMCRCA, MMCRESPONSE, MMCSHORT, MMCCRC, MMCNODATA,0);
if(i<0)return -8;

MMCLK=0x08;       // set the divisor with fundamental at 20MHz (fast after init)...
return 0;
}

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

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

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