Working SDHC with improved write speed

CodeKiller
Neugieriger
Neugieriger
Beiträge: 3
Registriert: Donnerstag 29. Dezember 2011, 12:49

Working SDHC with improved write speed

Beitrag von CodeKiller »

Hi to all,

I've done a lot of effort to make my DBox2 handle big cards, and to record a radio channel with 256kbps.
I have my issues with the building, so every time i made some changes, i had to build clean, and reflash an image to the box. (i would like to have a full tutorial to build a yadd, and setup the dboxII bootmanager)
So after a few dozen of trial and error finally some working code have been made. I'm not really interested in further maintaining the code (like active cvs updates once in a while) but i want to share what have i done already.

Some info and history about the process: i've started dev-ing for recording the SSL mixmission over a year ago, but abandoned due to lack of patience and big enough card. Now, I revisited again, and now as i have a 32GB card, it seemed more justified.
The code based on the mmc2.c , due to optimized read, but later further optimized for writing based on mmccombo.c thus rearranged the pins to use with.
- Now only NOKIA boxes can be used for this, sorry! And check for the modified pins, not the same anymore -
The module is capable of initializating (int theory) all MMC and SC cards in SPI mode, but only legacy (<2GB) MMC cards, and SD cards with 512bytes/block (all SDHC/SDXC(?), and legacy cards except 2GB ones).
At first (default mmc2 i/o) the write from ftp was ~16kBytes/s, after the mmccombo's write code and pin arrange nothing really changed, still ~17kB/s. The initial i/o was 512byte block read/write, but now uses SET_WR_BLK_ERASE_COUNT prior to WRITE_MULTIPLE_BLOCK which is really improved the writing speed.
This is a really major point, as before writing, the card issues an erase of an AU (accessible unit), and then copy AU_SIZE-512byte to somewhere, and after that can the write fill that 512 bytes with own datas. Now the cards gets how many blocks waiting to be written, then sending them by WRITE_MULTIPLE_BLOCK requireing less erase and less copy by the card's internal management.

I have a plan for further upgrade: using hardware parallel->serial and serial->parallel circuits, and a 1/3 clock divider (66/3=22MHz) and some transaction management --- buuut i may let others to continue in this path :)

So the source is here for everyone: ( not sure if the wmb() is needed )

Code: Alles auswählen

//  $Id: mmc2.c,v 2.0 2011/12/27 13:42:05 satsuse Exp $
//
//  MMC2.c
//  General MMC device driver for dbox Modem Connector
//---//  Modem connector pins PA9,PA8,PB16,PB17 used -> MMC2 connection sheme
//  Modem connector pins PA9,PA8,PB16,PA7 used -> MMC2 connection sheme
//
//  This version of MMC is used to test various optimisation variants only
//
//  14 Aug 2006
//                                           ---- Modem_CN ---
//                                           SAGEM NOKIA  PHIL
// PA9  = SD_DO = 0x0040 = SD_Card Pin 7   =   2    12     11
// PA8  = SD_DI = 0x0080 = SD_Card Pin 2   =   1    11      9
// PA7  = SD_CLK= 0x0100 = SD_Card Pin 5   =   ?    14      ?
///----------------------------// PB17 = SD_CLK= 0x4000 = SD_Card Pin 5   =  10     2      7
// PB16 = SD_CS = 0x8000 = SD_Card Pin 1   =   6     6      5
// GND  =       = Masse  = SD_Card Pin 3,6 =   3    10      2
// VCC  =       = 3,3V   = SD_Card Pin 4   =   5    16      x
// (x philips: connect in series 3 Diodes (1N4007) from MODEM_CN 1 to SD/MMC card pin 4)
// Also connect a pullup resistor 100 KOhm from SD/MMC card pin 7 (SD_DO) to SD/MMC card pin 4 (VCC)
// 
// written by Madsuk/Rohde/TaGana
// optimized by just_me/Guenther/DboxBar
// 
// (partial) big card (2gb+ MMC / SDHC / SDXC) support by CodeKiller
//
//
// TODO: more write speed by ACMD23 (pre erase multiblocks) and CMD25 (write multiblocks)
//                    mutiblock headers: block header - 0xfc    end of datas - 0xfd
#include <linux/delay.h>
#include <linux/timer.h>

#include <linux/kernel.h>

#include <linux/module.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/blkpg.h>
#include <linux/hdreg.h>
#include <linux/major.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/8xx_immap.h>

#define DEVICE_NAME "mmc"
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#define MAJOR_NR 121

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
extern void m8xx_wdt_reset(void);
#endif

#include <linux/blk.h>

MODULE_AUTHOR("Madsuk/Rohde/TaGana/CK");
MODULE_DESCRIPTION("Driver MMC/SD-Cards");
MODULE_SUPPORTED_DEVICE("all dbox2 on com2 connector");
MODULE_LICENSE("GPL");

//#define CHECK_MEDIA_CHANGE  // for developement ONLY, not working yet

#define SD_DO  0x0040 // on SD/MMC card pin 7
#define SD_DI  0x0080 // on SD/MMC card pin 2
#define SD_CLK 0x0100 // on SD/MMC card pin 5
#define SD_CS  0x8000 // on SD/MMC card pin 1

volatile immap_t *immap=(immap_t *)IMAP_ADDR ;

/* we have only one device */
static int hd_sizes[1<<6];   //1<<6=SILLY!!    EQUAL 64!    but why??
static int hd_blocksizes[1<<6];
static int hd_hardsectsizes[1<<6];
static int hd_maxsect[1<<6];
static struct hd_struct hd[1<<6];

static struct timer_list mmc_timer;
static int mmc_media_detect = 0;
static int mmc_media_changed = 1;

volatile int mmc_type=1;        //4=MMC 2GB+; 3=SDHC/SDXC; 2=SDSCv2.0; 1=SDSC; 0=MMC

extern struct gendisk hd_gendisk;

/////////////////////
// prototypes
static int mmc_open(struct inode *inode, struct file *filp);
static int mmc_release(struct inode *inode, struct file *filp);
static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
static void mmc_request(request_queue_t *q);


static struct block_device_operations mmc_bdops =
{
	open: mmc_open,
	release: mmc_release,
	ioctl: mmc_ioctl,
#ifdef CHECK_MEDIA_CHANGE
	check_media_change: mmc_check_media_change,
	revalidate: mmc_revalidate,
#endif
};

static struct gendisk hd_gendisk = {
	major:		MAJOR_NR,
	major_name:	DEVICE_NAME,
	minor_shift:	6,
	max_p:		1 << 6,
	part:		hd,
	sizes:		hd_sizes,
	fops:		&mmc_bdops,
};

///////////////////////////////////////////////////////////////////////////////////////////////
/////// Low-Level Driver
///////////////////////////////////////////////////////////////////////////////////////////////

static int mmc_hardware_init(void) {
   volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
   volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;

   printk("mmc2: Hardware init\n");
   cp->cp_pbpar &=   ~(SD_CS);
   cp->cp_pbodr &=   ~(SD_CS);
   cp->cp_pbdir |=    (SD_CS);
   cpi->iop_papar &= ~(SD_DO | SD_DI | SD_CLK);
   cpi->iop_paodr &= ~(SD_DO | SD_CLK);
   cpi->iop_padir |=   (SD_DI | SD_CLK);
   cpi->iop_padir &=  ~SD_DO;

    // Clock + CS low
   cpi->iop_padat &= ~SD_CLK;
   cp->cp_pbdat &= ~SD_CS;
   cpi->iop_padat &= ~SD_DI;
   return 0;
}

static void mmc_spi_cs_low(void) {
  volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
  cp->cp_pbdat &= ~(SD_CS);
}

static void mmc_spi_cs_high(void) {
  volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
  cp->cp_pbdat |= SD_CS;
}

static unsigned char mmc_spi_io(unsigned char data_out) {
  //volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
  volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;
  unsigned char result = 0;
  unsigned char  i;

  for(i = 0x80; i != 0; i >>= 1) {
    if (data_out & i)
      cpi->iop_padat |= SD_DI;
    else
      cpi->iop_padat &= ~SD_DI;

    // latch data on SD_DI into card
    cpi->iop_padat |= SD_CLK;
    // shouldn't data be read after clk h->l?
    if (cpi->iop_padat & SD_DO) {
    	result |= i;
    }
    cpi->iop_padat &= ~SD_CLK;
  }

  return result;
}

//////////////////////////////////
// optimized read/write functions

