/*
 ***********************************************************************************************
 *
 * Emulation of the AT91RM9200 Multimedia Card Interface (MCI) 
 *
 *  State: not implemented 
 *
 * Copyright 2006 Jochen Karrer. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright notice, this list of
 *       conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright notice, this list
 *       of conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY Jochen Karrer ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are those of the
 * authors and should not be interpreted as representing official policies, either expressed
 * or implied, of Jochen Karrer.
 *
 *************************************************************************************************
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "i2c.h"
#include "bus.h"
#include "signode.h"
#include "cycletimer.h"
#include "clock.h"
#include "at91_mci.h"
#include "mmcdev.h"
#include "mmcard.h"
#include "sgstring.h"

#define AT91MCI_MAGIC 0x384fa1b4

#define MCI_CR(base) 	((base) + 0x00)
#define		CR_SWRST	(1<<7)
#define		CR_PWSDIS	(1<<3)
#define		CR_PWSEN	(1<<2)
#define		CR_MCIDIS	(1<<1)
#define		CR_MCIEN	(1<<0)
#define MCI_MR(base) 	((base) + 0x04)
#define		MR_BLKLEN_MASK	(0xff<<18)
#define		MR_BLKLEN_SHIFT	(18)
#define		MR_PDCMODE	(1<<15)
#define		MR_PCDPADV	(1<<14)
#define		MR_PWSDIV_MASK	(7<<8)
#define		MR_PWSDIV_SHIFT	(8)
#define		MR_CLKDIV_MASK	(0xff)

#define MCI_DTOR(base)	((base) + 0x08)
#define		DTOR_DTOMUL_MASK	(7 << 4)
#define		DTOR_DTOMUL_SHIFT	(4)
#define		DTOR_DTOMUL_1		(0 << 4)
#define		DTOR_DTOMUL_16		(1 << 4)
#define		DTOR_DTOMUL_128		(2 << 4)
#define		DTOR_DTOMUL_256		(3 << 4)
#define		DTOR_DTOMUL_1K		(4 << 4)
#define		DTOR_DTOMUL_4K		(5 << 4)
#define		DTOR_DTOMUL_64K		(6 << 4)
#define		DTOR_DTOMUL_1M		(7 << 4)

#define		DTOR_DTOCYC_MASK	(0xf)
#define		DTOR_DTOCYC_SHIFT	(0)

#define	MCI_SDCR(base) 	((base) + 0x0c)
#define		SDCR_SDCSEL	(3<<0)
#define		   SDCSEL_SLOTA		(0)
#define		   SDCSEL_SLOTB		(1)
#define MCI_ARGR(base) 	((base) + 0x10)
#define MCI_CMDR(base)	((base) + 0x14)
#define		CMDR_TRTYP_MASK		(3<<19)
#define		CMDR_TRTYP_SHIFT	(19)
#define			TRTYP_SINGLE	(0<<19)
#define			TRTYP_MULTIPLE	(1<<19)
#define			TRTYP_STREAM	(2<<19)
#define		CMDR_TRDIR		(1<<18)
#define		     TRDIR_WRITE	(0<<18)
#define		     TRDIR_READ		(1<<18)
#define		CMDR_TRCMD_MASK		(3<<16)
#define              TRCMD_NONE		(0 << 16)
#define              TRCMD_START	(1 << 16)
#define		     TRCMD_STOP		(2 << 16)

#define		CMDR_MAXLAT		(1<<12)
#define		CMDR_CPDCMD		(1<<11)
#define		CMDR_SPCMD		(7<<8)
#define		CMDR_RSPTYP_MASK	(3<<6)
#define		CMDR_RSPTYP_SHIFT	(6)
#define		 	RSPTYP_NONE	(0)
#define			RSPTYP_48	(1<<6)
#define			RSPTYP_136	(2<<6)
#define		CMDR_CMDNB		(0x3f)
#define MCI_RSPR(base)	((base) + 0x20) /* 4 locations */
#define	MCI_RDR(base)	((base)	+ 0x30)
#define	MCI_TDR(base)	((base) + 0x34)
#define MCI_SR(base)	((base) + 0x40)
#define		SR_UNRE		(1<<31)
#define		SR_OVRE		(1<<30)
#define		SR_DTOE		(1<<22)
#define		SR_DCRCE	(1<<21)
#define		SR_RTOE		(1<<20)
#define		SR_RENDE	(1<<19)
#define		SR_RCRCE	(1<<18)
#define		SR_RDIRE	(1<<17)
#define		SR_RINDE	(1<<16)
#define		SR_TXBUFE	(1<<15)
#define		SR_RXBUFF	(1<<14)
#define		SR_SDIOIRQB	(1<<9)
#define		SR_SDIOIRQA	(1<<8)
#define		SR_ENDTX	(1<<7)
#define		SR_ENDRX	(1<<6)
#define		SR_NOTBUSY	(1<<5)
#define		SR_DTIP		(1<<4)
#define		SR_BLKE		(1<<3)
#define		SR_TXRDY	(1<<2)
#define		SR_RXRDY	(1<<1)
#define		SR_CMDRDY	(1<<0)
#define	MCI_IER(base)	((base) + 0x44)
#define	MCI_IDR(base)	((base) + 0x48)
#define MCI_IMR(base)	((base) + 0x4c)

