/* 
 * 2002 (C) Copyrigt SAMSUMNG ELECTRONICS 
 *           SW.LEE <hitchcar@sec.samsung.com>
 * 
 * 	    Low-level MMC functions for the S3CXX
 *
 * Copyright 2002 Hewlett-Packard Company
 *
 * Use consistent with the GNU GPL is permitted,
 * provided that this copyright notice is
 * preserved in its entirety in all copies and derived works.
 *
 * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
 * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
 * FITNESS FOR ANY PARTICULAR PURPOSE.
 *
 * Many thanks to Alessandro Rubini and Jonathan Corbet!
 *
 * Author:  Andrew Christian
 *          6 May 2002
 * SW.LEE <hitchcar@sec.samsung.com>
 *    Wrote MMC driver for S3C2410
 * Jamey Hicks <jamey.hicks@hp.com>  July 7, 2003
 *    Generalized the S3C2410 MMC driver for other instantiations of this samsung asic core. 
 */


#include <linux/module.h>
#include <linux/version.h>

#include <linux/init.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>

#include <asm/irq.h>       
#include <asm/unaligned.h>

#include <asm/arch/hardware.h>
#include <asm/io.h> /* ioremap */
#include <linux/mmc/mmc_ll.h>
#include "mmc_samsung.h"
#include "sdi_reg_s3c2410.h"