inline void mmc_spi_write(unsigned char data_out) {
   //volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
   volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;
   //unsigned long clk_high = cp->cp_pbdat | SD_CLK;
   //unsigned long clk_low =  cp->cp_pbdat & ~SD_CLK;

   //unsigned long data_high = cpi->iop_padat | SD_DI;
   //unsigned long data_low =  cpi->iop_padat & ~SD_DI;


    unsigned int cldl=cpi->iop_padat & ~(SD_CLK | SD_DI);
	unsigned int cldh=(cpi->iop_padat | SD_DI) & ~SD_CLK;	
	unsigned int chdl=(cpi->iop_padat | SD_CLK) & ~SD_DI;	
	unsigned int chdh=cpi->iop_padat | SD_DI | SD_CLK; 	

    
        // bit 7
		if (data_out & 0x80) {
			wmb();
			cpi->iop_padat = cldh;	// set data-high
			wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			wmb();
			cpi->iop_padat = cldl;	// set data-low
			wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 6
		if (data_out & 0x40) {
			wmb();
			cpi->iop_padat = cldh;	// set data-high
			wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			wmb();
			cpi->iop_padat = cldl;	// set data-low
			wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 5
		if (data_out & 0x20) {
			wmb();
			cpi->iop_padat = cldh;	// set data-high
			wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			wmb();
			cpi->iop_padat = cldl;	// set data-low
			wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 4
		if (data_out & 0x10) {
			wmb();
			cpi->iop_padat = cldh;	// set data-high
			wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			wmb();
			cpi->iop_padat = cldl;	// set data-low
			wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 3
		if (data_out & 0x08) {
			wmb();
			cpi->iop_padat = cldh;	// set data-high
			wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			wmb();
			cpi->iop_padat = cldl;	// set data-low
			wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 2
		if (data_out & 0x04) {
			wmb();
			cpi->iop_padat = cldh;	// set data-high
			wmb();
	 		cpi->iop_padat = chdh;	// latch
		}     
		else {
			wmb();
			cpi->iop_padat = cldl;	// set data-low
			wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 1
		if (data_out & 0x02) {
			wmb();
			cpi->iop_padat = cldh;	// set data-high
			wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			wmb();
			cpi->iop_padat = cldl;	// set data-low
			wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 0
		if (data_out & 0x01) {
            wmb();
			cpi->iop_padat = cldh;	// set data-high
			wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			wmb();
			cpi->iop_padat = cldl;	// set data-low
			wmb();
			cpi->iop_padat = chdl;	// latch
        }
    
        cpi->iop_padat = cldh;

}

inline unsigned char mmc_spi_read(void)
{
   unsigned char result = 0;

   //volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
   volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;

   unsigned long clk_high = cpi->iop_padat | SD_CLK | SD_DI;
   unsigned long clk_low =  (cpi->iop_padat | SD_DI ) & ~SD_CLK;

   cpi->iop_padat |= SD_DI;

   cpi->iop_padat = clk_high; // Bit 0 (MSB)
   result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
   cpi->iop_padat = clk_low;
   cpi->iop_padat = clk_high; // Bit 1
   result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
   cpi->iop_padat = clk_low;
   cpi->iop_padat = clk_high; // Bit 2
   result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
   cpi->iop_padat = clk_low;
   cpi->iop_padat = clk_high; // Bit 3 
   result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
   cpi->iop_padat = clk_low;
   cpi->iop_padat = clk_high; // Bit 4
   result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
   cpi->iop_padat = clk_low;
   cpi->iop_padat = clk_high; // Bit 5
   result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
   cpi->iop_padat = clk_low;
   cpi->iop_padat = clk_high; // Bit 6
   result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
   cpi->iop_padat = clk_low;
   cpi->iop_padat = clk_high; // Bit 7
   result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
   cpi->iop_padat = clk_low;

   return result;
}

////////////////////////////////////////
// burst transfer quite more optimized (approx 10% faster than above)

void mmc_read_block_low_level(unsigned char *dest)
{
   //volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
   volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;

   unsigned long clk_high = cpi->iop_padat | SD_CLK | SD_DI;   // load clock port into register ram and prefefine with pin low/high,to avoid reload on any clock trigger, this speeds ups the transmission significantly
   unsigned long clk_low = (cpi->iop_padat | SD_DI ) & ~SD_CLK;

   cpi->iop_padat |= SD_DI; // for read access we send high bytes only

   unsigned char result=0;
   unsigned int i=0;

   for (i = 0; i < 512; i++)
   {
      cpi->iop_padat = clk_high; // Bit 0
      result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
      cpi->iop_padat = clk_low;
      cpi->iop_padat = clk_high; // Bit 1
      result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
      cpi->iop_padat = clk_low;
      cpi->iop_padat = clk_high; // Bit 2
      result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
      cpi->iop_padat = clk_low;
      cpi->iop_padat = clk_high; // Bit 3
      result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
      cpi->iop_padat = clk_low;
      cpi->iop_padat = clk_high; // Bit 4
      result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
      cpi->iop_padat = clk_low;
      cpi->iop_padat = clk_high; // Bit 5
      result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
      cpi->iop_padat = clk_low;
      cpi->iop_padat = clk_high; // Bit 6
      result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
      cpi->iop_padat = clk_low;
      cpi->iop_padat = clk_high; // Bit 7
      result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
      cpi->iop_padat = clk_low;

      *dest++ = result;       
      result = 0;
   }
}




void mmc_write_block_low_level(unsigned char *dest)
{
   //volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
   volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;

   //unsigned long clk_high = cp->cp_padat | SD_CLK;   // load clock port into register ram and prefefine with pin low/high,to avoid reload on any clock trigger, this speeds ups the transmission significantly
   //unsigned long clk_low =  cp->cp_padat & ~SD_CLK;

   //unsigned long data_high = cpi->iop_padat | SD_DI;
   //unsigned long data_low =  cpi->iop_padat & ~SD_DI;
	unsigned int cldl=cpi->iop_padat & ~(SD_CLK | SD_DI);
	unsigned int cldh=(cpi->iop_padat | SD_DI) & ~SD_CLK;	
	unsigned int chdl=(cpi->iop_padat | SD_CLK) & ~SD_DI;	
	unsigned int chdh=cpi->iop_padat | SD_DI | SD_CLK; 	
   
   //cpi->iop_padat |= SD_DI; // for read access we send high bytes only

   unsigned char data = *dest++;
   unsigned int i=0;

   for (i = 0; i < 512; i++)
   {
   
        // bit 7
		if (data & 0x80) {
			wmb();
			cpi->iop_padat = cldh;	// set data-high
			wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			wmb();
			cpi->iop_padat = cldl;	// set data-low
			wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 6
		if (data & 0x40) {
			wmb();
			cpi->iop_padat = cldh;	// set data-high
			wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			wmb();
			cpi->iop_padat = cldl;	// set data-low
			wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 5
		if (data & 0x20) {
			wmb();
			cpi->iop_padat = cldh;	// set data-high
			wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			wmb();
			cpi->iop_padat = cldl;	// set data-low
			wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 4
		if (data & 0x10) {
			wmb();
			cpi->iop_padat = cldh;	// set data-high
			wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			wmb();
			cpi->iop_padat = cldl;	// set data-low
			wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 3
		if (data & 0x08) {
			wmb();
			cpi->iop_padat = cldh;	// set data-high
			wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			wmb();
			cpi->iop_padat = cldl;	// set data-low
			wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 2
		if (data & 0x04) {
			wmb();
			cpi->iop_padat = cldh;	// set data-high
			wmb();
	 		cpi->iop_padat = chdh;	// latch
		}     
		else {
			wmb();
			cpi->iop_padat = cldl;	// set data-low
			wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 1
		if (data & 0x02) {
			wmb();
			cpi->iop_padat = cldh;	// set data-high
			wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			wmb();
			cpi->iop_padat = cldl;	// set data-low
			wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 0
		if (data & 0x01) {
			wmb();
			cpi->iop_padat = cldh;	// set data-high
			wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			wmb();
			cpi->iop_padat = cldl;	// set data-low
			wmb();
			cpi->iop_padat = chdl;	// latch
        }
   
   
      data = *dest++;
   }
   
   cpi->iop_padat = cldh;
}

///////////////////////////////////////////////////////////////////////////////////////////////
/////// High-Level Driver
///////////////////////////////////////////////////////////////////////////////////////////////

static int mmc_write_block(unsigned int dest_addr, unsigned char *data)
{
	unsigned char r = 0;
	int i;

	mmc_spi_cs_low();
    //for (i = 0; i < 4; i++) mmc_spi_read();
    mmc_spi_write(0x58);        //cmd24 (WRITE_BLOCK)
    mmc_spi_write(0xff & (dest_addr >> 24)); /* msb */
    mmc_spi_write(0xff & (dest_addr >> 16));
    mmc_spi_write(0xff & (dest_addr >> 8));
    mmc_spi_write(0xff & dest_addr); /* lsb */
    mmc_spi_write(0xff);
	for (i = 0; i < 8; i++)
	{
       r = mmc_spi_read();
		if (r == 0x00) break;
	}
	if (r != 0x00)
	{
		mmc_spi_cs_high();
        mmc_spi_read();
		return(1);
	}

    mmc_spi_write(0xfe);

    mmc_write_block_low_level(data);
    //for (i = 0; i < 512; i++) mmc_spi_write(data[i]);
    //for (i = 0; i < 2; i++)   mmc_spi_write(0xff);   // send CRC for nothing
    mmc_spi_write(0xff);    //fake CRC
    mmc_spi_write(0xff);
    
	for (i = 0; i < 1000000; i++)
	{
       r = mmc_spi_read();
		if (r == 0xff) break;
	}
	if (r != 0xff)
	{
		mmc_spi_cs_high();
        mmc_spi_read();
		return(3);
	}
	mmc_spi_cs_high();
    mmc_spi_read();
	return(0);
}

static int mmc_write_multiblock(unsigned int dest_addr, unsigned char *data, unsigned int nr_blocks)
{
	unsigned char r = 0;
	int i;

	mmc_spi_cs_low();
    //for (i = 0; i < 4; i++) mmc_spi_read();
    mmc_spi_write(0x77);        //cmd55 (app_cmd)
    mmc_spi_write(0);
    mmc_spi_write(0);
    mmc_spi_write(0);
    mmc_spi_write(0);
    mmc_spi_write(0xff);
    
	i=255;
	do
	{
        r = mmc_spi_read();
	}while((i--)&&(r&0x80));
	if (r != 0x00)
	{
		mmc_spi_cs_high();
        mmc_spi_read();
		return(4);
	}
	mmc_spi_cs_high();
    
    mmc_spi_cs_low();

    mmc_spi_write(0x57);        //acmd23 (SET_WR_BLK_ERASE_COUNT)
    mmc_spi_write(0xff & (nr_blocks >> 24)); /* msb */
    mmc_spi_write(0xff & (nr_blocks >> 16));
    mmc_spi_write(0xff & (nr_blocks >> 8));
    mmc_spi_write(0xff & nr_blocks); /* lsb */
    mmc_spi_write(0xff);
    
	i=255;
	do
	{
        r = mmc_spi_read();
	}while((i--)&&(r&0x80));
	if (r != 0x00)
	{
		mmc_spi_cs_high();
        mmc_spi_read();
		return(5);
	}
	mmc_spi_cs_high();
    
    mmc_spi_cs_low();
        
    
    mmc_spi_write(0x59);        //cmd25 (WRITE_MULTIPLE_BLOCK)
    mmc_spi_write(0xff & (dest_addr >> 24)); /* msb */
    mmc_spi_write(0xff & (dest_addr >> 16));
    mmc_spi_write(0xff & (dest_addr >> 8));
    mmc_spi_write(0xff & dest_addr); /* lsb */
    mmc_spi_write(0xff);
	for (i = 0; i < 8; i++)
	{
       r = mmc_spi_read();
	   if (r == 0x00) break;
	}
	if (r != 0x00)
	{
		mmc_spi_cs_high();
        mmc_spi_read();
		return(1);
	}

    while (nr_blocks)
    {
        mmc_spi_write(0xfc);

        mmc_write_block_low_level(data);
        //for (i = 0; i < 512; i++) mmc_spi_write(data[i]);
        //for (i = 0; i < 2; i++)   mmc_spi_write(0xff);   // send CRC for nothing
        mmc_spi_write(0xff);    //fake CRC
        mmc_spi_write(0xff);
    
        for (i = 0; i < 1000000; i++)
        {
            r = mmc_spi_read();
            if (r == 0xff) break;
        }
        if (r != 0xff)
        {
            mmc_spi_cs_high();
            mmc_spi_read();
            return(3);
        }
        nr_blocks--;
        data+=512;
    }
    mmc_spi_write(0xfd);
    
    for (i = 0; i < 1000000; i++)
    {
        r = mmc_spi_read();
        if (r == 0xff) break;
    }
    if (r != 0xff)
    {
        mmc_spi_cs_high();
        mmc_spi_read();
        return(6);
    }
    
	mmc_spi_cs_high();
    mmc_spi_read();
	return(0);
}


static int mmc_read_block(unsigned char *data, unsigned int src_addr)
{
	unsigned char r = 0;
	int i;

	mmc_spi_cs_low();
    //for (i = 0; i < 4; i++) mmc_spi_read();
    mmc_spi_write(0x51);        //cmd17 (READ_SINGLE_BLOCK)
    mmc_spi_write(0xff & (src_addr >> 24)); /* msb */
    mmc_spi_write(0xff & (src_addr >> 16));
    mmc_spi_write(0xff & (src_addr >> 8));
    mmc_spi_write(0xff & src_addr); /* lsb */

    mmc_spi_read();
	for (i = 0; i < 8; i++)
	{
       r = mmc_spi_read();
		if (r == 0x00) break;
	}
	if (r != 0x00)
	{
		mmc_spi_cs_high();
        mmc_spi_read();
		return(1);
	}
	for (i = 0; i < 100000; i++)
	{
        r = mmc_spi_read();
		if (r == 0xfe) break;
	}
	if (r != 0xfe)
	{
		mmc_spi_cs_high();
        mmc_spi_read();
		return(2);
	}
    //for (i = 0; i < 512; i++) data[i] = mmc_spi_io(0xff);
    mmc_read_block_low_level(data);

    mmc_spi_read(); // just read the CRC for nothing
    mmc_spi_read();

	mmc_spi_cs_high();
    mmc_spi_read();

	return(0);
}

static int mmc_card_init(void)
{
	unsigned char r = 0;
	unsigned int i, j;
	unsigned long flags;
	unsigned char r7vca;
	unsigned char r7echo;

	save_flags(flags);
	cli();

	
	init:
	
	printk("mmc: Card init\n");
	mmc_spi_cs_high();
	for (i = 0; i < 100; i++) mmc_spi_io(0xff);		///need at least 74clk -- now this is 800clk

	unsigned short cntr=0;
	
    do{
		mmc_spi_cs_low();       //ATN
		
		mmc_spi_io(0x40);       //cmd0
		for (i = 0; i < 4; i++) mmc_spi_io(0x00);   //arg[4]
		mmc_spi_io(0x95);       //crc7+1 -- need to be good
		for (i = 0; i < 0xff; i++)
		{
			r = mmc_spi_io(0xff);
			if (r != 0xff) break;
		}	
		cntr++;
		
		mmc_spi_io(0xff);       //blank msg
		mmc_spi_cs_high();
	
	} while((r != 0x01)&&(cntr<0x40));   //continue if r1 response=idle or timeout
	
	if (r != 0x01)          //if not idle, return ///actually idle means initializing... (stupid, isn't it?)
	{
		restore_flags(flags);
        printk("mmc: Card init -cmd0 not respond- %d\n",r);
		return(1);
	}

    printk("mmc: Card init *OK1* (cmd0 response)\n");
    mmc_type=2;
    //ok1
    //cntr=0;

    //do
	//{
        //cntr++;
		mmc_spi_cs_low();

        //--cmd8
        mmc_spi_io(0x48);   //cmd8 --- check/init SDHC/SDXC
        //--args
        mmc_spi_io(0x00);
        mmc_spi_io(0x00);
        mmc_spi_io(0x01);
        mmc_spi_io(0xaa);   //check pattern (10101010)
        //--crc
        mmc_spi_io(0x87);   //need to be good        

		i=0;
		do
		{
			r = mmc_spi_io(0xff);
			i++;
		}while((i<8)&&(r==0xff));
        
        
		r7vca=mmc_spi_io(0xff);
		r7vca=mmc_spi_io(0xff);
		r7vca=mmc_spi_io(0xff);
		r7echo=mmc_spi_io(0xff);    //if there is a valid answer, this should be 0xaa or the communication is corrupted
		
        //if (cntr>10)				//only 10 retry...(should be enough)
        //{
        //    restore_flags(flags);
        //    printk("mmc: Card init -(cmd8) error-\n");
        //    return(2);
        //}
        
        mmc_spi_cs_high();      
        mmc_spi_io(0xff);       //blank msg

    //}while ( ((r&0x04)!=0x04) && ((r!=0x01)||(r7echo!=0xaa)) );      //exit if illegal cmd (SDSCv1.0 or MMC)  or  valid response
   
    if (r7echo==0xaa)
    {
		printk("mmc: SD Card v2 -- cmd8 response ok\n");
        //check voltage support -- cmd8 response vca part
        if (!(r7vca&1))
        {
            restore_flags(flags);
            printk("mmc: SD Card voltage not supported\n");
            return(2);
        }
    }
	else if((r&0x04)==0x04)
	{
		printk("mmc: legacy sd or mmc card -- cmd8 response illegal cmd\n");
		mmc_type=1;		//mmc or legacy sd card
	}

    //check voltage support for sd/2gb+ support for mmc cards -- cmd58
	//cntr=0;
    
    unsigned char o1;
    unsigned char o2;
    unsigned char o3;
    unsigned char o4;

	//do
	//{
		mmc_spi_cs_low(); //ATN
            
		mmc_spi_io(0x7a);       //cmd58
		mmc_spi_io(0x40);       //30:29=10b         //mmc 2gb+ init
		for (i = 0; i < 3; i++) mmc_spi_io(0x00);   //args
		mmc_spi_io(0xff);       //fake crc (need no more -- disabled by default for spi except cmd0 and cmd8)
        //printk("mmc: send cmd58 resp:\n");
		i=0;
		do
		{
			r = mmc_spi_io(0xff);
            //printk("%d.=%d\t",i,r);
			i++;
		}while((i<255)&&(r!=0x00));
 
		if (r == 255)
		{
			printk("mmc: (58) Card not ready / not respond -- %d\n",r);
			//break;
		};
		//read OCR register
		//cntr++;
        o1=mmc_spi_io(0xff);
        o2=mmc_spi_io(0xff);
        o3=mmc_spi_io(0xff);
        o4=mmc_spi_io(0xff);
        //if ((i==8) && (r==0xff))
        //{
        //    restore_flags(flags);
        //    printk("mmc: Cmd58 timeout\n");
        //    return(2);
        //}
		
		mmc_spi_cs_high();
        mmc_spi_io(0xff);
		
    //} while ( (r&0x80==0x80)&& !((r==0x00)||((r&0x04)==0x04)) );          //while busy -- exit if valid response and (ready or invalid cmd)
	if (r!=0xff)
	{
        printk("mmc: i=%d ## o1=%d o2=%d o3=%d o4=%d\n",i,o1,o2,o3,o4);
		if ((o1&80) &&(o1&40) && (mmc_type==2))
		{
			mmc_type=3;
		};
		
	};
    
    if (r&0x04)
	{
		mmc_type=1;
	};
	
    m8xx_wdt_reset();
    
        mmc_spi_io(0xff);       //blank msg
        //mmc_spi_io(0xff);       //blank msg
        //mmc_spi_io(0xff);       //blank msg
        
	printk("mmc: init mmc_type %d \n",mmc_type);
    printk("mmc: start sd init -acmd41-\n");
    for (j = 0; j < 100; j++)
    {
		//printk("mmc: %d- cmd55 \n",j);
        
		mmc_spi_cs_low();	//ATN
        //mmc_spi_io(0xff);
     
		mmc_spi_io(0x77);
        for (i = 0; i < 4; i++) mmc_spi_io(0x00);
        mmc_spi_io(0xff);
		
		i=255;
		do
		{
			r = mmc_spi_io(0xff);
		}while((i--)&&(r&0x80));

		if (r == 0)
		{
			printk("mmc: (55) Card init *OK2* (SD card)\n");
			break;
		};
			
		if ((r&0x84)==0x04)
		{
			printk("mmc: (55) a/cmd41 unknown -- switching to mmc mode init\n");
			mmc_type=0;
			break;
		}
		else
		{
			//printk("mmc: %d : unk55 response - %d \n",j,r);
		}
	
        //mmc_spi_io(0xff);
        //mmc_spi_io(0xff);
        //mmc_spi_io(0xff);
		
        mmc_spi_cs_high();
        mmc_spi_io(0xff);

		//printk("mmc: %d- acmd41 \n",j);
		
        mmc_spi_cs_low();	//ATN
        //mmc_spi_io(0xff);
			
        mmc_spi_io(0x69);
        //--args
        mmc_spi_io(0x40);	//high capacity card support
        mmc_spi_io(0x00);
        mmc_spi_io(0x00);
        mmc_spi_io(0x00);		
		//--fake_crc
        mmc_spi_io(0xff);
		
		i=200;
		do
		{
			r = mmc_spi_io(0xff);
		}while((i--)&&(r&0x80));
		
		if (r == 0)
		{
			printk("mmc: Card init *OK2* (SD card)\n");
			break;
		};
			
		if ((r&0x84)==0x04)
		{
			printk("mmc: a/cmd41 unknown -- switching to mmc mode init\n");
			mmc_type=0;
			break;
		}
		else
		{
			printk("mmc: %d : unk41 response - %d \n",j,r);
			//if ((j>10)&&(r==0xff)) goto init;
			if ((j>98)&&(r==1)) goto init;
		}
		
        //mmc_spi_io(0xff);
        //mmc_spi_io(0xff);

        mmc_spi_cs_high();
        mmc_spi_io(0xff);
		if(j%10==0)m8xx_wdt_reset();

        //mmc_spi_io(0xff);
        //mmc_spi_io(0xff);
        //mmc_spi_io(0xff);
	};
	
    mmc_spi_cs_high();
    mmc_spi_io(0xff);

       
    if (mmc_type==0)
    {
		printk("mmc: legacy/mmc init -cmd1-\n");
        for (j = 0; j < 100; j++)
        {
			printk("mmc: %d- cmd1 \n",j);
            mmc_spi_cs_low();
            
			//mmc_spi_io(0xff);
            
			mmc_spi_io(0x41);
            for (i = 0; i < 4; i++) mmc_spi_io(0x00);
            mmc_spi_io(0xff);
			i=0;
			do
			{
				r = mmc_spi_io(0xff);
				i++;
			}while((i<100)&&(r==0xff));
			
			if (r==0)
			{
				printk("mmc: Card init *OK2* (SDSCv1 or MMC)\n");
				break;
            }
            mmc_spi_cs_high();
            mmc_spi_io(0xff);
			if(j%10==0)m8xx_wdt_reset();
        }
        mmc_spi_cs_high();
        mmc_spi_io(0xff);
	}; 
    
	//cntr=0;
    if(mmc_type>1)
    {
        printk("mmc: Cmd58 send\n");
		mmc_spi_cs_low(); //ATN
            
		mmc_spi_io(0x7a);       //cmd58
		mmc_spi_io(0x40);       //30:29=10b         //mmc 2gb+ init
		for (i = 0; i < 3; i++) mmc_spi_io(0x00);   //args
		mmc_spi_io(0xff);       //fake crc (need no more -- disabled by default for spi except cmd0 and cmd8)
        //printk("mmc: send cmd58 resp:\n");
		i=0;
		do
		{
			r = mmc_spi_io(0xff);
            //printk("%d.=%d\t",i,r);
			i++;
		}while((i<80)&&(r!=0));

 
		//read OCR register
	//	cntr++;
        o1=mmc_spi_io(0xff);
        o2=mmc_spi_io(0xff);
        o3=mmc_spi_io(0xff);
        o4=mmc_spi_io(0xff);

        mmc_spi_io(0xff);
        
		mmc_spi_cs_high();
        mmc_spi_io(0xff);

        if (i>=8)
        {
            restore_flags(flags);
            printk("mmc: Cmd58 timeout\n");
            return(2);
        }
		
		
        //} while ( (r&0x80==0x80)&& !((r==0x00)||((r&0x04)==0x04)) );          //while busy -- exit if valid response and (ready or invalid cmd)
        printk("mmc: i=%d ## o1=%d o2=%d o3=%d o4=%d\n",i,o1,o2,o3,o4);
        if (r!=0xff)
        {
            if ((o1&80) && (o1&40) && (mmc_type==2))
            {
                mmc_type=3;
            }
		
        }
    }
    printk("mmc: init memory_card_type=%d\n",mmc_type);
    
	restore_flags(flags);
    return(0);
}

static int mmc_card_config(void)
{
	unsigned char r = 0;
	short i;//,j;
	unsigned char csd[32];
	unsigned int c_size;
	unsigned int c_size_mult;
	unsigned int mult;
	unsigned int read_bl_len;
	unsigned int blocknr = 0;
	unsigned int block_len = 0;
	unsigned int size = 0;

    
    //j=100;
  	//do
	//{
        mmc_spi_cs_high();
        
        //printk("mmc: send cmd9 (SEND_CSD) for %d times - %d\n",101-j,r);
        mmc_spi_io(0xff);
        mmc_spi_io(0xff);
        mmc_spi_io(0xff);
        
        mmc_spi_cs_low();
        //for (i = 0; i < 4; i++) mmc_spi_io(0xff);	//pre_junk (???)
        mmc_spi_io(0x49);       //cmd9 (SEND_CSD) -- only enough for sd cards
        mmc_spi_io(0x00);	//args
        mmc_spi_io(0x00);	//args
        mmc_spi_io(0x00);	//args
        mmc_spi_io(0x00);	//args
        mmc_spi_io(0xaf);							//fake crc

        //printk("mmc: send cmd9 resp:\n");
        for (i = 0; i < 8; i++) 
        {
            r = mmc_spi_io(0xff);					//wait for R1_READY response
            //printk("%d.=%d\t",i,r);
            if (r==0) break;
        }
        //j--;
	//}while((j) && (r!=0));
    
	if (r != 0x00)								//if not, error
	{
		mmc_spi_cs_high();
		mmc_spi_io(0xff);
        printk("mmc: config cmd9 response timeout %d\n",r);
		return(1);
	}
    printk("mmc: valid CSD R1 response: %d\n",r);
	for (i = 0; i < 8; i++)
	{
		r = mmc_spi_io(0xff);					//wait for startbit_token (%1111 1110)
            //printk("%d.=%d\t",i,r);
		if (r == 0xfe) break;
	}
	if (r != 0xfe)								//error
	{
		mmc_spi_cs_high();
		mmc_spi_io(0xff);
        printk("mmc: config data response timeout %d\n",r);
		return(2);
	}
    //csd[0]=r;
	for (i = 0; i < 16; i++)
	{
		r = mmc_spi_io(0xff);
		csd[i] = r;
	}
	for (i = 0; i < 2; i++)     //crc -- not used
	{
		r = mmc_spi_io(0xff);
	}
	mmc_spi_cs_high();
	mmc_spi_io(0xff);
	//if (r == 0x00) 
    //{
        
    //    return(3);				//bad response(?)
    //}
    read_bl_len=0;
    c_size_mult=0;
    printk("mmc: mmctype=%d     csd[0]=%d\n",mmc_type,csd[0]);
	if (((mmc_type==2) || (mmc_type==3)) && ((csd[0]&0xc0)==0x40))	//if SDSCv2 or SDHC/SDXC and CSD vers.2
	{
		block_len=512;
		c_size=csd[9]+csd[8]*256+(csd[7]&0x3f)*256*256;
		blocknr=(c_size+1)<<10;
		size = (c_size+1)<<9;								//(c_size+1)*block_len(=512) kbytes
		
	}
	else
	{	
		c_size = csd[8] + csd[7] * 256 + (csd[6] & 0x03) * 256 * 256;
		c_size >>= 6;
		if ((mmc_type==0) && (c_size==0x0fff))
		{
			printk("mmc: sorry, no 2gb+ mmc support yet");	//'coz i'm layzee :P
			return(2);
		}
		c_size_mult = csd[10] + (csd[9] & 0x03) * 256;
		c_size_mult >>= 7;
		read_bl_len = csd[5] & 0x0f;
		mult = 1;
		mult <<= c_size_mult + 2;
		blocknr = (c_size + 1) * mult;
		block_len = 1;
		block_len <<= read_bl_len;
		size = block_len * blocknr;
		size >>= 10;
	}

    if (block_len!=512)
    {
        printk("mmc: TODO - not-512bytes-a-sector support! -- Currently not supported!\n");
        return 2;
    }

	for(i=0; i<(1<<6); i++) {
	  hd_blocksizes[i] = 1024;
	  hd_hardsectsizes[i] = block_len;
	  hd_maxsect[i] = 256;
	}
	hd_sizes[0] = size;
	hd[0].nr_sects = blocknr;

    printk("mmc: Media found Size = %d kB, hardsectsize = %d, sectors = %d\n",
	       size, block_len, blocknr);
    printk("mmc: read_bl_len= %d, c_size=%d, c_size_mult=%d\n",
           read_bl_len,c_size,c_size_mult);

	return 0;
}


#ifdef CHECK_MEDIA_CHANGE
static int mmc_check_media_change(kdev_t dev)
{
	(void)dev;
	if (mmc_media_changed == 1)
	{
		mmc_media_changed = 0;
		return 1;
	}
	else return 0;
}
#endif

static int mmc_init(void)
{
	int rc;

	rc = mmc_hardware_init();

	if ( rc != 0)
	{
		printk("mmc: error in mmc_hardware_init (%d)\n", rc);
		return -1;
	}

	rc = mmc_card_init();
	if ( rc != 0)
	{
		// Give it an extra shot
		rc = mmc_card_init();
		if ( rc != 0)
		{
			printk("mmc: error in mmc_card_init (%d)\n", rc);
			return -1;
		}
	}

	memset(hd_sizes, 0, sizeof(hd_sizes));
	rc = mmc_card_config();
	if ( rc != 0)
	{
		printk("mmc: error in mmc_card_config (%d)\n", rc);
		return -1;
	}


	blk_size[MAJOR_NR] = hd_sizes;

	memset(hd, 0, sizeof(hd));
	hd[0].nr_sects = hd_sizes[0]*2;

	blksize_size[MAJOR_NR] = hd_blocksizes;
	hardsect_size[MAJOR_NR] = hd_hardsectsizes;
	max_sectors[MAJOR_NR] = hd_maxsect;

	hd_gendisk.nr_real = 1;

	register_disk(&hd_gendisk, MKDEV(MAJOR_NR,0), 1<<6,&mmc_bdops, hd_sizes[0]*2);

	return 0;
}

static void mmc_exit(void)
{
	blk_size[MAJOR_NR] = NULL;
	blksize_size[MAJOR_NR] = NULL;
	hardsect_size[MAJOR_NR] = NULL;
	max_sectors[MAJOR_NR] = NULL;
	hd[0].nr_sects = 0;
}

static void mmc_check_media(void)
{
	int old_state;
	int rc;

	old_state = mmc_media_detect;

	// TODO: Add card detection here
	mmc_media_detect = 1;
	if (old_state != mmc_media_detect)
	{
		mmc_media_changed = 1;
		if (mmc_media_detect == 1)
		{
			rc = mmc_init();
			if (rc != 0) printk("mmc: error in mmc_init (%d)\n", rc);
		}
		else
		{
			mmc_exit();
		}
	}

#ifdef CHECK_MEDIA_CHANGE
    del_timer(&mmc_timer);
	mmc_timer.expires = jiffies + 10*HZ;
	add_timer(&mmc_timer); 
#endif	
	
}

///////////////////////////////////////////////////////////////////////////////////////////////
/////// Driver Interface
///////////////////////////////////////////////////////////////////////////////////////////////
static int mmc_revalidate(kdev_t dev)
{
	int target, max_p, start, i;
	if (mmc_media_detect == 0) return -ENODEV;

	target = DEVICE_NR(dev);

	max_p = hd_gendisk.max_p;
	start = target << 6;
	for (i = max_p - 1; i >= 0; i--) {
		int minor = start + i;
		invalidate_device(MKDEV(MAJOR_NR, minor), 1);
		hd_gendisk.part[minor].start_sect = 0;
		hd_gendisk.part[minor].nr_sects = 0;
	}

	grok_partitions(&hd_gendisk, target, 1 << 6,
			hd_sizes[0] * 2);

	return 0;
}

static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
	if (!inode || !inode->i_rdev)
		return -EINVAL;

	switch(cmd) {
	case BLKGETSIZE:
		return put_user(hd[MINOR(inode->i_rdev)].nr_sects, (unsigned long *)arg);
	case BLKGETSIZE64:
		return put_user((u64)hd[MINOR(inode->i_rdev)].
				nr_sects, (u64 *) arg);
	case BLKRRPART:
		if (!capable(CAP_SYS_ADMIN))
			return -EACCES;

		return mmc_revalidate(inode->i_rdev);
	case HDIO_GETGEO:
	{
		struct hd_geometry *loc, g;
		loc = (struct hd_geometry *) arg;
		if (!loc)
			return -EINVAL;
        g.heads = 4;
        g.sectors = 16;
        g.cylinders = hd[0].nr_sects / (4 * 16);
		g.start = hd[MINOR(inode->i_rdev)].start_sect;
		return copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0;
	}
	default:
		return blk_ioctl(inode->i_rdev, cmd, arg);
	}
}

static void mmc_request(request_queue_t *q)
{
	unsigned int mmc_address;
	unsigned char *buffer_address;
	int nr_sectors;
	int i;
	int cmd;
	int rc, code;

	(void)q;
	while (1)
	{
        ///for address translation: SDHC/SDXC uses addressing blocks of 512bytes / SDSC or legacy cards usese byte addressing
		code = 1; // Default is success
		INIT_REQUEST;
		mmc_address = (mmc_type>2)?(CURRENT->sector + hd[MINOR(CURRENT->rq_dev)].start_sect):((CURRENT->sector + hd[MINOR(CURRENT->rq_dev)].start_sect) * hd_hardsectsizes[0]);
		buffer_address = CURRENT->buffer;
		nr_sectors = CURRENT->current_nr_sectors;
		cmd = CURRENT->cmd;
		if (((CURRENT->sector + CURRENT->current_nr_sectors + hd[MINOR(CURRENT->rq_dev)].start_sect) > hd[0].nr_sects) || (mmc_media_detect == 0))
		{
			code = 0;
		}
		else if (cmd == READ)
		{
			spin_unlock_irq(&io_request_lock);
			for (i = 0; i < nr_sectors; i++)
			{
				rc = mmc_read_block(buffer_address, mmc_address);
				if (rc != 0)
				{
					printk("mmc: error in mmc_read_block (%d)\n", rc);
					code = 0;
					break;
				}
				else
				{
					mmc_address +=(mmc_type>2)?1:hd_hardsectsizes[0];
					buffer_address +=hd_hardsectsizes[0];
				}
			}
			spin_lock_irq(&io_request_lock);
		}
		else if (cmd == WRITE)
		{
			spin_unlock_irq(&io_request_lock);
            if ((mmc_type==0) || (mmc_type==4))     ///sorry, it's not for MMC
            {
                for (i = 0; i < nr_sectors; i++)
                {
                    rc = mmc_write_block(mmc_address, buffer_address);
                    if (rc != 0)
                    {
                        printk("mmc: error in mmc_write_block (%d)\n", rc);
                        code = 0;
                        break;
                    }
                    else
                    {
                        mmc_address +=(mmc_type>2)?1:hd_hardsectsizes[0];
                        buffer_address +=hd_hardsectsizes[0];
                    }
                }
			}
            else    //but for SD cards
            {
                rc = mmc_write_multiblock(mmc_address, buffer_address, nr_sectors);
                if (rc != 0)
                {
                    printk("mmc: error in mmc_write_multiblock (%d)\n", rc);
                    code = 0;
                    break;
                }
                else
                {
                    mmc_address +=(mmc_type>2)?nr_sectors:(hd_hardsectsizes[0]*nr_sectors);
                    buffer_address +=(hd_hardsectsizes[0]*nr_sectors);
                }
            }
			spin_lock_irq(&io_request_lock);
		}
		else
		{
			code = 0;
		}
		end_request(code);
	}
}

static int mmc_open(struct inode *inode, struct file *filp)
{
  //int device;
	(void)filp;

	if (mmc_media_detect == 0) return -ENODEV;

#if defined(MODULE)
	MOD_INC_USE_COUNT;
#endif
	return 0;
}

static int mmc_release(struct inode *inode, struct file *filp)
{
	(void)filp;
	fsync_dev(inode->i_rdev);
        invalidate_buffers(inode->i_rdev);

#if defined(MODULE)
	MOD_DEC_USE_COUNT;
#endif
	return 0;
}

static int __init mmc_driver_init(void)
{
	int rc;

    printk("mmc2 Driver $Id: mmc2.c,v 2.0 2011/12/27 13:42:05 satsuse Exp $\n");
	rc = devfs_register_blkdev(MAJOR_NR, DEVICE_NAME, &mmc_bdops);
	if (rc < 0)
	{
		printk(KERN_WARNING "mmc: can't get major %d\n", MAJOR_NR);
		return rc;
	}

	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), mmc_request);

	read_ahead[MAJOR_NR] = 8;
	add_gendisk(&hd_gendisk);

	mmc_check_media();

#ifdef CHECK_MEDIA_CHANGE
	init_timer(&mmc_timer);
	mmc_timer.expires = jiffies + HZ;
	mmc_timer.function = (void *)mmc_check_media;
	add_timer(&mmc_timer);
#endif	

	return 0;
}

static void __exit mmc_driver_exit(void)
{
	int i;
	del_timer(&mmc_timer);

	for (i = 0; i < (1 << 6); i++)   //64
		fsync_dev(MKDEV(MAJOR_NR, i));

	devfs_register_partitions(&hd_gendisk, 0<<6, 1);   //0<<6 STILL EQ 0 !!
	devfs_unregister_blkdev(MAJOR_NR, DEVICE_NAME);
	del_gendisk(&hd_gendisk);
	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
	mmc_exit();
    printk("mmc: removing driver\n");
}

module_init(mmc_driver_init);
module_exit(mmc_driver_exit);
rhabarber1848
CDK-Experte
Beiträge: 4335
Registriert: Donnerstag 3. April 2008, 14:05

Re: Working SDHC with improved write speed

Beitrag von rhabarber1848 »

CodeKiller hat geschrieben:(i would like to have a full tutorial to build a yadd
http://wiki.tuxbox-cvs.sourceforge.net/ ... are-Images
CodeKiller
Neugieriger
Neugieriger
Beiträge: 3
Registriert: Donnerstag 29. Dezember 2011, 12:49

Re: Working SDHC with improved write speed

Beitrag von CodeKiller »

Thanks, i will try it next time!
sagemol
Einsteiger
Einsteiger
Beiträge: 193
Registriert: Donnerstag 11. Mai 2006, 09:26

Re: Working SDHC with improved write speed

Beitrag von sagemol »

subscribing...

Thanks for that !

Greez !
CodeKiller
Neugieriger
Neugieriger
Beiträge: 3
Registriert: Donnerstag 29. Dezember 2011, 12:49

Re: Working SDHC with improved write speed

Beitrag von CodeKiller »

Well, as the SSL time growed out of the ordinary, i had to edit my driver to work with SDXC cards i have (technically i shouldn't have to do it, but the manufactures seem doesn't care too much about SPI modes, so they have shoddy implementations)

Words of wisdom: due to limited number of cards to test with i can only talk about an Adata 64GB and a Samsung EVO 128GB
The former are downright bad for SPI (refuses to send start_data token on read_block) but the latter have quirks on multi_block_write (after data_end token it forgets to respond busy right away thus confusing the driver as it thought it was ready so then the next command fails horribly)
But on the bright side, the Samsung with this driver and without any other hardware modification, through FTP transfer can keep a stable 340 kBYTES/s!!!!!
Literally WTF??
Read is 200-250kBytes/s too.

DON'T FORGET: IT IS A DIFFERENT PINOUT and for NOKIA ONLY.
(code formatting is not great but not terrible :) )

Merry X-Mas and Happy New Year!

Code: Alles auswählen

//  $Id: mmc2.c,v 2.1 2019/12/22 19:00:00 satsuse Exp $
//
//  MMC2.c
//  General MMC device driver for dbox Modem Connector
//---//  Modem connector pins PA9,PA8,PB16,PB17 used -> MMC2 connection sheme
//  Modem connector pins PA9,PA8,PB16,PA7 used -> MMC2 connection sheme
//
//  This version of MMC is used to test various optimisation variants only
//
//  14 Aug 2006
//                                           ---- Modem_CN ---
//                                           SAGEM NOKIA  PHIL
// PA9  = SD_DO = 0x0040 = SD_Card Pin 7   =   2    12     11
// PA8  = SD_DI = 0x0080 = SD_Card Pin 2   =   1    11      9
// PA7  = SD_CLK= 0x0100 = SD_Card Pin 5   =   ?    14      ?
///----------------------------// PB17 = SD_CLK= 0x4000 = SD_Card Pin 5   =  10     2      7
// PB16 = SD_CS = 0x8000 = SD_Card Pin 1   =   6     6      5
// GND  =       = Masse  = SD_Card Pin 3,6 =   3    10      2
// VCC  =       = 3,3V   = SD_Card Pin 4   =   5    16      x
// (x philips: connect in series 3 Diodes (1N4007) from MODEM_CN 1 to SD/MMC card pin 4)
// Also connect a pullup resistor 100 KOhm from SD/MMC card pin 7 (SD_DO) to SD/MMC card pin 4 (VCC)
// 
// written by Madsuk/Rohde/TaGana
// optimized by just_me/Guenther/DboxBar
// 
// (partial) big card (2gb+ MMC / SDHC / SDXC) support by CodeKiller
//
//
#include <linux/delay.h>
#include <linux/timer.h>

#include <linux/kernel.h>

#include <linux/module.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/blkpg.h>
#include <linux/hdreg.h>
#include <linux/major.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/8xx_immap.h>

#define DEVICE_NAME "mmc"
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#define MAJOR_NR 121

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
extern void m8xx_wdt_reset(void);
#endif

#include <linux/blk.h>

MODULE_AUTHOR("Madsuk/Rohde/TaGana/CK");
MODULE_DESCRIPTION("Driver MMC/SD-Cards");
MODULE_SUPPORTED_DEVICE("all dbox2 on com2 connector");
MODULE_LICENSE("GPL");

//#define CHECK_MEDIA_CHANGE  // for developement ONLY, not working yet

#define SD_DO  0x0040 // on SD/MMC card pin 7
#define SD_DI  0x0080 // on SD/MMC card pin 2
#define SD_CLK 0x0100 // on SD/MMC card pin 5
#define SD_CS  0x8000 // on SD/MMC card pin 1

volatile immap_t *immap=(immap_t *)IMAP_ADDR ;

/* we have only one device */
static int hd_sizes[1<<6];   //1<<6=SILLY!!    EQUAL 64!    but why??
static int hd_blocksizes[1<<6];
static int hd_hardsectsizes[1<<6];
static int hd_maxsect[1<<6];
static struct hd_struct hd[1<<6];

static struct timer_list mmc_timer;
static int mmc_media_detect = 0;
static int mmc_media_changed = 1;

volatile int mmc_type=1;        //4=MMC 2GB+; 3=SDHC/SDXC; 2=SDSCv2.0; 1=SDSC; 0=MMC

extern struct gendisk hd_gendisk;

/////////////////////
// prototypes
static int mmc_open(struct inode *inode, struct file *filp);
static int mmc_release(struct inode *inode, struct file *filp);
static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
static void mmc_request(request_queue_t *q);


static struct block_device_operations mmc_bdops =
{
	open: mmc_open,
	release: mmc_release,
	ioctl: mmc_ioctl,
#ifdef CHECK_MEDIA_CHANGE
	check_media_change: mmc_check_media_change,
	revalidate: mmc_revalidate,
#endif
};

static struct gendisk hd_gendisk = {
	major:		MAJOR_NR,
	major_name:	DEVICE_NAME,
	minor_shift:	6,
	max_p:		1 << 6,
	part:		hd,
	sizes:		hd_sizes,
	fops:		&mmc_bdops,
};

///////////////////////////////////////////////////////////////////////////////////////////////
/////// Low-Level Driver
///////////////////////////////////////////////////////////////////////////////////////////////

static int mmc_hardware_init(void) {
   volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
   volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;

   printk("mmc2: Hardware init\n");
   cp->cp_pbpar &=   ~(SD_CS);
   cp->cp_pbodr &=   ~(SD_CS);
   cp->cp_pbdir |=    (SD_CS);
   cpi->iop_papar &= ~(SD_DO | SD_DI | SD_CLK);
   cpi->iop_paodr &= ~(SD_DO | SD_CLK);
   cpi->iop_padir |=   (SD_DI | SD_CLK);
   cpi->iop_padir &=  ~SD_DO;

    // Clock + CS low
   cpi->iop_padat &= ~SD_CLK;
   cp->cp_pbdat &= ~SD_CS;
   cpi->iop_padat &= ~SD_DI;
   return 0;
}

static void mmc_spi_cs_low(void) {
  volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
  cp->cp_pbdat &= ~(SD_CS);
}

static void mmc_spi_cs_high(void) {
  volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
  cp->cp_pbdat |= SD_CS;
}

static unsigned char mmc_spi_io(unsigned char data_out) {
  //volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
  volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;
  unsigned char result = 0;
  unsigned char  i;

  for(i = 0x80; i != 0; i >>= 1) {
    if (data_out & i)
      cpi->iop_padat |= SD_DI;
    else
      cpi->iop_padat &= ~SD_DI;

    // latch data on SD_DI into card
    cpi->iop_padat |= SD_CLK;
    // shouldn't data be read after clk h->l?
    if (cpi->iop_padat & SD_DO) {
    	result |= i;
    }
    cpi->iop_padat &= ~SD_CLK;
  }

  return result;
}

//////////////////////////////////
// optimized read/write functions

inline void mmc_spi_write(unsigned char data_out) {
   //volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
   volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;
   //unsigned long clk_high = cp->cp_pbdat | SD_CLK;
   //unsigned long clk_low =  cp->cp_pbdat & ~SD_CLK;

   //unsigned long data_high = cpi->iop_padat | SD_DI;
   //unsigned long data_low =  cpi->iop_padat & ~SD_DI;


    unsigned int cldl=cpi->iop_padat & ~(SD_CLK | SD_DI);
	unsigned int cldh=(cpi->iop_padat | SD_DI) & ~SD_CLK;	
	unsigned int chdl=(cpi->iop_padat | SD_CLK) & ~SD_DI;	
	unsigned int chdh=cpi->iop_padat | SD_DI | SD_CLK; 	

    
        // bit 7
		if (data_out & 0x80) {
			//wmb();
			cpi->iop_padat = cldh;	// set data-high
			//wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			//wmb();
			cpi->iop_padat = cldl;	// set data-low
            //wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 6
		if (data_out & 0x40) {
			//wmb();
			cpi->iop_padat = cldh;	// set data-high
			//wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			//wmb();
			cpi->iop_padat = cldl;	// set data-low
			//wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 5
		if (data_out & 0x20) {
			//wmb();
			cpi->iop_padat = cldh;	// set data-high
			//wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			//wmb();
			cpi->iop_padat = cldl;	// set data-low
			//wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 4
		if (data_out & 0x10) {
			//wmb();
			cpi->iop_padat = cldh;	// set data-high
			//wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			//wmb();
			cpi->iop_padat = cldl;	// set data-low
			//wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 3
		if (data_out & 0x08) {
			//wmb();
			cpi->iop_padat = cldh;	// set data-high
			//wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			//wmb();
			cpi->iop_padat = cldl;	// set data-low
			//wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 2
		if (data_out & 0x04) {
			//wmb();
			cpi->iop_padat = cldh;	// set data-high
			//wmb();
	 		cpi->iop_padat = chdh;	// latch
		}     
		else {
			//wmb();
			cpi->iop_padat = cldl;	// set data-low
			//wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 1
		if (data_out & 0x02) {
			//wmb();
			cpi->iop_padat = cldh;	// set data-high
			//wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			//wmb();
			cpi->iop_padat = cldl;	// set data-low
			//wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 0
		if (data_out & 0x01) {
            //wmb();
			cpi->iop_padat = cldh;	// set data-high
			//wmb();
 			cpi->iop_padat = chdh;	// latch
            cpi->iop_padat = cldh;
		}     
		else {
			//wmb();
			cpi->iop_padat = cldl;	// set data-low
			//wmb();
			cpi->iop_padat = chdl;	// latch
            cpi->iop_padat = cldl;
        }
    

   /* 
   if (data_out & 0x80) cpi->iop_padat = data_high;// 1 bit
   else                 cpi->iop_padat = data_low;
   cpi->iop_padat = clk_high;
   cpi->iop_padat= clk_low;
   if (data_out & 0x40) cpi->iop_padat = data_high;// 2 bit
   else                 cpi->iop_padat = data_low;
   cpi->iop_padat = clk_high;
   cpi->iop_padat = clk_low;
   if (data_out & 0x20) cpi->iop_padat = data_high;// 3 bit
   else                 cpi->iop_padat = data_low;
   cpi->iop_padat = clk_high;
   cpi->iop_padat = clk_low;
   if (data_out & 0x10) cpi->iop_padat = data_high;// 4 bit
   else                 cpi->iop_padat= data_low;
   cpi->iop_padat = clk_high;
   cpi->iop_padat = clk_low;
   if (data_out & 0x08) cpi->iop_padat = data_high;// 5 bit
   else                 cpi->iop_padat = data_low;
   cpi->iop_padat = clk_high;
   cpi->iop_padat = clk_low;
   if (data_out & 0x04) cpi->iop_padat = data_high;// 6 bit
   else                 cpi->iop_padat = data_low;
   cpi->iop_padat = clk_high;
   cpi->iop_padat = clk_low;
   if (data_out & 0x02) cpi->iop_padat = data_high;// 7 bit
   else                 cpi->iop_padat = data_low;
   cpi->iop_padat = clk_high;
   cpi->iop_padat = clk_low;
   if (data_out & 0x01) cpi->iop_padat = data_high;// 8 bit
   else                 cpi->iop_padat = data_low;
   cpi->iop_padat = clk_high;
   cpi->iop_padat = clk_low;
   */
}

inline unsigned char mmc_spi_read(void)
{
   unsigned char result = 0;

   //volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
   volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;

   unsigned long clk_high = cpi->iop_padat | SD_CLK | SD_DI;
   unsigned long clk_low =  (cpi->iop_padat | SD_DI ) & ~SD_CLK;

   cpi->iop_padat |= SD_DI;

   cpi->iop_padat = clk_high; // Bit 0 (MSB)
   result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
   cpi->iop_padat = clk_low;
   cpi->iop_padat = clk_high; // Bit 1
   result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
   cpi->iop_padat = clk_low;
   cpi->iop_padat = clk_high; // Bit 2
   result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
   cpi->iop_padat = clk_low;
   cpi->iop_padat = clk_high; // Bit 3 
   result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
   cpi->iop_padat = clk_low;
   cpi->iop_padat = clk_high; // Bit 4
   result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
   cpi->iop_padat = clk_low;
   cpi->iop_padat = clk_high; // Bit 5
   result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
   cpi->iop_padat = clk_low;
   cpi->iop_padat = clk_high; // Bit 6
   result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
   cpi->iop_padat = clk_low;
   cpi->iop_padat = clk_high; // Bit 7
   result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
   cpi->iop_padat = clk_low;

   return result;
}

////////////////////////////////////////
// burst transfer quite more optimized (approx 10% faster than above)

void mmc_read_block_low_level(unsigned char *dest)
{
   //volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
   volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;

   unsigned long clk_high = cpi->iop_padat | SD_CLK | SD_DI;   // load clock port into register ram and prefefine with pin low/high,to avoid reload on any clock trigger, this speeds ups the transmission significantly
   unsigned long clk_low = (cpi->iop_padat | SD_DI ) & ~SD_CLK;

   cpi->iop_padat |= SD_DI; // for read access we send high bytes only

   unsigned char result=0;
   unsigned int i=0;

   for (i = 0; i < 512; i++)
   {
      cpi->iop_padat = clk_high; // Bit 0
      result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
      cpi->iop_padat = clk_low;
      cpi->iop_padat = clk_high; // Bit 1
      result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
      cpi->iop_padat = clk_low;
      cpi->iop_padat = clk_high; // Bit 2
      result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
      cpi->iop_padat = clk_low;
      cpi->iop_padat = clk_high; // Bit 3
      result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
      cpi->iop_padat = clk_low;
      cpi->iop_padat = clk_high; // Bit 4
      result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
      cpi->iop_padat = clk_low;
      cpi->iop_padat = clk_high; // Bit 5
      result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
      cpi->iop_padat = clk_low;
      cpi->iop_padat = clk_high; // Bit 6
      result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
      cpi->iop_padat = clk_low;
      cpi->iop_padat = clk_high; // Bit 7
      result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
      cpi->iop_padat = clk_low;

      *dest++ = result;       
      result = 0;
   }
}




void mmc_write_block_low_level(unsigned char *dest)
{
   //volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
   volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;

   //unsigned long clk_high = cp->cp_padat | SD_CLK;   // load clock port into register ram and prefefine with pin low/high,to avoid reload on any clock trigger, this speeds ups the transmission significantly
   //unsigned long clk_low =  cp->cp_padat & ~SD_CLK;

   //unsigned long data_high = cpi->iop_padat | SD_DI;
   //unsigned long data_low =  cpi->iop_padat & ~SD_DI;
	unsigned int cldl=cpi->iop_padat & ~(SD_CLK | SD_DI);
	unsigned int cldh=(cpi->iop_padat | SD_DI) & ~SD_CLK;	
	unsigned int chdl=(cpi->iop_padat | SD_CLK) & ~SD_DI;	
	unsigned int chdh=cpi->iop_padat | SD_DI | SD_CLK; 	
   
   //cpi->iop_padat |= SD_DI; // for read access we send high bytes only

   unsigned char data = *dest++;
   unsigned int i=0;

   for (i = 0; i < 512; i++)
   {
   
        // bit 7
		if (data & 0x80) {
			//wmb();
			cpi->iop_padat = cldh;	// set data-high
			//wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			//wmb();
			cpi->iop_padat = cldl;	// set data-low
			//wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 6
		if (data & 0x40) {
			//wmb();
			cpi->iop_padat = cldh;	// set data-high
			//wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			//wmb();
			cpi->iop_padat = cldl;	// set data-low
			//wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 5
		if (data & 0x20) {
			//wmb();
			cpi->iop_padat = cldh;	// set data-high
			//wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			//wmb();
			cpi->iop_padat = cldl;	// set data-low
			//wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 4
		if (data & 0x10) {
			//wmb();
			cpi->iop_padat = cldh;	// set data-high
			//wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			//wmb();
			cpi->iop_padat = cldl;	// set data-low
			//wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 3
		if (data & 0x08) {
			//wmb();
			cpi->iop_padat = cldh;	// set data-high
			//wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			//wmb();
			cpi->iop_padat = cldl;	// set data-low
			//wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 2
		if (data & 0x04) {
			//wmb();
			cpi->iop_padat = cldh;	// set data-high
			//wmb();
	 		cpi->iop_padat = chdh;	// latch
		}     
		else {
			//wmb();
			cpi->iop_padat = cldl;	// set data-low
			//wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 1
		if (data & 0x02) {
			//wmb();
			cpi->iop_padat = cldh;	// set data-high
			//wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			//wmb();
			cpi->iop_padat = cldl;	// set data-low
			//wmb();
			cpi->iop_padat = chdl;	// latch
		}

		// bit 0
		if (data & 0x01) {
			//wmb();
			cpi->iop_padat = cldh;	// set data-high
			//wmb();
 			cpi->iop_padat = chdh;	// latch
		}     
		else {
			//wmb();
			cpi->iop_padat = cldl;	// set data-low
			//wmb();
			cpi->iop_padat = chdl;	// latch
        }
   
   
/*   
      cpi->iop_padat=(data&0x80)?data_high:data_low;      
      cp->cp_pbdat = clk_high; // Bit 0    
      cp->cp_pbdat = clk_low;
      cpi->iop_padat=(data&0x40)?data_high:data_low;      
      cp->cp_pbdat = clk_high; // Bit 1      
      cp->cp_pbdat = clk_low;
      cpi->iop_padat=(data&0x20)?data_high:data_low;
      cp->cp_pbdat = clk_high; // Bit 2
      cp->cp_pbdat = clk_low;
      cpi->iop_padat=(data&0x10)?data_high:data_low;      
      cp->cp_pbdat = clk_high; // Bit 3      
      cp->cp_pbdat = clk_low;
      cpi->iop_padat=(data&0x08)?data_high:data_low;      
      cp->cp_pbdat = clk_high; // Bit 4      
      cp->cp_pbdat = clk_low;
      cpi->iop_padat=(data&0x04)?data_high:data_low;
      cp->cp_pbdat = clk_high; // Bit 5
      cp->cp_pbdat = clk_low;
      cpi->iop_padat=(data&0x02)?data_high:data_low;     
      cp->cp_pbdat = clk_high; // Bit 6      
      cp->cp_pbdat = clk_low;
      cpi->iop_padat=(data&0x01)?data_high:data_low;
      cp->cp_pbdat = clk_high; // Bit 7      
      cp->cp_pbdat = clk_low;
*/
      data = *dest++;
   }
   
   cpi->iop_padat = cldh;
}

///////////////////////////////////////////////////////////////////////////////////////////////
/////// High-Level Driver
///////////////////////////////////////////////////////////////////////////////////////////////

static int mmc_write_block(unsigned int dest_addr, unsigned char *data)
{
	unsigned char r = 0;
	int i;

	mmc_spi_cs_low();
    //for (i = 0; i < 4; i++) mmc_spi_read();
    mmc_spi_write(0x58);        //cmd24 (WRITE_BLOCK)
    mmc_spi_write(0xff & (dest_addr >> 24)); /* msb */
    mmc_spi_write(0xff & (dest_addr >> 16));
    mmc_spi_write(0xff & (dest_addr >> 8));
    mmc_spi_write(0xff & dest_addr); /* lsb */
    mmc_spi_write(0xff);
	for (i = 0; i < 8; i++)
	{
       r = mmc_spi_read();
		if (r == 0x00) break;
	}
	if (r != 0x00)
	{
		mmc_spi_cs_high();
        mmc_spi_write(0xff);
		return(1);
	}

    mmc_spi_write(0xfe);

    mmc_write_block_low_level(data);
    //for (i = 0; i < 512; i++) mmc_spi_write(data[i]);
    //for (i = 0; i < 2; i++)   mmc_spi_write(0xff);   // send CRC for nothing
    mmc_spi_write(0xff);    //fake CRC
    mmc_spi_write(0xff);
    
	for (i = 0; i < 1000000; i++)
	{
       r = mmc_spi_read();
		if (r == 0xff) break;
	}
	if (r != 0xff)
	{
		mmc_spi_cs_high();
        mmc_spi_write(0xff);
		return(3);
	}
	mmc_spi_cs_high();
    mmc_spi_write(0xff);
	return(0);
}

static int mmc_write_multiblock(unsigned int dest_addr, unsigned char *data, unsigned int nr_blocks)
{
	unsigned char r = 0;
	int i;

	mmc_spi_cs_low();
    //mmc_spi_write(0xff);
    
    mmc_spi_write(0x77);        //cmd55 (app_cmd)
    mmc_spi_write(0);
    mmc_spi_write(0);
    mmc_spi_write(0);
    mmc_spi_write(0);
    mmc_spi_write(0xff);
    
	i=258;
	do
	{
        r = mmc_spi_read();
	}while((--i)&&(r&0x80));
	if (r) //> 0x02))
	{
		mmc_spi_cs_high();
        mmc_spi_write(0xff);
        printk("mmc: mmc_write_multiblock error55 response %d\n",r);
		return(4);
	}
	mmc_spi_cs_high();
    mmc_spi_write(0xff);
    mmc_spi_cs_low();

    // mmc_spi_write(0xff);
    // mmc_spi_write(0xff);
    // mmc_spi_write(0xff);
    // mmc_spi_write(0xff);

    mmc_spi_write(0x57);        //acmd23 (SET_WR_BLK_ERASE_COUNT)
    mmc_spi_write(0); /* msb */
    mmc_spi_write(0xff & (nr_blocks >> 16));
    mmc_spi_write(0xff & (nr_blocks >> 8));
    mmc_spi_write(0xff & nr_blocks); /* lsb */
    mmc_spi_write(0xff);
    
	i=500;
	do
	{
        r = mmc_spi_read();
	}while((--i)&&(r&0x80));
	if (r != 0x00)
	{
		mmc_spi_cs_high();
        mmc_spi_write(0xff);
        printk("mmc: mmc_write_multiblock error_a23 response %d i: %d  nr_blocks: %d @%d\n",r,i, nr_blocks, dest_addr);
		return(5);
	}
	mmc_spi_cs_high();    
    mmc_spi_write(0xff);
    mmc_spi_cs_low();
        
    mmc_spi_write(0xff);
    
    mmc_spi_write(0x59);        //cmd25 (WRITE_MULTIPLE_BLOCK)
    mmc_spi_write(0xff & (dest_addr >> 24)); /* msb */
    mmc_spi_write(0xff & (dest_addr >> 16));
    mmc_spi_write(0xff & (dest_addr >> 8));
    mmc_spi_write(0xff & dest_addr); /* lsb */
    mmc_spi_write(0xff);

	i=8;
	do
	{
        r = mmc_spi_read();
	}while((i--)&&(r&0x80));
	if (r)
	{
		mmc_spi_cs_high();
        mmc_spi_write(0xff);
		return(1);
	}

    //write blocks
    while (nr_blocks)
    {
        //start token
        mmc_spi_write(0xfc);

        //write 512 bytes of data
        mmc_write_block_low_level(data);
        //for (i = 0; i < 512; i++) mmc_spi_write(data[i]);
        //for (i = 0; i < 2; i++)   mmc_spi_write(0xff);   // send CRC for nothing
        mmc_spi_write(0xff);    //fake CRC
        mmc_spi_write(0xff);
    
        //wait for clear
        i=100000;
        do
        {
            r = mmc_spi_read();
        }while((--i)&&(r!=0xff));
        if (r != 0xff)
        {
            mmc_spi_cs_high();
            mmc_spi_write(0xff);
            return(3);
        }
        nr_blocks--;
        data+=512;
    }
    //multiple write end token
    mmc_spi_write(0xfd);

    //wait for busy
    i=100000;
    do
    {
        r = mmc_spi_read();
    }while((--i)&&(r==0xff));
    if (r == 0xff)
    {
        mmc_spi_cs_high();
        mmc_spi_write(0xff);
        return(7);
    }
    
    //wait for clear
    i=100000;
    do
    {
        r = mmc_spi_read();
    }while((--i)&&(r!=0xff));
    if (r != 0xff)
    {
        mmc_spi_cs_high();
        mmc_spi_write(0xff);
        return(6);
    }
    
	mmc_spi_cs_high();
    mmc_spi_write(0xff);
	return(0);
}


static int mmc_read_block(unsigned char *data, unsigned int src_addr)
{
	unsigned char r = 0;
	int i;

    //mmc_spi_read();

	mmc_spi_cs_low();
    //for (i = 0; i < 4; i++) 
    mmc_spi_read();

    mmc_spi_write(0x51);        //cmd17 (READ_SINGLE_BLOCK)
    mmc_spi_write(0xff & (src_addr >> 24)); /* msb */
    mmc_spi_write(0xff & (src_addr >> 16));
    mmc_spi_write(0xff & (src_addr >> 8));
    mmc_spi_write(0xff & src_addr); /* lsb */

    //mmc_spi_io(0x00); //crc
    mmc_spi_read();
    
    //readc r1 response
	i=10;
	do
	{
        r = mmc_spi_read();
	}while((--i)&&(r));//&0x80));
	if (r)
	{
		mmc_spi_cs_high();
        mmc_spi_read();
        printk("mmc: mmc_read_block error response %d\n",r);
		return(1);
	}
    
    //read start token
    // i=1000000;
    // do
    // {
        // r = mmc_spi_read();
    // }while((i--)&&(r==0xff));

	for (i = 0; i < 1000000; i++)
	{
        r = mmc_spi_read();
		if (r != 0xff) break;
	}     
	if (r != 0xfe)
	{
		mmc_spi_cs_high();
        mmc_spi_read();
        printk("mmc: mmc_read_block error token %d (i %d)\n",r,i);
		return(2);
	}
    
    //read 512 bytes of data
    //for (i = 0; i < 512; i++) data[i] = mmc_spi_io(0xff);
    mmc_read_block_low_level(data);

    mmc_spi_read(); // just read the CRC for nothing
    mmc_spi_read();

	mmc_spi_cs_high();
    mmc_spi_read();

	return(0);
}

static int mmc_card_init(void)
{
	unsigned char r = 0;
	unsigned int i, j;
	unsigned long flags;
	unsigned char r7vca;
	unsigned char r7echo;

	save_flags(flags);
	cli();

	
	init:
	
	printk("mmc: Card init\n");
	mmc_spi_cs_high();
	for (i = 0; i < 100; i++) mmc_spi_io(0xff);		///need at least 74clk -- now this is 800clk

	unsigned short cntr=0;
	
    do{
		mmc_spi_cs_low();       //ATN
		
		mmc_spi_io(0x40);       //cmd0
        mmc_spi_io(0x00);
        mmc_spi_io(0x00);
        mmc_spi_io(0x00);
		mmc_spi_io(0x00);   //arg[4]
		mmc_spi_io(0x95);       //crc7+1 -- need to be good
		for (i = 0; i < 0xff; i++)
		{
			r = mmc_spi_io(0xff);
			if (r != 0xff) break;
		}	
		cntr++;
		
		mmc_spi_io(0xff);       //blank msg
		mmc_spi_cs_high();
	
	} while((r != 0x01)&&(cntr<0x40));   //continue if r1 response=idle or timeout
	
	if (r != 0x01)          //if not idle, return ///actually idle means initializing... (stupid, isn't it?)
	{
		restore_flags(flags);
        printk("mmc: Card init -cmd0 not respond- %d\n",r);
		return(1);
	}

    printk("mmc: Card init *OK1* (cmd0 response)\n");
    mmc_type=2;
    //ok1
    //cntr=0;

    //do
	//{
        //cntr++;
		mmc_spi_cs_low();

        //--cmd8
        mmc_spi_io(0x48);   //cmd8 --- check/init SDHC/SDXC
        //--args
        mmc_spi_io(0x00);
        mmc_spi_io(0x00);
        mmc_spi_io(0x01);
        mmc_spi_io(0xaa);   //check pattern (10101010)
        //--crc
        mmc_spi_io(0x87);   //need to be good        

		i=0;
		do
		{
			r = mmc_spi_io(0xff);
			i++;
		}while((i<8)&&(r==0xff));
        
        
		r7vca=mmc_spi_io(0xff);
		r7vca=mmc_spi_io(0xff);
		r7vca=mmc_spi_io(0xff);
		r7echo=mmc_spi_io(0xff);    //if there is a valid answer, this should be 0xaa or the communication is corrupted
		
        //if (cntr>10)				//only 10 retry...(should be enough)
        //{
        //    restore_flags(flags);
        //    printk("mmc: Card init -(cmd8) error-\n");
        //    return(2);
        //}
        
        mmc_spi_cs_high();      
        mmc_spi_io(0xff);       //blank msg

    //}while ( ((r&0x04)!=0x04) && ((r!=0x01)||(r7echo!=0xaa)) );      //exit if illegal cmd (SDSCv1.0 or MMC)  or  valid response
   
    if (r7echo==0xaa)
    {
		printk("mmc: SD Card v2 -- cmd8 response ok\n");
        //check voltage support -- cmd8 response vca part
        if (!(r7vca&1))
        {
            restore_flags(flags);
            printk("mmc: SD Card voltage not supported\n");
            return(2);
        }
    }
	else if((r&0x04)==0x04)
	{
		printk("mmc: legacy sd or mmc card -- cmd8 response illegal cmd\n");
		mmc_type=1;		//mmc or legacy sd card
	}

    //check voltage support for sd/2gb+ support for mmc cards -- cmd58
	//cntr=0;
    
    unsigned char o1;
    unsigned char o2;
    unsigned char o3;
    unsigned char o4;

	//do
	//{
		mmc_spi_cs_low(); //ATN
            
		mmc_spi_io(0x7a);       //cmd58
		mmc_spi_io(0x40);       //30:29=10b         //mmc 2gb+ init
        mmc_spi_io(0x00);
        mmc_spi_io(0x00);
		mmc_spi_io(0x00);   //args
		mmc_spi_io(0xff);       //fake crc (need no more -- disabled by default for spi except cmd0 and cmd8)
        //printk("mmc: send cmd58 resp:\n");
		i=0;
		do
		{
			r = mmc_spi_io(0xff);
            //printk("%d.=%d\t",i,r);
			i++;
		}while((i<255)&&(r!=0x00));
 
		if (r == 255)
		{
			printk("mmc: (58) Card not ready / not respond -- %d\n",r);
			//break;
		};
		//read OCR register
		//cntr++;
        o1=mmc_spi_io(0xff);
        o2=mmc_spi_io(0xff);
        o3=mmc_spi_io(0xff);
        o4=mmc_spi_io(0xff);
        //if ((i==8) && (r==0xff))
        //{
        //    restore_flags(flags);
        //    printk("mmc: Cmd58 timeout\n");
        //    return(2);
        //}
		
		mmc_spi_cs_high();
        mmc_spi_io(0xff);
		
    //} while ( (r&0x80==0x80)&& !((r==0x00)||((r&0x04)==0x04)) );          //while busy -- exit if valid response and (ready or invalid cmd)
	if (r!=0xff)
	{
        printk("mmc: i=%d ## o1=%d o2=%d o3=%d o4=%d\n",i,o1,o2,o3,o4);
		if ((o1&80) &&(o1&40) && (mmc_type==2))
		{
			mmc_type=3;
		};
		
	};
    
    if (r&0x04)
	{
		mmc_type=1;
	};
	
    m8xx_wdt_reset();
    
        mmc_spi_io(0xff);       //blank msg
        //mmc_spi_io(0xff);       //blank msg
        //mmc_spi_io(0xff);       //blank msg
        
	printk("mmc: init mmc_type %d \n",mmc_type);
    printk("mmc: start sd init -acmd41-\n");
    for (j = 0; j < 100; j++)
    {
		//printk("mmc: %d- cmd55 \n",j);
        
		mmc_spi_cs_low();	//ATN
        //mmc_spi_io(0xff);
     
		mmc_spi_io(0x77);
        mmc_spi_io(0x00);
        mmc_spi_io(0x00);
        mmc_spi_io(0x00);
        mmc_spi_io(0x00);
        mmc_spi_io(0xff);
		
		i=255;
		do
		{
			r = mmc_spi_io(0xff);
		}while((--i)&&(r&0x80));

		if (r == 0)
		{
			printk("mmc: (55) Card init *OK2* (SD card)\n");
			//break;
		};
			
		if ((r&0x84)==0x04)
		{
			printk("mmc: (55) a/cmd41 unknown -- switching to mmc mode init\n");
			mmc_type=0;
			break;
		}
		else
		{
			//printk("mmc: %d : unk55 response - %d \n",j,r);
		}
	
        //mmc_spi_io(0xff);
        //mmc_spi_io(0xff);
        //mmc_spi_io(0xff);
		
        mmc_spi_cs_high();
        mmc_spi_io(0xff);

		//printk("mmc: %d- acmd41 \n",j);
		
        mmc_spi_cs_low();	//ATN
        //mmc_spi_io(0xff);
			
        mmc_spi_io(0x69);
        //--args
        mmc_spi_io(0x40);	//high capacity card support
        mmc_spi_io(0x00);
        mmc_spi_io(0x00);
        mmc_spi_io(0x00);		
		//--fake_crc
        mmc_spi_io(0xff);
		
		i=200;
		do
		{
			r = mmc_spi_io(0xff);
		}while((--i)&&(r&0x80));
		
		if (r == 0)
		{
			printk("mmc: Card init *OK2* (SD card)\n");
			break;
		};
			
		if ((r&0x84)==0x04)
		{
			printk("mmc: a/cmd41 unknown -- switching to mmc mode init\n");
			mmc_type=0;
			break;
		}
		else
		{
			printk("mmc: %d : unk41 response - %d \n",j,r);
			//if ((j>10)&&(r==0xff)) goto init;
			if ((j>98)&&(r==1)) goto init;
		}
		
        //mmc_spi_io(0xff);
        //mmc_spi_io(0xff);

        mmc_spi_cs_high();
        mmc_spi_io(0xff);
		if(j%10==0)m8xx_wdt_reset();

        //mmc_spi_io(0xff);
        //mmc_spi_io(0xff);
        //mmc_spi_io(0xff);
	};
	
    mmc_spi_cs_high();
    mmc_spi_io(0xff);

       
    if (mmc_type==0)
    {
		printk("mmc: legacy/mmc init -cmd1-\n");
        for (j = 0; j < 100; j++)
        {
			printk("mmc: %d- cmd1 \n",j);
            mmc_spi_cs_low();
            
			//mmc_spi_io(0xff);
            
			mmc_spi_io(0x41);
            for (i = 0; i < 4; i++) mmc_spi_io(0x00);
            mmc_spi_io(0xff);
			i=0;
			do
			{
				r = mmc_spi_io(0xff);
				i++;
			}while((i<100)&&(r==0xff));
			
			if (r==0)
			{
				printk("mmc: Card init *OK2* (SDSCv1 or MMC)\n");
				break;
            }
            mmc_spi_cs_high();
            mmc_spi_io(0xff);
			if(j%10==0)m8xx_wdt_reset();
        }
        mmc_spi_cs_high();
        mmc_spi_io(0xff);
	}; 
    
	//cntr=0;
    if(mmc_type>1)
    {
        printk("mmc: Cmd58 send\n");
		mmc_spi_cs_low(); //ATN
            
		mmc_spi_io(0x7a);       //cmd58
		mmc_spi_io(0x40);       //30:29=10b         //mmc 2gb+ init
		for (i = 0; i < 3; i++) mmc_spi_io(0x00);   //args
		mmc_spi_io(0xff);       //fake crc (need no more -- disabled by default for spi except cmd0 and cmd8)
        //printk("mmc: send cmd58 resp:\n");
		i=0;
		do
		{
			r = mmc_spi_io(0xff);
            //printk("%d.=%d\t",i,r);
			i++;
		}while((i<80)&&(r!=0));

 
		//read OCR register
	//	cntr++;
        o1=mmc_spi_io(0xff);
        o2=mmc_spi_io(0xff);
        o3=mmc_spi_io(0xff);
        o4=mmc_spi_io(0xff);

        mmc_spi_io(0xff);
        
		mmc_spi_cs_high();
        mmc_spi_io(0xff);

        if (i>=8)
        {
            restore_flags(flags);
            printk("mmc: Cmd58 timeout\n");
            return(2);
        }
		
		
        //} while ( (r&0x80==0x80)&& !((r==0x00)||((r&0x04)==0x04)) );          //while busy -- exit if valid response and (ready or invalid cmd)
        printk("mmc: i=%d ## o1=%d o2=%d o3=%d o4=%d\n",i,o1,o2,o3,o4);
        if (r!=0xff)
        {
            if ((o1&80) && (o1&40) && (mmc_type==2))
            {
                mmc_type=3;
            }
		
        }
    }
    printk("mmc: init memory_card_type=%d\n",mmc_type);
    
	restore_flags(flags);
    return(0);
}

static int mmc_card_config(void)
{
	unsigned char r = 0;
	short i;//,j;
	unsigned char csd[32];
	unsigned int c_size;
	unsigned int c_size_mult;
	unsigned int mult;
	unsigned int read_bl_len;
	unsigned int blocknr = 0;
	unsigned int block_len = 0;
	unsigned int size = 0;

    
    //j=100;
  	//do
	//{
        mmc_spi_cs_high();
        
        //printk("mmc: send cmd9 (SEND_CSD) for %d times - %d\n",101-j,r);
        mmc_spi_io(0xff);
        mmc_spi_io(0xff);
        mmc_spi_io(0xff);
        
        mmc_spi_cs_low();
        //for (i = 0; i < 4; i++) mmc_spi_io(0xff);	//pre_junk (???)
        mmc_spi_io(0x49);       //cmd9 (SEND_CSD) -- only enough for sd cards
        mmc_spi_io(0x00);	//args
        mmc_spi_io(0x00);	//args
        mmc_spi_io(0x00);	//args
        mmc_spi_io(0x00);	//args
        mmc_spi_io(0xaf);							//fake crc

        //printk("mmc: send cmd9 resp:\n");
        for (i = 0; i < 8; i++) 
        {
            r = mmc_spi_io(0xff);					//wait for R1_READY response
            //printk("%d.=%d\t",i,r);
            if (r==0) break;
        }
        //j--;
	//}while((j) && (r!=0));
    
	if (r != 0x00)								//if not, error
	{
		mmc_spi_cs_high();
		mmc_spi_io(0xff);
        printk("mmc: config cmd9 response timeout %d\n",r);
		return(1);
	}
    printk("mmc: valid CSD R1 response: %d\n",r);
	for (i = 0; i < 8; i++)
	{
		r = mmc_spi_io(0xff);					//wait for startbit_token (%1111 1110)
            //printk("%d.=%d\t",i,r);
		if (r == 0xfe) break;
	}
	if (r != 0xfe)								//error
	{
		mmc_spi_cs_high();
		mmc_spi_io(0xff);
        printk("mmc: config data response timeout %d\n",r);
		return(2);
	}
    //csd[0]=r;
	for (i = 0; i < 16; i++)
	{
		r = mmc_spi_io(0xff);
		csd[i] = r;
	}
	for (i = 0; i < 2; i++)     //crc -- not used
	{
		r = mmc_spi_io(0xff);
	}
	mmc_spi_cs_high();
	mmc_spi_io(0xff);
	//if (r == 0x00) 
    //{
        
    //    return(3);				//bad response(?)
    //}
    read_bl_len=0;
    c_size_mult=0;
    printk("mmc: mmctype=%d     csd[0]=%d\n",mmc_type,csd[0]);
	if (((mmc_type==2) || (mmc_type==3)) && ((csd[0]&0xc0)==0x40))	//if SDSCv2 or SDHC/SDXC and CSD vers.2
	{
		block_len=512;
		c_size=csd[9]+csd[8]*256+(csd[7]&0x3f)*256*256;
		blocknr=(c_size+1)<<10;
		size = (c_size+1)<<9;								//(c_size+1)*block_len(=512) kbytes
		
	}
	else
	{	
		c_size = csd[8] + csd[7] * 256 + (csd[6] & 0x03) * 256 * 256;
		c_size >>= 6;
		if ((mmc_type==0) && (c_size==0x0fff))
		{
			printk("mmc: sorry, no 2gb+ mmc support yet");	//'coz i'm layzee :P
			return(2);
		}
		c_size_mult = csd[10] + (csd[9] & 0x03) * 256;
		c_size_mult >>= 7;
		read_bl_len = csd[5] & 0x0f;
		mult = 1;
		mult <<= c_size_mult + 2;
		blocknr = (c_size + 1) * mult;
		block_len = 1;
		block_len <<= read_bl_len;
		size = block_len * blocknr;
		size >>= 10;
	}

    if (block_len!=512)
    {
        printk("mmc: TODO - not-512bytes-a-sector support! -- Currently not supported!\n");
        return 2;
    }

	for(i=0; i<(1<<6); i++) {
	  hd_blocksizes[i] = 1024;
	  hd_hardsectsizes[i] = block_len;
	  hd_maxsect[i] = 256;
	}
	hd_sizes[0] = size;
	hd[0].nr_sects = blocknr;

    printk("mmc: Media found Size = %d kB, hardsectsize = %d, sectors = %d\n",
	       size, block_len, blocknr);
    printk("mmc: read_bl_len= %d, c_size=%d, c_size_mult=%d\n",
           read_bl_len,c_size,c_size_mult);

	return 0;
}


#ifdef CHECK_MEDIA_CHANGE
static int mmc_check_media_change(kdev_t dev)
{
	(void)dev;
	if (mmc_media_changed == 1)
	{
		mmc_media_changed = 0;
		return 1;
	}
	else return 0;
}
#endif

static int mmc_init(void)
{
	int rc;

	rc = mmc_hardware_init();

	if ( rc != 0)
	{
		printk("mmc: error in mmc_hardware_init (%d)\n", rc);
		return -1;
	}

	rc = mmc_card_init();
	if ( rc != 0)
	{
		// Give it an extra shot
		rc = mmc_card_init();
		if ( rc != 0)
		{
			printk("mmc: error in mmc_card_init (%d)\n", rc);
			return -1;
		}
	}

	memset(hd_sizes, 0, sizeof(hd_sizes));
	rc = mmc_card_config();
	if ( rc != 0)
	{
		printk("mmc: error in mmc_card_config (%d)\n", rc);
		return -1;
	}


	blk_size[MAJOR_NR] = hd_sizes;

	memset(hd, 0, sizeof(hd));
	hd[0].nr_sects = hd_sizes[0]*2;

	blksize_size[MAJOR_NR] = hd_blocksizes;
	hardsect_size[MAJOR_NR] = hd_hardsectsizes;
	max_sectors[MAJOR_NR] = hd_maxsect;

	hd_gendisk.nr_real = 1;

	register_disk(&hd_gendisk, MKDEV(MAJOR_NR,0), 1<<6,&mmc_bdops, hd_sizes[0]*2);

	return 0;
}

static void mmc_exit(void)
{
	blk_size[MAJOR_NR] = NULL;
	blksize_size[MAJOR_NR] = NULL;
	hardsect_size[MAJOR_NR] = NULL;
	max_sectors[MAJOR_NR] = NULL;
	hd[0].nr_sects = 0;
}

static void mmc_check_media(void)
{
	int old_state;
	int rc;

	old_state = mmc_media_detect;

	// TODO: Add card detection here
	mmc_media_detect = 1;
	if (old_state != mmc_media_detect)
	{
		mmc_media_changed = 1;
		if (mmc_media_detect == 1)
		{
			rc = mmc_init();
			if (rc != 0) printk("mmc: error in mmc_init (%d)\n", rc);
		}
		else
		{
			mmc_exit();
		}
	}

#ifdef CHECK_MEDIA_CHANGE
    del_timer(&mmc_timer);
	mmc_timer.expires = jiffies + 10*HZ;
	add_timer(&mmc_timer); 
#endif	
	
}

///////////////////////////////////////////////////////////////////////////////////////////////
/////// Driver Interface
///////////////////////////////////////////////////////////////////////////////////////////////
static int mmc_revalidate(kdev_t dev)
{
	int target, max_p, start, i;
	if (mmc_media_detect == 0) return -ENODEV;

	target = DEVICE_NR(dev);

	max_p = hd_gendisk.max_p;
	start = target << 6;
	for (i = max_p - 1; i >= 0; i--) {
		int minor = start + i;
		invalidate_device(MKDEV(MAJOR_NR, minor), 1);
		hd_gendisk.part[minor].start_sect = 0;
		hd_gendisk.part[minor].nr_sects = 0;
	}

	grok_partitions(&hd_gendisk, target, 1 << 6,
			hd_sizes[0] * 2);

	return 0;
}

static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
	if (!inode || !inode->i_rdev)
		return -EINVAL;

	switch(cmd) {
	case BLKGETSIZE:
		return put_user(hd[MINOR(inode->i_rdev)].nr_sects, (unsigned long *)arg);
	case BLKGETSIZE64:
		return put_user((u64)hd[MINOR(inode->i_rdev)].
				nr_sects, (u64 *) arg);
	case BLKRRPART:
		if (!capable(CAP_SYS_ADMIN))
			return -EACCES;

		return mmc_revalidate(inode->i_rdev);
	case HDIO_GETGEO:
	{
		struct hd_geometry *loc, g;
		loc = (struct hd_geometry *) arg;
		if (!loc)
			return -EINVAL;
        g.heads = 4;
        g.sectors = 16;
        g.cylinders = hd[0].nr_sects / (4 * 16);
		g.start = hd[MINOR(inode->i_rdev)].start_sect;
		return copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0;
	}
	default:
		return blk_ioctl(inode->i_rdev, cmd, arg);
	}
}

static void mmc_request(request_queue_t *q)
{
	unsigned int mmc_address;
	unsigned char *buffer_address;
	int nr_sectors;
	int i;
	int cmd;
	int rc, code;

	(void)q;
	while (1)
	{
        ///for address translation: SDHC/SDXC uses addressing blocks of 512bytes / SDSC or legacy cards usese byte addressing
		code = 1; // Default is success
		INIT_REQUEST;
		mmc_address = (mmc_type>2)?(CURRENT->sector + hd[MINOR(CURRENT->rq_dev)].start_sect):((CURRENT->sector + hd[MINOR(CURRENT->rq_dev)].start_sect) * hd_hardsectsizes[0]);
		buffer_address = CURRENT->buffer;
		nr_sectors = CURRENT->current_nr_sectors;
		cmd = CURRENT->cmd;
		if (((CURRENT->sector + CURRENT->current_nr_sectors + hd[MINOR(CURRENT->rq_dev)].start_sect) > hd[0].nr_sects) || (mmc_media_detect == 0))
		{
			code = 0;
		}
		else if (cmd == READ)
		{
			spin_unlock_irq(&io_request_lock);
			for (i = 0; i < nr_sectors; i++)
			{
				rc = mmc_read_block(buffer_address, mmc_address);
				if (rc != 0)
				{
					printk("mmc: error in mmc_read_block @ %d (code: %d)\n", mmc_address, rc);
					code = 0;
					break;
				}
				else
				{
					mmc_address +=(mmc_type>2)?1:hd_hardsectsizes[0];
					buffer_address +=hd_hardsectsizes[0];
				}
			}
			spin_lock_irq(&io_request_lock);
		}
		else if (cmd == WRITE)
		{
			spin_unlock_irq(&io_request_lock);
            if ((mmc_type==0) || (mmc_type==4))     ///sorry, it's not for MMC
            {
                for (i = 0; i < nr_sectors; i++)
                {
                    rc = mmc_write_block(mmc_address, buffer_address);
                    if (rc != 0)
                    {
                        printk("mmc: error in mmc_write_block (%d)\n", rc);
                        code = 0;
                        break;
                    }
                    else
                    {
                        mmc_address +=(mmc_type>2)?1:hd_hardsectsizes[0];
                        buffer_address +=hd_hardsectsizes[0];
                    }
                }
			}
            else    //but for SD cards
            {
                rc = mmc_write_multiblock(mmc_address, buffer_address, nr_sectors);
                //printk("mmc: mmc_write_multiblock @ %d no# %d (resp: %d)\n",mmc_address, nr_sectors, rc);
                if (rc != 0)
                {
                    printk("mmc: error in mmc_write_multiblock (%d)\n", rc);
                    code = 0;
                    break;
                }
                else
                {
                    mmc_address +=(mmc_type>2)?nr_sectors:(hd_hardsectsizes[0]*nr_sectors);
                    buffer_address +=(hd_hardsectsizes[0]*nr_sectors);
                }
            }
			spin_lock_irq(&io_request_lock);
		}
		else
		{
			code = 0;
		}
		end_request(code);
	}
}

static int mmc_open(struct inode *inode, struct file *filp)
{
  //int device;
	(void)filp;

	if (mmc_media_detect == 0) return -ENODEV;

#if defined(MODULE)
	MOD_INC_USE_COUNT;
#endif
	return 0;
}

static int mmc_release(struct inode *inode, struct file *filp)
{
	(void)filp;
	fsync_dev(inode->i_rdev);
        invalidate_buffers(inode->i_rdev);

#if defined(MODULE)
	MOD_DEC_USE_COUNT;
#endif
	return 0;
}

static int __init mmc_driver_init(void)
{
	int rc;

    printk("mmc2 Driver $Id: mmc2.c,v 2.0 2011/12/27 13:42:05 satsuse Exp $\n");
	rc = devfs_register_blkdev(MAJOR_NR, DEVICE_NAME, &mmc_bdops);
	if (rc < 0)
	{
		printk(KERN_WARNING "mmc: can't get major %d\n", MAJOR_NR);
		return rc;
	}

	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), mmc_request);

	read_ahead[MAJOR_NR] = 8;
	add_gendisk(&hd_gendisk);

	mmc_check_media();

#ifdef CHECK_MEDIA_CHANGE
	init_timer(&mmc_timer);
	mmc_timer.expires = jiffies + HZ;
	mmc_timer.function = (void *)mmc_check_media;
	add_timer(&mmc_timer);
#endif	

	return 0;
}

static void __exit mmc_driver_exit(void)
{
	int i;
	del_timer(&mmc_timer);

	for (i = 0; i < (1 << 6); i++)   //64
		fsync_dev(MKDEV(MAJOR_NR, i));

	devfs_register_partitions(&hd_gendisk, 0<<6, 1);   //0<<6 STILL EQ 0 !!
	devfs_unregister_blkdev(MAJOR_NR, DEVICE_NAME);
	del_gendisk(&hd_gendisk);
	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
	mmc_exit();
    printk("mmc: removing driver\n");
}

module_init(mmc_driver_init);
module_exit(mmc_driver_exit);