#define MAX_CARDS_PER_BUS	(30)

typedef unsigned int uint;

typedef struct AT91Mci {
	BusDevice bdev;
	SigNode *irqNode;
	SigNode *nDmaReq;
	uint32_t cr;
	uint32_t mr;
	uint32_t dtor;
	uint32_t sdcr;
	uint32_t argr;
	uint32_t cmdr;
	uint8_t rspr[16];
	int rspr_wp;
	int rspr_rp;
	uint32_t rdr;
	uint32_t tdr;
	uint32_t sr;
	uint32_t imr;
	MMCDev *card[2][MAX_CARDS_PER_BUS];
} AT91Mci;

static void 
update_interrupt(AT91Mci *mci) 
{
	if(mci->sr & mci->imr) {
		SigNode_Set(mci->irqNode,SIG_HIGH);
	} else {
		SigNode_Set(mci->irqNode,SIG_PULLDOWN);
	}
}

static void __UNUSED__
do_cmd_delayed(void *clientData)
{
        AT91Mci *mci = (AT91Mci*) clientData;
        MMCResponse resp;
        int rsptyp = mci->cmdr  & CMDR_RSPTYP_MASK;
        int result;
	int bus = mci->sdcr & 1;
	int cmd = mci->cmdr & 0x3f;
        uint32_t arg;
        int i;
        if(!mci->card[bus][0]) {
		mci->sr |= SR_CMDRDY | SR_RTOE;
                update_interrupt(mci);
                return;
        }
        arg = mci->argr;
        //dbgprintf("Start cmd 0x%02x \n",cmd);
        result = MMCDev_DoCmd(mci->card[bus][0],cmd,arg,&resp);
        if(result != MMC_ERR_NONE) {
		mci->sr |= SR_CMDRDY | SR_RTOE;
                update_interrupt(mci);
                return;
        }
        /* Handle the response from SD/MMC card */
        if((rsptyp == RSPTYP_48)) {
                for(i=0;i<6;i++) {
			mci->rspr[i] = resp.data[i];
                }
        } else if (rsptyp == RSPTYP_136) {
                for(i=1;i<17;i++) {
			mci->rspr[i] = resp.data[i];
                }
        } else if (rsptyp == RSPTYP_NONE) {
                /* put nothing to fifo */
        } else {
                fprintf(stderr,"SDHC emulator: Illegal response type %d\n",rsptyp);
        }
	mci->sr |= SR_CMDRDY;
	/*
         * ----------------------------------------------------------
         * For the case that the transfer involves a data packet
         * trigger the data transaction
         * ----------------------------------------------------------
         */
	
	if(((mci->cmdr & CMDR_TRDIR) == TRDIR_WRITE) &&
	    (mci->cmdr & CMDR_TRCMD_MASK) == TRCMD_START) {
		//SigNode_Set(mci->nDmaReq,SIG_LOW);
	}

}