#undef ASSERT
#define  ASSERT(expr) \
if(!(expr)) { \
            printk(" Assertion failed ! %s, %s,%s,line=%d\n",\
                       #expr,__FILE__,__FUNCTION__,__LINE__);\
}

struct s3c_mmc_lowlevel *lowlevel;

struct response_info {
        int length;
        u16 flags;
};

static struct response_info rinfo[] = {
	{ 0,  MMC_CMD_DATA_CONT_FORMAT_NO_RESPONSE                     }, /* R0 */
	{ 6,  MMC_CMD_DATA_CONT_FORMAT_R1                              }, /* R1  */
	{ 6,  MMC_CMD_DATA_CONT_FORMAT_R1 | MMC_CMD_DATA_CONT_BUSY_BIT }, /* R1b */
	{ 17, MMC_CMD_DATA_CONT_FORMAT_R2                              }, /* R2 CID */
	{ 17, MMC_CMD_DATA_CONT_FORMAT_R2                              }, /* R2 CSD */
	{ 6,  MMC_CMD_DATA_CONT_FORMAT_R3                              }, /* R3  */
	{ 6,  MMC_CMD_DATA_CONT_FORMAT_R4                              }, /* R4  */
	{ 6,  MMC_CMD_DATA_CONT_FORMAT_R5                              }, /* R5  */
	{ 6,  MMC_CMD_DATA_CONT_FORMAT_R6                              }, /* R6  */
};

enum s3c_request_type {
	RT_NO_RESPONSE,
	RT_RESPONSE_ONLY,
	RT_READ,
	RT_WRITE
};

struct s3c_mmc_data {
	struct timer_list        sd_detect_timer;
	struct timer_list        reset_timer;
	struct timer_list        irq_timer;    
                                      /* Panic timer - make sure we get a response */
	u32                      clock;        /* Current clock frequency */
	struct mmc_request      *request;
        enum s3c_request_type  type; 
                       /* used in s3c_set_transfer() */
         u32    size2send;	/* MAX(datasize)= request->nob * request->block_len  */
         u32    size2read;      /* read byts from SD Card */
         u32    insert ;
};
static struct s3c_mmc_data g_s3c_mmc_data;

#define MMC_IRQ_TIMEOUT (3*HZ)

static __inline__ void mmc_delay(void) {  mdelay(100); }


/**************************************************************************
 * Utility routines for debuging
 **************************************************************************/

struct cmd_to_name {
	int   id;
	char *name;
};

static struct cmd_to_name cmd_names[] = {
	{ MMC_CIM_RESET, "CIM_RESET" },
	{ MMC_GO_IDLE_STATE, "GO_IDLE_STATE" },
	{ MMC_SEND_OP_COND, "SEND_OP_COND" },
	{ MMC_ALL_SEND_CID, "ALL_SEND_CID" },
	{ MMC_SET_RELATIVE_ADDR, "SET_RELATIVE_ADDR" },
	{ MMC_SET_DSR, "SET_DSR" },
	{ MMC_SELECT_CARD, "SELECT_CARD" },
	{ MMC_SEND_CSD, "SEND_CSD" },
	{ MMC_SEND_CID, "SEND_CID" },
	{ MMC_READ_DAT_UNTIL_STOP, "READ_DAT_UNTIL_STOP" },
	{ MMC_STOP_TRANSMISSION, "STOP_TRANSMISSION" },
	{ MMC_SEND_STATUS	, "SEND_STATUS	" },
	{ MMC_GO_INACTIVE_STATE, "GO_INACTIVE_STATE" },
	{ MMC_SET_BLOCKLEN, "SET_BLOCKLEN" },
	{ MMC_READ_SINGLE_BLOCK, "READ_SINGLE_BLOCK" },
	{ MMC_READ_MULTIPLE_BLOCK, "READ_MULTIPLE_BLOCK" },
	{ MMC_WRITE_DAT_UNTIL_STOP, "WRITE_DAT_UNTIL_STOP" },
	{ MMC_SET_BLOCK_COUNT, "SET_BLOCK_COUNT" },
	{ MMC_WRITE_BLOCK, "WRITE_BLOCK" },
	{ MMC_WRITE_MULTIPLE_BLOCK, "WRITE_MULTIPLE_BLOCK" },
	{ MMC_PROGRAM_CID, "PROGRAM_CID" },
	{ MMC_PROGRAM_CSD, "PROGRAM_CSD" },
	{ MMC_SET_WRITE_PROT, "SET_WRITE_PROT" },
	{ MMC_CLR_WRITE_PROT, "CLR_WRITE_PROT" },
	{ MMC_SEND_WRITE_PROT, "SEND_WRITE_PROT" },
	{ MMC_ERASE_GROUP_START, "ERASE_GROUP_START" },
	{ MMC_ERASE_GROUP_END, "ERASE_GROUP_END" },
	{ MMC_ERASE, "ERASE" },
	{ MMC_FAST_IO, "FAST_IO" },
	{ MMC_GO_IRQ_STATE, "GO_IRQ_STATE" },
	{ MMC_LOCK_UNLOCK, "LOCK_UNLOCK" },
	{ MMC_APP_CMD, "APP_CMD" },
	{ MMC_GEN_CMD, "GEN_CMD" },
};

static char * get_cmd_name( int cmd )
{
	int i;
	int len = sizeof(cmd_names) / sizeof(struct cmd_to_name);
	for ( i = 0 ; i < len ; i++ )
		if ( cmd == cmd_names[i].id )
			return cmd_names[i].name;

	return "UNKNOWN";
}


struct status_bit_to_name {
	u16   mask;
	int   shift;
	char *name;
};


struct status_bit_to_name cmd_status_bit_names[] = {
  {MMC_STATUS_CRC_RESPONSE_ERROR ,-1," MMC_STATUS_CRC_RESPONSE_ERROR"},
  {MMC_STATUS_CMD_SENT_DONE      ,-1," MMC_STATUS_CMD_SENT_DONE"},
  {MMC_STATUS_RESPONSE_TIMEOUT   ,-1,"MMC_STATUS_RESPONSE_TIMEOUT"},
  {MMC_STATUS_RESPONSE_RECV_DONE ,-1,"MMC_STATUS_RESPONSE_RECV_DONE"},
  {MMC_STATUS_TRANS_ON           ,-1,"MMC_STATUS_TRANS_ON"},
  {MMC_STATUS_RESPONSE_INDEX     ,-1,"MMC_STATUS_RESPONSE_INDEX"}
};


struct status_bit_to_name data_status_bit_names[] = {
  {  MMC_STATUS_RD_WAIT           ,-1, "MMC_STATUS_RD_WAIT"},
  {  MMC_STATUS_IO_INT_DETECT     ,-1, "MMC_STATUS_RD_WAIT"},
  {  MMC_STATUS_IO_INT_DETECT     ,-1, "MMC_STATUS_IO_INT_DETECT"},
  {  MMC_STATUS_FIFO_ERROR        ,-1, "MMC_STATUS_IO_INT_DETECT"},
  {  MMC_STATUS_CRC_WRITE_ERROR   ,-1, "MMC_STATUS_CRC_WRITE_ERROR"},
  {  MMC_STATUS_CRC_READ_ERROR    ,-1, "MMC_STATUS_CRC_READ_ERROR"},
  {  MMC_STATUS_DATA_BUSY_TIMEOUT ,-1, "MMC_STATUS_DATA_BUSY_TIMEOUT"},
  {  MMC_STATUS_DATA_TRANSFER_DONE,-1, "MMC_STATUS_DATA_TRANSFER_DONE"},
  {  MMC_STATUS_BUSY_FINISH       ,-1, "MMC_STATUS_BUSY_FINISH"},
  {  MMC_STATUS_STARTBIT_ERROR    ,-1, "MMC_STATUS_BUSY_FINISH"},
  {  MMC_STATUS_TX_DATA_ON_PROGRESS,-1,"MMC_STATUS_BUSY_FINISH"},
  {  MMC_STATUS_RX_DATA_ON_PROGRESS,-1,"MMC_STATUS_BUSY_FINISH"}
};


struct status_bit_to_name fifo_status_bit_names[] = {
  {  MMC_STATUS_FIFO_READY_FOR_TX   ,-1,"MMC_STATUS_FIFO_READY_FOR_TX" },
  {  MMC_STATUS_FIFO_READY_FOR_RX   ,-1,"MMC_STATUS_FIFO_READY_FOR_RX" },
  {  MMC_STATUS_FIFO_TX_HALF_FULL   ,-1,"MMC_STATUS_FIFO_TX_HALF_FULL" },
  {  MMC_STATUS_FIFO_EMPTY          ,-1,"MMC_STATUS_FIFO_EMPTY" },
  {  MMC_STATUS_FIFO_RX_LAST_DATA   ,-1,"MMC_STATUS_FIFO_RX_LAST_DATA" },
  {  MMC_STATUS_FIFO_RX_FULL        ,-1,"MMC_STATUS_FIFO_RX_FULL" },
  {  MMC_STATUS_FIFO_RX_HALF_FULL   ,-1,"MMC_STATUS_FIFO_RX_HALF_FULL" },
  {  MMC_STATUS_FIFO_COUNT_MASK     ,-1,"MMC_STATUS_FIFO_COUNT" },
};

/*******
 * Prototype static void decode_status( u16 status )
 * Purpose:
 *        1. verify SD Command status / SD Data Status
 *           FIFO status
 * Entry
 * Exit
 * Exceptions:
 *****************************************************/
static void decode_status( u16 status[])
{
	int i;
	int cmd_len  = sizeof( cmd_status_bit_names)/sizeof(struct status_bit_to_name);
	int data_len = sizeof(data_status_bit_names)/sizeof(struct status_bit_to_name);
	int fifo_len = sizeof(fifo_status_bit_names)/sizeof(struct status_bit_to_name);

	struct status_bit_to_name *b = cmd_status_bit_names;

	for ( i = 0 ; i <cmd_len ; i++, b++ ) {
		if ( status[0] & b->mask ) {
			if ( b->shift >= 0 )
				printk("%s[%d] ",b->name,((status[0] & b->mask) >> b->shift));
			else
				printk("%s ", b->name);
		}
	}
	
	b = data_status_bit_names;
	for ( i = 0 ; i <data_len ; i++, b++ ) {
		if ( status[1] & b->mask ) {
			if ( b->shift >= 0 )
				printk("%s[%d] ",b->name,((status[1]& b->mask)>> b->shift));
			else
				printk("%s ",b->name);
		}
	}

	b = fifo_status_bit_names;
	for ( i = 0 ; i <fifo_len ; i++, b++ ) {
		if ( status[2]& b->mask ) {
			if ( b->shift >= 0 )
				printk("%s[%d] ",b->name,((status[2]&b->mask)>> b->shift));
			else
				printk("%s ", b->name);
		}
	}
}


/*******
 * Prototype 
 *     static  void mmc_s3c_reset_timeout( unsigned long nr )
 * Purpose:
 *        1. timeout + reset 
 * Entry
 * Exit
 * Exceptions:
 *       I don't know how to implement but
 *         1. clock down or slow
 *         2. FIFO RESETw
 *         3. MMC Interrupt disable and enable
 *****************************************************/
static void mmc_s3c_reset_timeout( unsigned long nr )
{
//	struct s3c_mmc_data *sd = (struct s3c_mmc_data *) nr;

	/* Send 80 clocks to get things started */
/*
#ifdef CONFIG_MMC_DEBUG
	START_MMC_DEBUG(2) { 
		u16 status = S3C_ASIC1_MMC_Status;
		printk(__FUNCTION__ ": enabling irq mask=%04x status=0x%04x (", 
		       S3C_ASIC1_MMC_InterruptMask, status);
		decode_status(status); 
		printk(")\n"); 
	} END_MMC_DEBUG;
#endif
*/
//	mod_timer( &g_s3c_mmc_data.irq_timer, jiffies + MMC_IRQ_TIMEOUT); 
	enable_irq( lowlevel->sdi_irq );
}


static void mmc_s3c_start_clock(void)
{
       rSDICON |= CLOCK_OUT_EN;
       rSDICCON |= CMD_START;
}


/*******
 * Prototype 
 *   static int mmc_s3c_stop_clock(void)
 *   
 * Purpose:
 *   
 * Entry
 * Exit
 * Exceptions:
 *   must add routine to check the status register fields.
 *****************************************************************/

static int mmc_s3c_stop_clock(void)
{
       //rSDIDCON &= ~CMD_START;
       rSDICON  &= ~CLOCK_OUT_EN;
       return MMC_NO_ERROR;
}


static int mmc_s3c_set_clock( u32 rate )
{
	int retval;
#if 0
	int prescaler = 0;

	prescaler = PCLK/(2*rate) - 1;
#endif
	retval = mmc_s3c_stop_clock(); 
	if ( retval )	return retval;
	mmc_s3c_start_clock();
	g_s3c_mmc_data.clock = rate  ;
	return MMC_NO_ERROR;
}


/*******
 * Prototype 
 *  	static int mmc_s3c_adjust_clock( u32 clock_rate )
 *
 * Purpose:
 *      
 * Entry
 * Exit
 * Exceptions:
 *          BAUD RATE = PCLK/2/(PRESCALER +1)
 *          rSDIPRE = PRESCALER( 8 bits) 
 *******************************************************************/
static int mmc_s3c_adjust_clock( u32 clock_rate )
{
	int retval;
	retval = mmc_s3c_set_clock( clock_rate );
	mmc_s3c_start_clock();
	return retval;
}

/*******
 * Prototype 
 *       staitc void sd_status_clear(int index ) 
 * Purpose:
 * Entry:
 *      0 : cmd status reg.
 *      1 : data status reg.	
 *      2 : fifo status reg.
 * Exit
 * Exceptions:
 *           if you want to advance performance ,get rid of this func.
 *****************************************************************/
static void sd_status_clear(int index)
{
	switch ( index ) {
		case 0:
			rSDICSTA = MMC_CMD_STATUS_WRITEONE_CLEAR;
			break;
		case 1:
			rSDICSTA = MMC_CMD_STATUS_WRITEONE_CLEAR;
			rSDIDSTA = MMC_DATA_STATUS_WRITEONE_CLEAR;
			break;
		case 2:
			rSDICSTA = MMC_CMD_STATUS_WRITEONE_CLEAR;
			rSDIDSTA = MMC_DATA_STATUS_WRITEONE_CLEAR;
			//rSDIFSTA = MMC_FIFO_STATUS_WRITEONE_CLEAR; /* Read Only */
			break;
		default:
			break;
	}
}

/*******
 * Prototype 
 *       staitc int  chk_CMDend(int cmd, int be_resp)
 * Purpose:
 *        1. CMD0 use this function.
 *        2. Debug 
 *        	After issuing one command , you can see response chk_CMDend 
 *        	while making busy-waiting.
 * Entry
 * Exit
 * Exceptions:
 *           not sure  whether other command needs chk_CMDend func.
 *****************************************************************/
static int chk_CMDend(int cmd, int be_resp)
{
	int status;
	
#define CONFIG_MMC_SPINLOOP_TIMEOUT 10000000

	MMC_DEBUG(2, "");
	if(!be_resp)
	{
		int count = 0;
		status = rSDICSTA;
		while ( !(status & MMC_STATUS_CMD_SENT_DONE) && (count++ < CONFIG_MMC_SPINLOOP_TIMEOUT)) {
			status = rSDICSTA;
		}
		MMC_DEBUG(2,"   STATUS=0x%08X",rSDICSTA);
		if (count >= CONFIG_MMC_SPINLOOP_TIMEOUT) {
			MMC_DEBUG(0, "cmd timed out status=%#x", status);
			return -EIO;
		}
		MMC_DEBUG(2,"    Ending  Progressing");
		sd_status_clear(0);
		return 0;
	}
   	else	// With response
   	{
		int count = 0;
   	 	status=rSDICSTA;
		while ( !(status&MMC_STATUS_CMD_SENT_DONE)&&!(status&MMC_STATUS_RESPONSE_RECV_DONE)
			&& (count++ < CONFIG_MMC_SPINLOOP_TIMEOUT)) {
			printk(".");
			status = rSDICSTA;	
		}
		if(cmd==MMC_SEND_OP_COND || cmd==MMC_SEND_CSD || cmd==MMC_READ_DAT_UNTIL_STOP)	
			// CRC no check
		{
	    		if( (status&0xf00) != 0xa00 )  // Check error
		   	{
				rSDICSTA=status;   // Clear error state
				if(((status&0x400)==0x400)) {
					MMC_DEBUG(2," TIMEOUT ERROR");	
					return -EIO;	// Timeout error
				}
				else {
					MMC_DEBUG(2," RESPONSE OK ");
				}
    	   		}
			rSDICSTA=status;	// Clear cmd & rsp end state
		}
		else	// CRC check
		{
			if( (status&0x1f00) != 0xa00 )	// Check error
	    		{
				MMC_DEBUG(2," ERROR CMD%d:rSDICSTA=0x%x, rSDIRSP0=0x%x",cmd, rSDICSTA, rSDIRSP0);
				rSDICSTA=status;   // Clear error state

				if(((status&0x400)==0x400))
		    		return -EIO;	// Timeout error
    	    		}
	   		rSDICSTA=status;
		}
		return 0;
    	}
}

static void  mmc_CMD0(void) 
{
	MMC_DEBUG(2,"");
	rSDICARG = 0x0;  
	rSDICCON = MMC_STATUS_TRANS_ON | START_TRANSMISSION_PRE;
	chk_CMDend(0,0);
}

/*******
 * Prototype 
 *  	static int mmc_s3c_reset( struct s3c_mmc_data *sd )
 *
 * Purpose:
 *      1. mmc_cim_init_stack(struct mmc_dev *dev,int first ) issue "MMC_CIM_RESET"
 *      2. implement "CMD0"
 * Entry
 * Exit
 * Exceptions:
 *     It is nessecary to get clock slow that obtains the sufficient response time.
 *****************************************************************************/
static int mmc_s3c_reset( struct s3c_mmc_data *sd )
{
	int retval = 0 ;
	MMC_DEBUG(2,"");
//	retval = mmc_s3c_adjust_clock( MMC_CLOCK_SLOW );
	g_s3c_mmc_data.type = RT_NO_RESPONSE;
//	mod_timer( &sd->reset_timer, jiffies + (100 * HZ) / 1000 );
//	g_s3c_asic_statistics.mmc_reset++;
	mmc_CMD0();
	return retval;
}



/*******
 * Prototype 
 *  	mmc_s3c_set_command( request->cmd, request->arg )
 *
 * Purpose:
 *         1. set SDICARG
 *         2. set the "Command with data " of SDICCON
 *         3. clear registers
 *         4. set cmd index including Start bit & transmission bit 
 *            and response type.
 * Entry
 * Exit
 * Exceptions:
 *            Not sure that 2 must be set here.
 *******************************************************************/
static void mmc_s3c_set_command( u32 cmd, u32 arg, enum mmc_rsp_t rtype )
{
        u32 cc_reg = 0;
	//	MMC_DEBUG(2,": cmd=%d arg=0x%08x", cmd, arg);
	MMC_DEBUG(2,"");
	/* write one clear */
	rSDICSTA = ( MMC_STATUS_CRC_RESPONSE_ERROR
		    |MMC_STATUS_CMD_SENT_DONE
		    |MMC_STATUS_RESPONSE_TIMEOUT
		    |MMC_STATUS_RESPONSE_RECV_DONE
                    ); 
	rSDICARG = arg;
	cc_reg  = cmd |START_TRANSMISSION_PRE|NORMAL_COMMAND;

	if (g_s3c_mmc_data.type == RT_READ || g_s3c_mmc_data.type==RT_WRITE){
	}
	
#define S3C_SDIO
#ifdef S3C_SDIO
	if (g_s3c_mmc_data.type == RT_READ || g_s3c_mmc_data.type==RT_WRITE){
		cc_reg |= CMD_WITH_DATA;	
	}
#endif	
	if( rtype != RESPONSE_NONE ) {
	        cc_reg |= WAIT_RESPONSE;
		if ( rtype==RESPONSE_R2_CID || rtype==RESPONSE_R2_CSD ) {
		        cc_reg |= LONG_136RESPONSE;   
		}
	} 
	else {
		MMC_DEBUG(2,"  RESPONSE_NONE");
	}
	rSDICCON = cc_reg;
	MMC_DEBUG(2,": rSDICCON 0x%08X  rSDIDTIMER Ox%08X ",rSDICCON,rSDIDTIMER);
}


/*******
 * Prototype 
 * static void mmc_s3c_set_transfer(u16 block_len, u16 nob,u16 int cmd)
 * Purpose:
 *          1. set SDIDIMER, SDIDCON
 * Entry
 * Exit
 * Exceptions:
 *           some field of SDIDCON must be moved into initial code of mmc driver.
 *******************************************************************/
static void mmc_s3c_set_transfer(u16 block_len,u16 nob,int cmd)
{
	MMC_DEBUG(2,": block_len=%d nob=%d", block_len, nob);
        rSDIDTIMER = SDIDTIMER_DEFAULT;
	if ( block_len > 0x1000) {
		MMC_DEBUG(2,"ERROR BLOCK SIZE");
	}
	rSDIBSIZE = block_len;

	if ( nob  > 0x1000) {
		MMC_DEBUG(2,"ERROR Num BLOCK ");
	}
//	rSDIDCON  |=  (rSDIDCON& ~DATA_MAX_BLKNUM)| nob;
	rSDIDCON   =  nob;

	/* ---------------------------------------------------------------*/
	/* Move Me to init routine */
	/* INT_PERIOD_SINGLEBLOCK , WIDE_BUS_MODE,DMA_MODE, STOP_BY_FORDE of SDIDCON */

	/* Wide Bus Mode Disable */
	rSDIDCON |=RECV_AF_CMD_NODIRECT|DATA_TRANS_BLOCK|BUSY_AF_CMD_NODIRECT|STANDARD_BUS_MODE;
	/* ---------------------------------------------------------------*/
	switch (cmd) {
	case MMC_READ_SINGLE_BLOCK:
	case MMC_READ_MULTIPLE_BLOCK:
                rSDIDCON |= DATA_RECV_START;    		
		break;
	case MMC_WRITE_BLOCK:
	case MMC_WRITE_MULTIPLE_BLOCK:
	        rSDIDCON |= DATA_TRANS_START;
		break;
	default:
	  	break;
	}
}


/*******
 * Prototype 
 *   static int mmc_s3c_exec_command( struct mmc_request *request )
 *
 * Purpose:
 *       1. Directly Not containing Register 
 * Entry
 * Exit
 * Exceptions:
 *     what is busy check irq enable --- only for write ?
 *     what is CRC status errors interrupt 
 *     where is RT_NO_RESPONSE ? -- must be implemented in mmc_s3c_reset 
 *****************************************************************/
static int mmc_s3c_exec_command( struct mmc_request *request )
{
	int retval;
	int irq = 0;

	disable_irq(lowlevel->sdi_irq);
	MMC_DEBUG(2," request = %p request->rtype  0x%08X ",request,request->rtype);

	/* Start the actual transfer */
	retval = mmc_s3c_stop_clock();
	if ( retval )   return retval;

	switch (request->cmd) {
	case MMC_READ_SINGLE_BLOCK:
	case MMC_READ_MULTIPLE_BLOCK:
  		g_s3c_mmc_data.type = RT_READ;
	        mmc_s3c_set_transfer(request->block_len,request->nob,request->cmd);	
		irq = S3C_RX_FIFO_LAST_DATA_INT_ON| S3C_RX_FIFO_HALF_INT_ON 
		      |S3C_CMD_RESPONSE_TIMEOUT_INT_ON| S3C_RESPONSE_CRC_INT_ON|S3C_RESPONSE_RECV_INT_ON;
		break;
	case MMC_WRITE_BLOCK:
	case MMC_WRITE_MULTIPLE_BLOCK:
    		g_s3c_mmc_data.type = RT_WRITE;
		mmc_s3c_set_transfer(request->block_len,request->nob,request->cmd);
		irq = /*S3C_TX_FIFO_EMPTY_INT_ON |*/ S3C_TX_FIFO_HALF_INT_ON |S3C_DATA_CNT_ZERO_INT_ON
		      |S3C_CMD_RESPONSE_TIMEOUT_INT_ON|S3C_RESPONSE_CRC_INT_ON |S3C_BUSY_COMPLETE_INT_ON;
		break;
	default:
       		g_s3c_mmc_data.type = RT_RESPONSE_ONLY;
		irq = S3C_RESPONSE_RECV_INT_ON|S3C_CMD_RESPONSE_TIMEOUT_INT_ON;	
		/* response only interrupt */
		break;
	}
	/* mmc_s3c_set_command must run after mmc_s3c_set_transfer closely */

	mmc_s3c_set_command(request->cmd,request->arg,request->rtype);
	rSDIIMSK = irq;

	MMC_DEBUG(2,"   Start clocking   ");
	if ( request->cmd == MMC_READ_SINGLE_BLOCK)  {
		unsigned int i ;
		for ( i = 0; i < 0x0f000000;i++)
			;
	}

	MMC_DEBUG(2,"   Real Start clocking   ");
	mmc_s3c_start_clock();

#ifdef S3C_CMD_DONE_BUSY_WAITING_DEBUG 
	if ( request->cmd == MMC_READ_SINGLE_BLOCK)  {
		if ( chk_CMDend(MMC_READ_SINGLE_BLOCK,1) == 0 ) {
				MMC_DEBUG(2," RESPONSE & CMD done ");
		}
		do {} while(1); 
	}
#endif
/*
#ifdef CONFIG_MMC_DEBUG
	START_MMC_DEBUG(2) { 
		u16 status[3];
		status[0] = SDICSTA;
		status[1] = rSDIDSTA;
		status[2] = rSDIFSTA;
		printk(__FUNCTION__ ": status[0]=%04x status[1]=0x%04x status[2]=0x%04x ", 
		       status[0],status[1],status[2]);
		decode_status(status); 
		printk(")\n"); 
	} END_MMC_DEBUG;
#endif
*/
//	mod_timer( &g_s3c_mmc_data.irq_timer, jiffies + MMC_IRQ_TIMEOUT); 
	
	/* 
	 * I want to delay the MMC Interrupt until this function end.
	 */
//	mmc_delay();	
	MMC_DEBUG(2,"   Before Start lowlevel->sdi_irq     ");
	enable_irq( lowlevel->sdi_irq );
	//	g_s3c_asic_statistics.mmc_command++;
	return MMC_NO_ERROR;
}


/*******
 * Prototype 
 *      static void mmc_s3c_send_cmd (struct mmc_request * mr) 
 * Purpose:
 * Entry
 * Exit
 * Exceptions:
 *         not fully convinced of mmc_s3c_reset functions 
 ************************************************************/
static void mmc_s3c_send_command (struct mmc_request * request) 
{
	int retval;
/*
	MMC_DEBUG(2," rINTMSK   0x%08X
		  rINTMOD   0x%08X
	  	  rEXTINT2  0x%08X
		  rEINTMSK 0x%08X
		  rEINTPND 0x%08X 
		  rGPGCON  0x%08X ", rINTMSK,rINTMOD,rEXTINT2,rEINTMSK,rEINTPND,rGPGCON );
*/
	MMC_DEBUG(1,
	      ": request=%p cmd=%d (%s) arg=%08x "
	      "             Cmd   status=%04x"
	      "             Data  status=%04x"
	      "             FIFO  status=%04x", 
	      request, request->cmd, get_cmd_name(request->cmd), request->arg,
	      rSDICSTA,rSDIDSTA,rSDIFSTA);

	/* Global Lock Needed */
	g_s3c_mmc_data.request = request;
	request->result = MMC_NO_RESPONSE; /* Flag to indicate don't have result yet */

	if ( request->cmd == MMC_CIM_RESET  ||  request->cmd == 0 /* GO_IDLE_STATE */) {
		retval = mmc_s3c_reset(&g_s3c_mmc_data);
	}
	else {
		retval = mmc_s3c_exec_command(request);
		MMC_DEBUG(2,"  retval 0x%08X   ",retval);
		
#if 0		
		if ( retval) {
	  //			g_s3c_statistics.mmc_error++;
			MMC_DEBUG(0," MMC not responding ! Trying to reset");
			mmc_s3c_stop_clock();
			retval = mmc_s3c_adjust_clock(g_s3c_mmc_data.clock);
			if(retval) {
			       MMC_DEBUG(0," MMC doesn't response  ! Panic Now");
			}
			else {
			       retval = mmc_s3c_exec_command(request);
			       if (retval) {
				      MMC_DEBUG(0," ASIC unable to exec !");
			       }
			}
		}
#endif		
	}
	if( retval) {
		request->result = retval;
		MMC_DEBUG(2,"  Before mmc_cmd_complete  ");
		mmc_cmd_complete(request);
	}
}

	
static void mmc_s3c_fix_sd_detect( unsigned long nr )
{
	int sd_card = ! lowlevel->slot_is_empty(lowlevel);
	int sd_con_slot = g_s3c_mmc_data.insert;

	MMC_DEBUG(2,": card=%d con_slot=%d", sd_card, sd_con_slot);

	if ( sd_card ) {
		mmc_insert(0);
	}
	else {
		mmc_eject(0);
		// mmc_s3c_set_clock (MMC_CLOCK_SLOW);
	}
}

/* the ASIC interrupted us because it detected card insertion or removal.
 * wait 1/4 second and see which it was.
 */
static void mmc_s3c_sd_detect_isr(int irq, void *dev_id, struct pt_regs *regs)
{
	struct s3c_mmc_data *sd = (struct s3c_mmc_data *) dev_id;
	mod_timer( &sd->sd_detect_timer, jiffies + (250 * HZ) / 1000 );
}


/*******
 * Prototype 
 * static void mmc_s3c_get_response( struct mmc_request *request )
 * Purpose: the MMC upper layer determines only Response type and the other response information 
 *          should be provided in host adapter. 
 * Entry
 * Exit
 * Exceptions:
 *          1. Command Status Register[8][command Transfer in progress] can happen
 *            to exist with command response received[9] ?
 *          2. To improve the performance of S3C SD Host, we must put the component of 
 *             response into upper layer.but Now we reconstruct SDIRSP0 | SDIRSP1 into one response format
 *          3. response buffer is 18 bytes and last two byte means crc 
 ***********************************************************************/
static void mmc_s3c_get_response( struct mmc_request *request )
{
	int i,reg;
	int len = rinfo[request->rtype].length;
	u8 *buf = request->response;
	
	request->result = MMC_NO_ERROR;    	/* Mark this as having a request result of some kind */
	if ( len <= 0 )				/* MMC_NO_RESPONSE */
		return;
	switch ( len) {
	case 6:					/* 6 bytes response */
		reg = rSDIRSP0;
		buf[0] =  rSDICSTA & MMC_STATUS_RESPONSE_INDEX_MASK ;/*  */
		buf[1] =  (reg & 0xff000000) >> 24;
		buf[2] =  (reg & 0x00ff0000) >> 16;
		buf[3] =  (reg & 0x0000ff00) >> 8;
		buf[4] =  (reg & 0x000000ff) >> 0;
		buf[5] =  (u8) ((rSDIRSP1&MMC_RESPONSE_CRC_8_0)>>SDIRSP1_CRC_SHIFT ); /* CRC+ end bit */
		break;
	case 17:				/* R2 (CID,CSD registers)  */
		reg = rSDIRSP0;
		buf[0] =  MMC_RESPONSE_LONG_135_128_VAL;
		buf[1] =  (reg & 0xff000000) >> 24;
		buf[2] =  (reg & 0x00ff0000) >> 16;
		buf[3] =  (reg & 0x0000ff00) >> 8;
		buf[4] =  (reg & 0x000000ff) >> 0;
		reg = rSDIRSP1;
		buf[5] =  (reg & 0xff000000) >> 24;
		buf[6] =  (reg & 0x00ff0000) >> 16;
		buf[7] =  (reg & 0x0000ff00) >> 8;
		buf[8] =  (reg & 0x000000ff) >> 0;
		reg = rSDIRSP2;
		buf[9] =  (reg & 0xff000000) >> 24;
		buf[10] =  (reg & 0x00ff0000) >> 16;
		buf[11] =  (reg & 0x0000ff00) >> 8;
		buf[12] =  (reg & 0x000000ff) >> 0;
		reg = rSDIRSP3;
		buf[13] =  (reg & 0xff000000) >> 24;
		buf[14] =  (reg & 0x00ff0000) >> 16;
		buf[15] =  (reg & 0x0000ff00) >> 8;
		buf[16] =  (reg & 0x000000ff) >> 0;
		break;
	default:
		break;	
	}
#ifdef CONFIG_MMC_DEBUG
	START_MMC_DEBUG(2) {
		printk("%s:  Raw byte stream: ", __FUNCTION__);
		for ( i = 0 ; i < len; i++ )
			printk("%02x ", buf[i]);
		printk("\n");
	} END_MMC_DEBUG;
#endif
}


/*******
 * Prototype 
 *     static void mmc_s3c_transmit_data( struct mmc_request *request )
 * Purpose:
 * Entry
 * Exit   
 *        0 : the other blocks to send remaind
 *        1 : transfer done
 * Exceptions:
 *     S3C FIFO SIZE of SD is 64 Bytes
 *     block_len * nob should be multiple of S3C_SD_FIFO_SIZE 
 ***********************************************************************/
static int mmc_s3c_transmit_data( struct mmc_request *request )
{
	u32 *buf32 = (u32 *)request->buffer;
	int maxdata = request->block_len * request->nob;
	int i;

	if ( (g_s3c_mmc_data.size2send == maxdata) || (maxdata == 0) ) {
	        request->nob = 0;
		g_s3c_mmc_data.size2send = 0;
		return 1;	/* transfer done */
	}

      	for ( i= 0; i < S3C_SD_FIFO_SIZE ;i += 4 ) {
	        rSDIDAT = *buf32++;
	}
	g_s3c_mmc_data.size2send += (i-4);  	/* offset adjusting  */
	if (0== (g_s3c_mmc_data.size2send % request->block_len))
		request->nob--;
	MMC_DEBUG(2,": nob=%d block_len=%d", request->nob, request->block_len);

/*
#ifdef CONFIG_MMC_DEBUG
	START_MMC_DEBUG(2) { 
		u16 status = H3800_ASIC1_MMC_Status;


		printk(__FUNCTION__ ": irq_mask=%04x status=0x%04x (", 
		       H3800_ASIC1_MMC_InterruptMask, status);
		decode_status(status); 
		printk(")\n"); 
	} END_MMC_DEBUG;

  	START_MMC_DEBUG(3) {
		u8 *b = request->buffer;
		for ( i = 0 ; i < request->block_len ; i++ ) {
			printk(" %02x", *b++);
			if ( ((i + 1) % 16) == 0 )
				printk("\n");
		}
	} END_MMC_DEBUG;
#endif
*/

	/* Updated the request buffer to reflect the current state */
	request->buffer = (u8 *) buf32;
	//	g_s3c_asic_statistics.mmc_written++;
	return 0;
}


/*******
 * Prototype 
 *     static int mmc_s3c_receive_data( struct mmc_request *request,int flag )
 * Purpose:
 * Entry  
 *     flag -1 : RX_FIFO_HALF_FULL
            others : the size of last FIFO data when RX_FIFO_LAST_DATA occurs.
 * Exit   
 *     -1     : Unexpected Error
 *     0      : last data
 *     1      : keep receiving data.
 * Exceptions:
 ***********************************************************************/
static int mmc_s3c_receive_data( struct mmc_request *request, int flag )
{
	u8 *buf = (u8 *)request->buffer;
	u32  data;
	int i;

	MMC_DEBUG(2,": nob=%d block_len=%d buf=%p", request->nob, request->block_len, buf);
	if ( request->nob <= 0 ) {
		MMC_DEBUG(1,": *** nob already at 0 ***");
		return -1;
	}


	if ( flag != -1 ) { 		
		
		if (rSDIDSTA & MMC_STATUS_RX_DATA_ON_PROGRESS) {
			MMC_DEBUG(2, " Not yet DATA Rx in progress ");
		}
		g_s3c_mmc_data.size2read +=flag;

		i = flag % 4;
		if( i  !=  0 ) {
		           MMC_DEBUG(2," UNEXPECTED FIFO COUNT 1 ");
		}

		i = g_s3c_mmc_data.size2read % 512;

		if ( i  !=  0 ) {
		        MMC_DEBUG(2," UNEXPECTED FIFO COUNT  2");
		}

		for (i=0; i < flag            ; i += 4) {
			data = rSDIDAT;
			*buf++ = data >> 0 & 0xff;
			*buf++ = data >> 8 & 0xff;
			*buf++ = data >> 16 & 0xff;
			*buf++ = data >> 24 & 0xff;
		}

		if ( request->nob > 1 ) {
			MMC_DEBUG(2," Unexpected nob count error ");
		} 
		else {
			request->nob-- ; 
		}

		request->buffer = buf;
		MMC_DEBUG(2," Total Read Size for single read cmd 0x%08X",g_s3c_mmc_data.size2read);
		g_s3c_mmc_data.size2read = 0;
		return 0 ;
	}

	if ( (rSDIFSTA & MMC_STATUS_FIFO_COUNT_MASK) != S3C_SD_FIFO_SIZE) {
		MMC_DEBUG(2, " Unexpected FIFO Data count v.s Received Data ");
	}

	for (i=0; i < S3C_SD_FIFO_SIZE; i += 4) {
		data = rSDIDAT;
		*buf++ = data >> 0 & 0xff;
		*buf++ = data >> 8 & 0xff;
		*buf++ = data >> 16 & 0xff;
		*buf++ = data >> 24 & 0xff;
	}

	g_s3c_mmc_data.size2read += S3C_SD_FIFO_SIZE;	
	if (0 == (g_s3c_mmc_data.size2read % request->block_len)) 
		request->nob--;	

	if ( !request->nob ) {
		while(!(rSDIDSTA&MMC_STATUS_DATA_TRANSFER_DONE)){
			;
		}
		rSDIDSTA = MMC_STATUS_DATA_TRANSFER_DONE;
		request->buffer = buf;
		g_s3c_mmc_data.size2read = 0;
		MMC_DEBUG(2,": Ending nob=%d block_len=%d buf=%p", request->nob, request->block_len, buf);
	
		MMC_DEBUG(2, " accumulated g_s3c_mmc_data.size2read 0x%08X rSDIDSTA 0x%08X rSDIFSTA 0x%08X",
			g_s3c_mmc_data.size2read,rSDIDSTA,rSDIFSTA);

#ifdef CONFIG_MMC_DEBUG
		START_MMC_DEBUG(3) {
		u8 *b = request->buffer - 512 ;
		for ( i = 0 ; i < request->block_len ; i++ ) {
			printk(" %02x", *b++);
			if ( ((i + 1) % 16) == 0 )
				printk("\n");
			}
		} END_MMC_DEBUG;
#endif

		return 0;
	}

	MMC_DEBUG(2, " accumulated g_s3c_mmc_data.size2read 0x%08X rSDIDSTA 0x%08X rSDIFSTA 0x%08X",
			g_s3c_mmc_data.size2read,rSDIDSTA,rSDIFSTA);



	/* Updated the request buffer to reflect the current state */
	request->buffer = buf;

	//	g_s3c_asic_statistics.mmc_read++;
	return 1;
}


/*******
 * Prototype 
 * static void mmc_s3c_split_status( struct s3c_mmc_data *sd, u16 status[], int timeout )
 * Purpose:
 *       1. status[SDI_CMD]= command status
 *          status[SDI_DATA]= data    status
 *          status[SDI_FIFO]= FIFO    status
 *       2. collect the same sorts of interrupts. 
 * Entry
 * Exit
 *       timeout not used 
 * Exceptions:
 *       where is timeout routine ?
 *       rSDIIMSK = S3C_ALL_MMC_INT_MASK is right ?
 *        
 *   1.  In MMC mode, MAX clock rate is 10 Mhz
 *   2.  In MMC write mode(response only, ex cmd1), the CRC error is
 *       occured in spite of correct writing. 
 *   3.  In case of long response, the CRC error should be detected after receiving
 *       exact response data from SD device. User should check the CRC of received response
 *       by software.
 *   4.  Supposed that RESPONSE State and DATA RECEIVE don't appeare simultaneously. 
 ********************************************************************************************/
static void mmc_s3c_split_status( struct s3c_mmc_data *sd, u16 status[], int timeout )
{
	int retval = MMC_NO_ERROR;
	int rsp_done = 0,msk_reg =0,data_busy=0;

	disable_irq( lowlevel->sdi_irq );	/* It needs test !! */

	if ((status[SDI_CMD] & MMC_STATUS_RESPONSE_TIMEOUT )  || 
            (status[SDI_DATA]& MMC_STATUS_DATA_BUSY_TIMEOUT)
	    ) {
		MMC_DEBUG(0,"  MMC_ERROR_TIMEOUT ");
//		mmc_s3c_get_response(sd->request);
		retval = MMC_ERROR_TIMEOUT;
		goto terminate_int;
	}
	
	if ((status[SDI_CMD] & MMC_STATUS_CRC_RESPONSE_ERROR )  || 
            (status[SDI_DATA]& (MMC_STATUS_CRC_WRITE_ERROR|MMC_STATUS_CRC_READ_ERROR))
	    ) {
		if (/* Slot device is MMC && see Exception 2.*/
			   g_s3c_mmc_data.type == RT_RESPONSE_ONLY) {
			MMC_DEBUG(0,"  DUMMY MMC_ERROR_CRC     ");
		}
		else {
			MMC_DEBUG(0,"  MMC_ERROR_CRC     ");
			retval = MMC_ERROR_CRC;
			goto terminate_int;
		}
	}

	if ( status[SDI_CMD]& MMC_STATUS_RESPONSE_RECV_DONE 
	     && sd->request->result == MMC_NO_RESPONSE /* not yet response receveied. */
	    ) { 
		rsp_done = 1;
		MMC_DEBUG(0,"  get_response      ");
		mmc_s3c_get_response( sd->request );
	}

	/** TODO :Fix me...
	 * Read operation data error
	 * Where do I handle "data error" not CRC Error--> I don't know
	 */
	
	if ((g_s3c_mmc_data.type==RT_READ) ){ 
		 if ( status[SDI_FIFO]&MMC_STATUS_FIFO_RX_FULL) {
	    	         mmc_s3c_receive_data( sd->request,-1 );
		 }
		 else {
		         if (status[SDI_FIFO]&MMC_STATUS_FIFO_RX_LAST_DATA) {
		     	         mmc_s3c_receive_data( sd->request,
				     status[SDI_FIFO]&MMC_STATUS_FIFO_COUNT_MASK );
			 }
			 else {
				 if (rsp_done){
				 } 
				 else {
			         	MMC_DEBUG(0,": ERROR READ COMMAND 0x%08X,0x%08X,0x%08X"
					 ,status[SDI_CMD],status[SDI_DATA],status[SDI_FIFO]);
				 }
			 }
		 }
	}
	
	if (g_s3c_mmc_data.type==RT_WRITE && status[SDI_DATA]&MMC_STATUS_DATA_BUSY_TIMEOUT) {
		MMC_DEBUG(2,"  Not BUSY Finish");
		data_busy =1;
	}
	if ((g_s3c_mmc_data.type==RT_WRITE)&& !rsp_done && !data_busy) { 
		if ( ((status[SDI_FIFO]&MMC_STATUS_FIFO_TX_HALF_FULL)
					||(status[SDI_FIFO]&MMC_STATUS_FIFO_EMPTY))
	           &&(sd->request->nob>0)
		   &&!(status[SDI_CMD]&MMC_STATUS_DATA_TRANSFER_DONE)
		   ){
	        	mmc_s3c_transmit_data(sd->request); 
		       /*
			* if we have the data block less than 32 bytes but Not in data transfer complete,
			*  we contine to receive the TX_FIFO_HALF_FULL so I want to get rid of this INT.
		        */
		}
	}
		
	switch (g_s3c_mmc_data.type) {
	case RT_NO_RESPONSE:
		MMC_DEBUG(2,"  RT_NO_RESPONSE ");
		msk_reg  = S3C_ALL_MMC_INT_MASK;
		break;
	case RT_RESPONSE_ONLY:
		MMC_DEBUG(2,"  RT_RESPONSE_ONLY ");
		if ( sd->request->result < 0 ) {
			printk(KERN_INFO "%s: illegal interrupt - command hasn't finished\n", __FUNCTION__);
			retval = MMC_ERROR_TIMEOUT;
		}
		msk_reg  = S3C_ALL_MMC_INT_MASK;
		break;
	case RT_READ:
		if(sd->request->nob <=0 )  {
			msk_reg  = S3C_ALL_MMC_INT_MASK;
			break;
		}
		/* It is impossible to get both response and data  */
		if ( sd->request->nob ) {
			msk_reg = S3C_RX_FIFO_LAST_DATA_INT_ON | S3C_RX_FIFO_FULL_INT_ON;
			MMC_DEBUG(2,": read SDIIMSK  mask=0x%08x",rSDIIMSK);
			//			mod_timer( &sd->irq_timer, jiffies + MMC_IRQ_TIMEOUT); 
		//	mmc_s3c_start_clock(); /* redundancy ? */
			rSDIIMSK = msk_reg; 	
			enable_irq(lowlevel->sdi_irq);
			return;
		}
		if(rsp_done) {
			MMC_DEBUG(2,": READ RESPONSE DONE ");
			MMC_DEBUG(2,": REGS sdicon 0x%08X sdiccon %08X sdidcon 0x%08X"
					,rSDICON,rSDICCON,rSDIDCON);
			MMC_DEBUG(2,": REGS cmd 0x%08X data 0x%08X fifo 0x%08X",rSDICSTA,rSDIDSTA,rSDIFSTA);
			MMC_DEBUG(2,": REGS cmd 0x%08X data 0x%08X fifo 0x%08X",rSDICSTA,rSDIDSTA,rSDIFSTA);
			msk_reg = S3C_RX_FIFO_LAST_DATA_INT_ON | S3C_RX_FIFO_FULL_INT_ON;
			rSDIIMSK = msk_reg;
			enable_irq(lowlevel->sdi_irq);
			return;
		}
		MMC_DEBUG(2," Unexpected Error ");
		break;

	case RT_WRITE:
		if( sd->request->nob <= 0) {
			msk_reg = S3C_ALL_MMC_INT_MASK;
			break;
		}	
		if( (sd->request->nob || !(status[SDI_DATA]& MMC_STATUS_DATA_TRANSFER_DONE)) 
		   && !data_busy ){
			msk_reg = S3C_DATA_CNT_ZERO_INT_ON|S3C_BUSY_COMPLETE_INT_ON|
				S3C_TX_FIFO_HALF_INT_ON|S3C_TX_FIFO_EMPTY_INT_ON;
			MMC_DEBUG(2,": write SDIIMSK  mask=0x%08x",rSDIIMSK);
			//			mod_timer( &sd->irq_timer, jiffies + MMC_IRQ_TIMEOUT); 
			mmc_s3c_start_clock(); /* redundancy ? */
			rSDIIMSK  = msk_reg;
			enable_irq( lowlevel->sdi_irq );
			return;
		}
		if(data_busy)  {
			msk_reg = S3C_BUSY_COMPLETE_INT_ON;
		}
		else  {
			msk_reg  = S3C_ALL_MMC_INT_MASK;
		}
		break;
	}
	//	MMC_DEBUG(2,": terminating status=0x%04x", S3C_ASIC1_MMC_Status );
terminate_int:
	rSDIIMSK = msk_reg; 
	//	del_timer_sync( &sd->irq_timer );
	sd->request->result = retval;
	MMC_DEBUG(2,"  Before mmc_cmd_complete  ");
	mmc_cmd_complete( sd->request );
}


/*******
 * Prototype 
 *  static void mmc_s3c_general_mmc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 *
 * Purpose:
 *       1. Analysis Command,Data and Fifo Status of SD/MMC registers
 * Entry
 * Exit
 * Exceptions:
 *****************************************************/
static void mmc_s3c_general_mmc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
	struct s3c_mmc_data *sd = (struct s3c_mmc_data *) dev_id;

	u16 status[SDI_STATUS_LENGTH]= {0};
	status[SDI_CMD] = rSDICSTA;
	status[SDI_DATA] = rSDIDSTA;
	status[SDI_FIFO] = rSDIFSTA;
	sd_status_clear(2);

	MMC_DEBUG(2, " rSDIIMSK 0x%08X cmd 0x%08X data 0x%08X fifo 0x%08X",rSDIIMSK,status[0],status[1],status[2]); 
	MMC_DEBUG(2, " rSDICON  0x%08X CCON  0x%08X DCONdata 0x%08X ",rSDICON,rSDICCON,rSDIDCON); 
/*
#ifdef DEBUG
	START_MMC_DEBUG(2) { 
		printk(__FUNCTION__ " cmd 0x%08X data 0x%08X fifo 0x%08X",rSDICSTA,rSDIDSTA,rSDIFSTA); 
		decode_status(status); 
		printk(")\n"); 
	} END_MMC_DEBUG;
#endif
*/	
	mmc_s3c_split_status( sd, status, 0 );
}


/*******
 * Prototype 
 *   static void mmc_s3c_irq_timeout( unsigned long nr )
 * Purpose:
 *       1. mmc_s3c_reset_timeout  (start)
 *       2. mmc_s3c_exec_command   (start)
         3. mmc_s3c_split_status   (stop)
	 DATA Transfer (read/write), Reset timeout implementation
 * Entry
 * Exit
 * Exceptions:
 *****************************************************/
static void mmc_s3c_irq_timeout( unsigned long nr )
{
	struct s3c_mmc_data *sd = (struct s3c_mmc_data *) nr;

	u16 status[SDI_STATUS_LENGTH]= {0};
	status[SDI_CMD] = rSDICSTA;
	status[SDI_DATA] = rSDIDSTA;
	status[SDI_FIFO] = rSDIFSTA;
	sd_status_clear(2);
/*
#ifdef CONFIG_MMC_DEBUG
	START_MMC_DEBUG(2) { 
		printk(__FUNCTION__ " sd=%p status=0x%04x (", sd, status);
		decode_status(status); 
		printk(")\n"); 
	} END_MMC_DEBUG;
#endif
*/

	//	g_s3c_asic_statistics.mmc_timeout++;
	mmc_s3c_split_status( sd, status, 1 );
}


static void mmc_s3c_slot_up( void )
{
	MMC_DEBUG(1,"    before slot_up");

	/* Set up timers */
	g_s3c_mmc_data.sd_detect_timer.function = mmc_s3c_fix_sd_detect;
	g_s3c_mmc_data.sd_detect_timer.data     = (unsigned long) &g_s3c_mmc_data;
	init_timer(&g_s3c_mmc_data.sd_detect_timer);

	if (lowlevel->slot_init)
		lowlevel->slot_init(lowlevel);
	    /* Important notice for MMC test condition */
	    /* Cmd & Data lines must be enabled pull up resister */

        rSDIPRE=PCLK/(2*MMC_CLOCK_SLOW)-1;	// 400KHz
	/* For SDIO use RECV_SDIO_INT_EN */
	rSDICON=FIFO_RESET |CLOCK_OUT_EN; /* BYTE Type A */
	rSDIDTIMER=0xffff;		// Set timeout count
}