static uint32_t
cr_read(void *clientData,uint32_t address,int rqlen)
{
	fprintf(stderr,"Register 0x%08x not implemented\n",address);
        return 0;
}

static void
cr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	fprintf(stderr,"Register 0x%08x not implemented\n",address);
}
static uint32_t
mr_read(void *clientData,uint32_t address,int rqlen)
{
	fprintf(stderr,"Register 0x%08x not implemented\n",address);
        return 0;
}

static void
mr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	fprintf(stderr,"Register 0x%08x not implemented\n",address);
}

static uint32_t
dtor_read(void *clientData,uint32_t address,int rqlen)
{
	fprintf(stderr,"Register 0x%08x not implemented\n",address);
        return 0;
}

static void
dtor_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	fprintf(stderr,"Register 0x%08x not implemented\n",address);
}

static uint32_t
sdcr_read(void *clientData,uint32_t address,int rqlen)
{
	fprintf(stderr,"Register 0x%08x not implemented\n",address);
        return 0;
}

static void
sdcr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	fprintf(stderr,"Register 0x%08x not implemented\n",address);
}

static uint32_t
argr_read(void *clientData,uint32_t address,int rqlen)
{
	AT91Mci *mci = (AT91Mci *) clientData;
        return mci->argr;
}

static void
argr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	AT91Mci *mci = (AT91Mci *) clientData;
	mci->argr = value;
}
static uint32_t
cmdr_read(void *clientData,uint32_t address,int rqlen)
{
        return 0;
}

static void
cmdr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	AT91Mci *mci = (AT91Mci *) clientData;
	//int cmd = value & 0x3f;
	mci->cmdr = value;		
	
	
	fprintf(stderr,"Register 0x%08x not implemented\n",address);
}

static uint32_t
rspr_read(void *clientData,uint32_t address,int rqlen)
{
	fprintf(stderr,"Register 0x%08x not implemented\n",address);
        return 0;
}

static void
rspr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	fprintf(stderr,"Register 0x%08x not implemented\n",address);
}

static uint32_t
rdr_read(void *clientData,uint32_t address,int rqlen)
{
	fprintf(stderr,"Register 0x%08x not implemented\n",address);
        return 0;
}

static void
rdr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	fprintf(stderr,"Register 0x%08x not implemented\n",address);
}

static uint32_t
tdr_read(void *clientData,uint32_t address,int rqlen)
{
	fprintf(stderr,"Register 0x%08x not implemented\n",address);
        return 0;
}

static void
tdr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	fprintf(stderr,"Register 0x%08x not implemented\n",address);
}

static uint32_t
sr_read(void *clientData,uint32_t address,int rqlen)
{
	AT91Mci *mci = (AT91Mci *) clientData;
        return mci->sr;
}

static void
sr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	fprintf(stderr,"AT91Mci: SR is readonly\n");
}

static uint32_t
ier_read(void *clientData,uint32_t address,int rqlen)
{
	fprintf(stderr,"IER Register is writeonly\n");
        return 0;
}

static void
ier_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	AT91Mci *mci = (AT91Mci *) clientData;
	mci->imr |= value & 0xc07fc3ff;
	update_interrupt(mci);
}

static uint32_t
idr_read(void *clientData,uint32_t address,int rqlen)
{
	fprintf(stderr,"IDR Register is writeonly\n");
        return 0;
}

static void
idr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	AT91Mci *mci = (AT91Mci *) clientData;
	mci->imr &= ~(value & 0xc07fc3ff);
	update_interrupt(mci);
}
static uint32_t
imr_read(void *clientData,uint32_t address,int rqlen)
{
	AT91Mci *mci = (AT91Mci *) clientData;
        return mci->imr;
}