static void mmc_s3c_slot_down(void) 
{
	del_timer_sync(&g_s3c_mmc_data.sd_detect_timer);
	if (lowlevel->slot_cleanup)
		lowlevel->slot_cleanup(lowlevel);

}

/*******
 * Prototype 
 *  static int  mmc_s3c_slot_is_empty (int slot)
 * Purpose:
  * Entry
 * Exit
 *        1  : slot is empty
 *        0  : occupied
 * Exceptions:
 *****************************************************/
static int  mmc_s3c_slot_is_empty (int slot)
{
	int retval = 1;
	if (lowlevel->slot_is_empty)
		retval = lowlevel->slot_is_empty(lowlevel);
	return retval;
}


static int mmc_s3c_slot_init( void )
{
	int retval;
	MMC_DEBUG(1,"");

	/* Set up timers */

	/*
	g_s3c_mmc_data.sd_detect_timer.function = mmc_s3c_fix_sd_detect;
	g_s3c_mmc_data.sd_detect_timer.data     = (unsigned long) &g_s3c_mmc_data;
	init_timer(&g_s3c_mmc_data.sd_detect_timer);
	g_s3c_mmc_data.sd_detect_timer.expires = jiffies + (250 * HZ) / 1000;
	add_timer(&g_s3c_mmc_data.sd_detect_timer);
	*/

	
/*
	g_s3c_mmc_data.reset_timer.function = mmc_s3c_reset_timeout;
	g_s3c_mmc_data.reset_timer.data     = (unsigned long) &g_s3c_mmc_data;
	init_timer(&g_s3c_mmc_data.reset_timer);
*/

/*
	g_s3c_mmc_data.irq_timer.function = mmc_s3c_irq_timeout;
	g_s3c_mmc_data.irq_timer.data     = (unsigned long) &g_s3c_mmc_data;
	init_timer(&g_s3c_mmc_data.irq_timer);
*/

	/* Basic service interrupt */
	mmc_s3c_slot_up();

	retval = request_irq( lowlevel->sdi_irq, mmc_s3c_general_mmc_interrupt,
			      SA_INTERRUPT, "mmc_s3c_int", &g_s3c_mmc_data );
	if ( retval ) {
		printk(KERN_CRIT "%s: unable to grab MMC IRQ %d\n", __FUNCTION__, lowlevel->sdi_irq);
		return retval;
	}
//	disable_irq( lowlevel->sdi_irq );

	retval = request_irq( lowlevel->detect_irq, mmc_s3c_sd_detect_isr, 
		      SA_INTERRUPT, "mmc_s3c_sd_detect", &g_s3c_mmc_data );

	if ( retval ) {
		printk(KERN_CRIT "%s: unable to grab SD_DETECT IRQ %d\n", __FUNCTION__, lowlevel->detect_irq);
		free_irq(lowlevel->sdi_irq, &g_s3c_mmc_data);
	}
	return retval;
}