static void
imr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	fprintf(stderr,"IMR register is readonly\n");
}

static int
data_input(void *dev,const uint8_t *buf,int count)
{
	return 0;
}
int
AT91Mci_InsertCard(BusDevice *dev,MMCDev *card,uint bus,uint index)
{
	AT91Mci *mci = (AT91Mci *) dev->owner;
	if(dev->magic != AT91MCI_MAGIC) {
		fprintf(stderr,"Type check error: Device is not an AT91 MCI\n");
		exit(2);
	}
	if(index >  MAX_CARDS_PER_BUS) {
		fprintf(stderr,"Error: AT91MCI allows only %d cards per bus\n",index); 
		return -2;
	}
	if(bus > 1) {
		fprintf(stderr,"Error: AT91Mci has only two MMC/SD card Busses\n");
		return -1;
	}
	mci->card[bus][index] = card;
        MMCard_AddListener(card,mci,16,data_input);
	return 0;
}

static void
AT91Mci_Map(void *owner,uint32_t base,uint32_t mask,uint32_t flags)
{
        AT91Mci *mci = (AT91Mci*) owner;
	IOH_New32(MCI_CR(base),cr_read,cr_write,mci);
	IOH_New32(MCI_MR(base),mr_read,mr_write,mci);
	IOH_New32(MCI_DTOR(base),dtor_read,dtor_write,mci);
	IOH_New32(MCI_SDCR(base),sdcr_read,sdcr_write,mci);
	IOH_New32(MCI_ARGR(base),argr_read,argr_write,mci);
	IOH_New32(MCI_CMDR(base),cmdr_read,cmdr_write,mci);
	IOH_New32(MCI_RSPR(base),rspr_read,rspr_write,mci);
	IOH_New32(MCI_RDR(base),rdr_read,rdr_write,mci);
	IOH_New32(MCI_TDR(base),tdr_read,tdr_write,mci);
	IOH_New32(MCI_SR(base),sr_read,sr_write,mci);
	IOH_New32(MCI_IER(base),ier_read,ier_write,mci);	
	IOH_New32(MCI_IDR(base),idr_read,idr_write,mci);
	IOH_New32(MCI_IMR(base),imr_read,imr_write,mci);
}

static void
AT91Mci_UnMap(void *owner,uint32_t base,uint32_t mask)
{
        //IOH_Delete32(TWI_THR(base));
	IOH_Delete32(MCI_CR(base));
	IOH_Delete32(MCI_MR(base));
	IOH_Delete32(MCI_DTOR(base));
	IOH_Delete32(MCI_SDCR(base));
	IOH_Delete32(MCI_ARGR(base));
	IOH_Delete32(MCI_CMDR(base));
	IOH_Delete32(MCI_RSPR(base));
	IOH_Delete32(MCI_RDR(base));
	IOH_Delete32(MCI_TDR(base));
	IOH_Delete32(MCI_SR(base));
	IOH_Delete32(MCI_IER(base));
	IOH_Delete32(MCI_IDR(base));
	IOH_Delete32(MCI_IMR(base));

}
BusDevice *
AT91Mci_New(const char *name)
{
        AT91Mci *mci = sg_new(AT91Mci);
	mci->irqNode = SigNode_New("%s.irq",name);
	mci->nDmaReq = SigNode_New("%s.dma_req",name);
	if(!mci->irqNode || !mci->nDmaReq) {
		fprintf(stderr,"Can not create signal lines for AT91Mci\n");
		exit(1);
	}
	mci->bdev.first_mapping=NULL;
        mci->bdev.Map=AT91Mci_Map;
        mci->bdev.UnMap=AT91Mci_UnMap;
        mci->bdev.owner=mci;
        mci->bdev.hw_flags=MEM_FLAG_WRITABLE|MEM_FLAG_READABLE;
	mci->bdev.magic = AT91MCI_MAGIC; 
        update_interrupt(mci);
        fprintf(stderr,"AT91RM9200 MCI \"%s\" created\n",name);
        return &mci->bdev;
}