static void mmc_s3c_slot_cleanup( void )
{
	MMC_DEBUG(1,"");

	mmc_s3c_slot_down();

	free_irq(lowlevel->sdi_irq, &g_s3c_mmc_data);
	free_irq(lowlevel->detect_irq, &g_s3c_mmc_data);
}


/***********************************************************/
static struct mmc_slot_driver dops = {
	owner:     THIS_MODULE,
	name:      "S3C2410 MMC",
	ocr:       0x00ff8000,           /*Valid voltage ranges */
	flags:     MMC_SDFLAG_MMC_MODE,  /* Slot driver flags */

	init:      mmc_s3c_slot_init,
	cleanup:   mmc_s3c_slot_cleanup,
	is_empty:  mmc_s3c_slot_is_empty,
	send_cmd:  mmc_s3c_send_command,
	set_clock: mmc_s3c_set_clock,
};

int s3c_mmc_register_slot(struct s3c_mmc_lowlevel *s3c_mmc_lowlevel)
{
        int retval;

       	MMC_DEBUG(0," s3c_mmc_init function start"); 	
	lowlevel = s3c_mmc_lowlevel;
	retval = mmc_register_slot_driver(&dops,1);
	if ( retval < 0 )
		printk(KERN_INFO "%s: unable to register slot", __FUNCTION__);

	return retval;
}
EXPORT_SYMBOL(s3c_mmc_register_slot);

int s3c_mmc_unregister_slot(void)
{
	int retval = 0;
	MMC_DEBUG(0,"");
	mmc_unregister_slot_driver(&dops);
	lowlevel = NULL;
	return retval;
}
EXPORT_SYMBOL(s3c_mmc_unregister_slot);

MODULE_AUTHOR("Sangwook Lee<hitchcar@sec.samsung.com>, Jamey Hicks <jamey.hicks@hp.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Samsung Asic SD/MMC driver ");
