r3889 - branches/src/target/kernel/2.6.24.x/patches

werner at sita.openmoko.org werner at sita.openmoko.org
Mon Jan 21 03:19:36 CET 2008


Author: werner
Date: 2008-01-21 03:19:30 +0100 (Mon, 21 Jan 2008)
New Revision: 3889

Added:
   branches/src/target/kernel/2.6.24.x/patches/glamo-mmc.patch
Modified:
   branches/src/target/kernel/2.6.24.x/patches/series
   branches/src/target/kernel/2.6.24.x/patches/smedia-glamo.patch
Log:
This patch adds a new MMC/SD MCI driver for the Glamo MMC/SD interface.

It supports

 - interrupt-based operation where possible
 - 1-bit and 4-bit mode
 - clock rate up to 16MHz -- 25MHz is not reliable and gives
     IO errors
 - read and write throughput is ~1.2MBytes/sec
 - voltage scaling as requested by MCI (done via platform)

The driver detects if it is on < GTA02 A5 and spins instead of using
the interrupt, so it will work on unmodified older A4 devices at least.

Several patches from this work fixing bugs in glamo-core and elsewhere
are already merged in mokopatches.  I have updated the kernel.git tree
as well with current mokopatches, so the only dependency at the moment
is glamo-platform-mmc.patch which is also in git.  You also need to
have these guys in .config:

CONFIG_MMC=y
CONFIG_MMC_BLOCK=y
CONFIG_MFD_GLAMO_MCI=y

You will typically see an error like this during boot

[   23.000000] FAT: invalid media value (0x15)
[   23.000000] VFS: Can't find a valid FAT filesystem on dev mmcblk0.
[   23.010000] FAT: invalid media value (0x15)
[   23.010000] VFS: Can't find a valid FAT filesystem on dev mmcblk0.

but it's okay, some flash devices don't have a partition table and it
tries to mount first the actual device node, only afterwards it tries
the first partition (mmcblk0p1), which succeeds silently.

One remaining write corruption bug with Glamo MMC behaviour is not
certain to be solved yet, the following stress test will expose any
problems:

$ dd if=/dev/urandom of=/tmp/dump bs=4096 count=2560
2560+0 records in
2560+0 records out
$ mount /media/card ; c=0; hexdump -C /tmp/dump >/tmp/dump.txt ; a=`md5sum /tmp/dump | cut -d' ' -f1` ; while [ 1 ] ; do cp /tmp/dump /media/card ; umount /media/card ; mount /media/card ; b=`md5sum /media/card/dump | cut -d' ' -f1`; if [ "$a" != "$b" ] ; then echo $c: ***** FAIL $a != $b ; hexdump -C /media/card/dump >/tmp/mc-dump.txt; diff -urN /tmp/dump.txt /tmp/mc-dump.txt | head -n50; else echo $c: OK ; fi ; c=$(( $c + 1 )); done

It creates a 10MB random file and then copies it to the card, unmounts
forcing it to be flushed to the card, remounts and checks the md5sum
is the same as before.  If not, it shows the first part of an ascii hex
diff of the change.

Signed-off-by: Andy Green <andy at openmoko.com>

smedia-glamo.patch:
- drivers/mfd/glamo/glamo-core.c (glamo_init_script): more register changes 
glamo-mmc.patch:
- drivers/mfd/glamo/Kconfig, drivers/mfd/glamo/glamo-core.c, 
  drivers/mfd/glamo/glamo-mci.c, drivers/mfd/glamo/glamo-mci.h,
  drivers/mfd/glamo/glamo-regs.h: SD/MMC driver



Added: branches/src/target/kernel/2.6.24.x/patches/glamo-mmc.patch
===================================================================
--- branches/src/target/kernel/2.6.24.x/patches/glamo-mmc.patch	2008-01-21 02:04:49 UTC (rev 3888)
+++ branches/src/target/kernel/2.6.24.x/patches/glamo-mmc.patch	2008-01-21 02:19:30 UTC (rev 3889)
@@ -0,0 +1,1167 @@
+Index: linux-2.6.24-rc7/drivers/mfd/glamo/Kconfig
+===================================================================
+--- linux-2.6.24-rc7.orig/drivers/mfd/glamo/Kconfig
++++ linux-2.6.24-rc7/drivers/mfd/glamo/Kconfig
+@@ -33,3 +33,12 @@
+ 	 control channel.  This SPI interface is frequently used to
+ 	 interconnect the LCM control interface.
+ 
++config MFD_GLAMO_MCI
++	tristate "Glamo S3C SD/MMC Card Interface support"
++	depends on MFD_GLAMO && MMC
++	help
++	  This selects a driver for the MCI interface found in
++	  the S-Media GLAMO chip, as used in OpenMoko
++	  neo1973 GTA-02.
++
++	  If unsure, say N.
+\ No newline at end of file
+Index: linux-2.6.24-rc7/drivers/mfd/glamo/Makefile
+===================================================================
+--- linux-2.6.24-rc7.orig/drivers/mfd/glamo/Makefile
++++ linux-2.6.24-rc7/drivers/mfd/glamo/Makefile
+@@ -8,4 +8,5 @@
+ 
+ obj-$(CONFIG_MFD_GLAMO_FB)		+= glamo-fb.o
+ obj-$(CONFIG_MFD_GLAMO_SPI_FB)		+= glamo-lcm-spi.o
++obj-$(CONFIG_MFD_GLAMO_MCI)		+= glamo-mci.o
+ 
+Index: linux-2.6.24-rc7/drivers/mfd/glamo/glamo-core.c
+===================================================================
+--- linux-2.6.24-rc7.orig/drivers/mfd/glamo/glamo-core.c
++++ linux-2.6.24-rc7/drivers/mfd/glamo/glamo-core.c
+@@ -440,7 +440,22 @@
+ {
+ 	spin_lock(&glamo->lock);
+ 	switch (engine) {
+-	/* FIXME: Implementation */
++	case GLAMO_ENGINE_MMC:
++		__reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_MMC, 0,
++						   GLAMO_CLOCK_MMC_EN_M9CLK |
++						   GLAMO_CLOCK_MMC_EN_TCLK |
++						   GLAMO_CLOCK_MMC_DG_M9CLK |
++						   GLAMO_CLOCK_MMC_DG_TCLK);
++		__reg_set_bit_mask(glamo, GLAMO_REG_HOSTBUS(2), 0,
++						   GLAMO_HOSTBUS2_MMIO_EN_MMC);
++		/* disable the TCLK divider clk input */
++		__reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_GEN5_1, 0,
++						GLAMO_CLOCK_GEN51_EN_DIV_TCLK);
++		/* good idea to hold the thing in reset when we power it off? */
++/*		writew(readw(glamo->base + GLAMO_REG_CLOCK_MMC) |
++		      GLAMO_CLOCK_MMC_RESET, glamo->base + GLAMO_REG_CLOCK_MMC);
++*/
++		break;
+ 	default:
+ 		break;
+ 	}
+Index: linux-2.6.24-rc7/drivers/mfd/glamo/glamo-mci.c
+===================================================================
+--- /dev/null
++++ linux-2.6.24-rc7/drivers/mfd/glamo/glamo-mci.c
+@@ -0,0 +1,837 @@
++/*
++ *  linux/drivers/mmc/host/glamo-mmc.c - Glamo MMC driver
++ *
++ *  Copyright (C) 2007 OpenMoko, Inc,  Andy Green <andy at openmoko.com>
++ *  Based on S3C MMC driver that was:
++ *  Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk at maintech.de>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/dma-mapping.h>
++#include <linux/clk.h>
++#include <linux/mmc/mmc.h>
++#include <linux/mmc/host.h>
++#include <linux/platform_device.h>
++#include <linux/irq.h>
++#include <linux/pcf50633.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++
++#include <asm/dma.h>
++#include <asm/dma-mapping.h>
++#include <asm/io.h>
++
++#include "glamo-mci.h"
++#include "glamo-core.h"
++#include "glamo-regs.h"
++
++/* from glamo-core.c */
++extern struct glamo_mci_pdata glamo_mci_def_pdata;
++
++
++#define DRIVER_NAME "glamo-mci"
++#define RESSIZE(ressource) (((ressource)->end - (ressource)->start) + 1)
++
++static void glamo_mci_send_request(struct mmc_host *mmc);
++
++unsigned char CRC7(u8 * pu8, int cnt)
++{
++	u8 crc = 0;
++
++	while (cnt--) {
++		int n;
++		u8 d = *pu8++;
++		for (n = 0; n < 8; n++) {
++			crc <<= 1;
++			if ((d & 0x80) ^ (crc & 0x80))
++				crc ^= 0x09;
++			d <<= 1;
++		}
++	}
++	return (crc << 1) | 1;
++}
++
++/* these _dly versions account for the dead time rules for reg access */
++static u16 readw_dly(u16 __iomem * pu16)
++{
++	glamo_reg_access_delay();
++	return readw(pu16);
++}
++
++static void writew_dly(u16 val, u16 __iomem * pu16)
++{
++	glamo_reg_access_delay();
++	writew(val, pu16);
++}
++
++static int get_data_buffer(struct glamo_mci_host *host,
++			   volatile u32 *words, volatile u16 **pointer)
++{
++	struct scatterlist *sg;
++
++	*words = 0;
++	*pointer = NULL;
++
++	if (host->pio_active == XFER_NONE)
++		return -EINVAL;
++
++	if ((!host->mrq) || (!host->mrq->data))
++		return -EINVAL;
++
++	if (host->pio_sgptr >= host->mrq->data->sg_len) {
++		dev_dbg(&host->pdev->dev, "no more buffers (%i/%i)\n",
++		      host->pio_sgptr, host->mrq->data->sg_len);
++		return -EBUSY;
++	}
++	sg = &host->mrq->data->sg[host->pio_sgptr];
++
++	*words = sg->length >> 1; /* we are working with a 16-bit data bus */
++	*pointer = page_address(sg_page(sg)) + sg->offset;
++
++	BUG_ON(((long)(*pointer)) & 1);
++
++	host->pio_sgptr++;
++
++	/* dev_info(&host->pdev->dev, "new buffer (%i/%i)\n",
++	      host->pio_sgptr, host->mrq->data->sg_len); */
++	return 0;
++}
++
++static void do_pio_read(struct glamo_mci_host *host)
++{
++	int res;
++	u16 __iomem *from_ptr = host->base_data + (RESSIZE(host->mem_data) /
++							      sizeof(u16) / 2);
++#ifdef DEBUG
++	u16 * block;
++#endif
++
++	while (1) {
++		res = get_data_buffer(host, &host->pio_words, &host->pio_ptr);
++		if (res) {
++			host->pio_active = XFER_NONE;
++			host->complete_what = COMPLETION_FINALIZE;
++
++			dev_dbg(&host->pdev->dev, "pio_read(): "
++				"complete (no more data).\n");
++			return;
++		}
++
++		dev_dbg(&host->pdev->dev, "pio_read(): host->pio_words: %d\n",
++				host->pio_words);
++
++		host->pio_count += host->pio_words << 1;
++
++#ifdef DEBUG
++		block = (u16 *)host->pio_ptr;
++		res = host->pio_words << 1;
++#endif
++		while (host->pio_words--)
++			*host->pio_ptr++ = *from_ptr++;
++#ifdef DEBUG
++		print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 1,
++			       (void *)block, res, 1);
++#endif
++	}
++}
++
++static int do_pio_write(struct glamo_mci_host *host)
++{
++	int res = 0;
++	volatile u16 __iomem *to_ptr = host->base_data;
++	int err = 0;
++
++	dev_dbg(&host->pdev->dev, "pio_write():\n");
++	while (!res) {
++		res = get_data_buffer(host, &host->pio_words, &host->pio_ptr);
++		if (res)
++			continue;
++
++		dev_dbg(&host->pdev->dev, "pio_write():new source: [%i]@[%p]\n",
++			host->pio_words, host->pio_ptr);
++
++		host->pio_count += host->pio_words << 1;
++		while (host->pio_words--)
++			writew(*host->pio_ptr++, to_ptr++);
++	}
++
++	dev_dbg(&host->pdev->dev, "pio_write(): complete\n");
++	host->pio_active = XFER_NONE;
++	return err;
++}
++
++static void glamo_mci_irq(unsigned int irq, struct irq_desc *desc)
++{
++	struct glamo_mci_host *host = (struct glamo_mci_host *)
++				      desc->handler_data;
++	u16 status;
++	struct mmc_command *cmd;
++	unsigned long iflags;
++
++	if (!host)
++		return;
++	if (!host->mrq)
++		return;
++	cmd = host->mrq->cmd;
++	if (!cmd)
++		return;
++
++	spin_lock_irqsave(&host->complete_lock, iflags);
++
++	status = readw_dly(host->base + GLAMO_REG_MMC_RB_STAT1);
++
++	/* ack this interrupt source */
++	writew(GLAMO_IRQ_MMC,
++	       glamo_mci_def_pdata.pglamo->base + GLAMO_REG_IRQ_CLEAR);
++
++	if (status & (GLAMO_STAT1_MMC_RTOUT |
++		      GLAMO_STAT1_MMC_DTOUT))
++		cmd->error = -ETIMEDOUT;
++	if (status & (GLAMO_STAT1_MMC_BWERR |
++		      GLAMO_STAT1_MMC_BRERR))
++		cmd->error = -EILSEQ;
++	if (cmd->error) {
++		dev_err(&host->pdev->dev, "Error after cmd: 0x%x\n", status);
++		goto done;
++	}
++
++	if (host->pio_active == XFER_READ)
++		do_pio_read(host);
++
++	host->mrq->data->bytes_xfered = host->pio_count;
++	dev_dbg(&host->pdev->dev, "status = 0x%04x count=%d\n",
++		 status, host->pio_count);
++
++	/* issue STOP if we have been given one to use */
++	if (host->mrq->stop) {
++		host->cmd_is_stop = 1;
++		glamo_mci_send_request(host->mmc);
++		host->cmd_is_stop = 0;
++	}
++done:
++	host->complete_what = COMPLETION_NONE;
++	host->mrq = NULL;
++	mmc_request_done(host->mmc, cmd->mrq);
++	spin_unlock_irqrestore(&host->complete_lock, iflags);
++}
++
++static int glamo_mci_send_command(struct glamo_mci_host *host,
++				  struct mmc_command *cmd)
++{
++	u8 u8a[6];
++	u16 fire = 0;
++
++	/* if we can't do it, reject as busy */
++	if (!readw_dly(host->base + GLAMO_REG_MMC_RB_STAT1) &
++	     GLAMO_STAT1_MMC_IDLE) {
++		host->mrq = NULL;
++		cmd->error = -EBUSY;
++		mmc_request_done(host->mmc, host->mrq);
++		return -EBUSY;
++	}
++
++	/* create an array in wire order for CRC computation */
++	u8a[0] = 0x40 | (cmd->opcode & 0x3f);
++	u8a[1] = (u8)(cmd->arg >> 24);
++	u8a[2] = (u8)(cmd->arg >> 16);
++	u8a[3] = (u8)(cmd->arg >> 8);
++	u8a[4] = (u8)cmd->arg;
++	u8a[5] = CRC7(&u8a[0], 5); /* CRC7 on first 5 bytes of packet */
++
++	/* issue the wire-order array including CRC in register order */
++	writew_dly((u8a[4] << 8) | u8a[5], host->base + GLAMO_REG_MMC_CMD_REG1);
++	writew_dly((u8a[2] << 8) | u8a[3], host->base + GLAMO_REG_MMC_CMD_REG2);
++	writew_dly((u8a[0] << 8) | u8a[1], host->base + GLAMO_REG_MMC_CMD_REG3);
++
++	/* command index toggle */
++	fire |= (host->ccnt & 1) << 12;
++
++	/* set type of command */
++	switch (mmc_cmd_type(cmd)) {
++	case MMC_CMD_BC:
++		fire |= GLAMO_FIRE_MMC_CMDT_BNR;
++		break;
++	case MMC_CMD_BCR:
++		fire |= GLAMO_FIRE_MMC_CMDT_BR;
++		break;
++	case MMC_CMD_AC:
++		fire |= GLAMO_FIRE_MMC_CMDT_AND;
++		break;
++	case MMC_CMD_ADTC:
++		fire |= GLAMO_FIRE_MMC_CMDT_AD;
++		break;
++	}
++	/*
++	 * if it expects a response, set the type expected
++	 *
++	 * R1, Length  : 48bit, Normal response
++	 * R1b, Length : 48bit, same R1, but added card busy status
++	 * R2, Length  : 136bit (really 128 bits with CRC snipped)
++	 * R3, Length  : 48bit (OCR register value)
++	 * R4, Length  : 48bit, SDIO_OP_CONDITION, Reverse SDIO Card
++	 * R5, Length  : 48bit, IO_RW_DIRECTION, Reverse SDIO Card
++	 * R6, Length  : 48bit (RCA register)
++	 * R7, Length  : 48bit (interface condition, VHS(voltage supplied),
++	 *                     check pattern, CRC7)
++	 */
++	switch (mmc_resp_type(cmd)) {
++	case MMC_RSP_R6: /* same index as R7 and R1 */
++		fire |= GLAMO_FIRE_MMC_RSPT_R1;
++		break;
++	case MMC_RSP_R1B:
++		fire |= GLAMO_FIRE_MMC_RSPT_R1b;
++		break;
++	case MMC_RSP_R2:
++		fire |= GLAMO_FIRE_MMC_RSPT_R2;
++		break;
++	case MMC_RSP_R3:
++		fire |= GLAMO_FIRE_MMC_RSPT_R3;
++		break;
++	/* R4 and R5 supported by chip not defined in linux/mmc/core.h (sdio) */
++	}
++	/*
++	 * From the command index, set up the command class in the host ctrllr
++	 *
++	 * missing guys present on chip but couldn't figure out how to use yet:
++	 *     0x0 "stream read"
++	 *     0x9 "cancel running command"
++	 */
++	switch (cmd->opcode) {
++	case MMC_READ_SINGLE_BLOCK:
++		fire |= GLAMO_FIRE_MMC_CC_SBR; /* single block read */
++		break;
++	case MMC_SWITCH: /* 64 byte payload */
++	case 0x33: /* observed issued by MCI */
++	case MMC_READ_MULTIPLE_BLOCK:
++		/* we will get an interrupt off this */
++		if (!cmd->mrq->stop)
++			/* multiblock no stop */
++			fire |= GLAMO_FIRE_MMC_CC_MBRNS;
++		else
++			 /* multiblock with stop */
++			fire |= GLAMO_FIRE_MMC_CC_MBRS;
++		break;
++	case MMC_WRITE_BLOCK:
++		fire |= GLAMO_FIRE_MMC_CC_SBW; /* single block write */
++		break;
++	case MMC_WRITE_MULTIPLE_BLOCK:
++		if (cmd->mrq->stop)
++			 /* multiblock with stop */
++			fire |= GLAMO_FIRE_MMC_CC_MBWS;
++		else
++			 /* multiblock NO stop-- 'RESERVED'? */
++			fire |= GLAMO_FIRE_MMC_CC_MBWNS;
++		break;
++	case MMC_STOP_TRANSMISSION:
++		fire |= GLAMO_FIRE_MMC_CC_STOP; /* STOP */
++		break;
++	default:
++		fire |= GLAMO_FIRE_MMC_CC_BASIC; /* "basic command" */
++		break;
++	}
++	/* enforce timeout */
++	if (cmd->data) {
++		if (cmd->data->timeout_clks)
++			writew_dly(cmd->data->timeout_clks >> 4, /* / 16 clks */
++					host->base + GLAMO_REG_MMC_TIMEOUT);
++		else
++			writew_dly(0xfff, host->base + GLAMO_REG_MMC_TIMEOUT);
++	} else
++		writew(0xfff, host->base + GLAMO_REG_MMC_TIMEOUT);
++
++	/* Generate interrupt on txfer; drive strength max */
++	writew_dly((readw_dly(host->base + GLAMO_REG_MMC_BASIC) & 0xfe) |
++		   0x0800 | GLAMO_BASIC_MMC_NO_CLK_RD_WAIT |
++		   GLAMO_BASIC_MMC_EN_COMPL_INT |
++		   GLAMO_BASIC_MMC_EN_DR_STR0 |
++		   GLAMO_BASIC_MMC_EN_DR_STR1,
++		   host->base + GLAMO_REG_MMC_BASIC);
++
++	/* send the command out on the wire */
++	/* dev_info(&host->pdev->dev, "Using FIRE %04X\n", fire); */
++	writew_dly(fire, host->base + GLAMO_REG_MMC_CMD_FIRE);
++	cmd->error = 0;
++	return 0;
++}
++
++static int glamo_mci_prepare_pio(struct glamo_mci_host *host,
++				 struct mmc_data *data)
++{
++	/*
++	 * the S-Media-internal RAM offset for our MMC buffer
++	 * Read is halfway up the buffer and write is at the start
++	 */
++	if (data->flags & MMC_DATA_READ) {
++		writew_dly((u16)(GLAMO_FB_SIZE + (RESSIZE(host->mem_data) / 2)),
++			   host->base + GLAMO_REG_MMC_WDATADS1);
++		writew_dly((u16)((GLAMO_FB_SIZE +
++					(RESSIZE(host->mem_data) / 2)) >> 16),
++			   host->base + GLAMO_REG_MMC_WDATADS2);
++	} else {
++		writew_dly((u16)GLAMO_FB_SIZE, host->base +
++					       GLAMO_REG_MMC_RDATADS1);
++		writew_dly((u16)(GLAMO_FB_SIZE >> 16), host->base +
++						       GLAMO_REG_MMC_RDATADS2);
++	}
++
++	/* set up the block info */
++	writew_dly(data->blksz, host->base + GLAMO_REG_MMC_DATBLKLEN);
++	writew_dly(data->blocks, host->base + GLAMO_REG_MMC_DATBLKCNT);
++	dev_dbg(&host->pdev->dev, "(blksz=%d, count=%d)\n",
++				   data->blksz, data->blocks);
++	host->pio_sgptr = 0;
++	host->pio_words = 0;
++	host->pio_count = 0;
++	host->pio_active = 0;
++	/* if write, prep the write into the shared RAM before the command */
++	if (data->flags & MMC_DATA_WRITE) {
++		host->pio_active = XFER_WRITE;
++		return do_pio_write(host);
++	}
++	host->pio_active = XFER_READ;
++	return 0;
++}
++
++static void glamo_mci_send_request(struct mmc_host *mmc)
++{
++	struct glamo_mci_host *host = mmc_priv(mmc);
++	struct mmc_request *mrq = host->mrq;
++	struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
++	u16 * pu16 = (u16 *)&cmd->resp[0];
++	u16 * reg_resp = (u16 *)(host->base + GLAMO_REG_MMC_CMD_RSP1);
++	u16 status;
++	int n;
++
++	host->ccnt++;
++	/*
++	 * somehow 2.6.24 MCI manages to issue MMC_WRITE_BLOCK *without* the
++	 * MMC_DATA_WRITE flag, WTF?  Work around the madness.
++	 */
++	if (cmd->opcode == MMC_WRITE_BLOCK)
++		if (mrq->data)
++			mrq->data->flags |= MMC_DATA_WRITE;
++
++	 /* this guy has data to read/write? */
++	if ((!host->cmd_is_stop) && cmd->data) {
++		int res;
++		host->dcnt++;
++		res = glamo_mci_prepare_pio(host, cmd->data);
++		if (res) {
++			cmd->error = -EIO;
++			cmd->data->error = -EIO;
++			mmc_request_done(mmc, mrq);
++			return;
++		}
++	}
++
++	dev_dbg(&host->pdev->dev,"cmd 0x%x, "
++		 "arg 0x%x data=%p mrq->stop=%p flags 0x%x\n",
++		 cmd->opcode, cmd->arg, cmd->data, cmd->mrq->stop,
++		 cmd->flags);
++
++	if (glamo_mci_send_command(host, cmd))
++		return;
++	/*
++	 * we must spin until response is ready or timed out
++	 * -- we don't get interrupts unless there is a bulk rx
++	 */
++	do
++		status = readw_dly(host->base + GLAMO_REG_MMC_RB_STAT1);
++	while ((((status >> 15) & 1) != (host->ccnt & 1)) ||
++		(!(status & (GLAMO_STAT1_MMC_RB_RRDY |
++			     GLAMO_STAT1_MMC_RTOUT |
++			     GLAMO_STAT1_MMC_DTOUT |
++			     GLAMO_STAT1_MMC_BWERR |
++			     GLAMO_STAT1_MMC_BRERR))));
++
++	if (status & (GLAMO_STAT1_MMC_RTOUT |
++		      GLAMO_STAT1_MMC_DTOUT))
++		cmd->error = -ETIMEDOUT;
++	if (status & (GLAMO_STAT1_MMC_BWERR |
++		      GLAMO_STAT1_MMC_BRERR))
++		cmd->error = -EILSEQ;
++
++	if (host->cmd_is_stop)
++		return;
++
++	if (cmd->error) {
++		dev_err(&host->pdev->dev, "Error after cmd: 0x%x\n", status);
++		goto done;
++	}
++	/*
++	 * mangle the response registers in two different exciting
++	 * undocumented ways discovered by trial and error
++	 */
++	if (mmc_resp_type(cmd) == MMC_RSP_R2)
++		/* grab the response */
++		for (n = 0; n < 8; n++) /* super mangle power 1 */
++			pu16[n ^ 6] = readw_dly(&reg_resp[n]);
++	else
++		for (n = 0; n < 3; n++) /* super mangle power 2 */
++			pu16[n] = (readw_dly(&reg_resp[n]) >> 8) |
++				  (readw_dly(&reg_resp[n + 1]) << 8);
++	/*
++	 * if we don't have bulk data to take care of, we're done
++	 */
++	if (!cmd->data)
++		goto done;
++	if (!(cmd->data->flags & (MMC_DATA_READ | MMC_DATA_WRITE)))
++		goto done;
++	/*
++	 * Otherwise can can use the interrupt as async completion --
++	 * if there is read data coming, or we wait for write data to complete,
++	 * exit without mmc_request_done() as the payload interrupt
++	 * will service it
++	 */
++	dev_dbg(&host->pdev->dev, "Waiting for payload data\n");
++	/*
++	 * if the glamo INT# line isn't wired (*cough* it can happen)
++	 * I'm afraid we have to spin on the IRQ status bit and "be
++	 * our own INT# line"
++	 */
++	if (!glamo_mci_def_pdata.pglamo->irq_works) {
++		/* we have faith we will get an "interrupt"... */
++		while (!(readw_dly(glamo_mci_def_pdata.pglamo->base +
++			 GLAMO_REG_IRQ_STATUS) & GLAMO_IRQ_MMC))
++			;
++		/* yay we are an interrupt controller! -- call the ISR */
++		glamo_mci_irq(IRQ_GLAMO(GLAMO_IRQIDX_MMC),
++			      irq_desc + IRQ_GLAMO(GLAMO_IRQIDX_MMC));
++	}
++	return;
++
++done:
++	host->complete_what = COMPLETION_NONE;
++	host->mrq = NULL;
++	mmc_request_done(host->mmc, cmd->mrq);
++}
++
++static void glamo_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
++{
++	struct glamo_mci_host *host = mmc_priv(mmc);
++
++	host->cmd_is_stop = 0;
++	host->mrq = mrq;
++	glamo_mci_send_request(mmc);
++}
++
++static void glamo_mci_reset(struct glamo_mci_host *host)
++{
++	/* reset MMC controller */
++	writew_dly(GLAMO_CLOCK_MMC_RESET | GLAMO_CLOCK_MMC_DG_TCLK |
++		   GLAMO_CLOCK_MMC_EN_TCLK | GLAMO_CLOCK_MMC_DG_M9CLK |
++		   GLAMO_CLOCK_MMC_EN_M9CLK,
++		   glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_MMC);
++	msleep(1);
++	/* and disable reset */
++	writew_dly(GLAMO_CLOCK_MMC_DG_TCLK |
++		   GLAMO_CLOCK_MMC_EN_TCLK | GLAMO_CLOCK_MMC_DG_M9CLK |
++		   GLAMO_CLOCK_MMC_EN_M9CLK,
++		   glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_MMC);
++}
++
++static void glamo_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
++{
++	struct glamo_mci_host *host = mmc_priv(mmc);
++	int mci_psc = 0;
++	int n = 0;
++
++	/* Set power */
++	switch(ios->power_mode) {
++	case MMC_POWER_ON:
++	case MMC_POWER_UP:
++		if (host->power_mode_current != MMC_POWER_OFF)
++			break;
++		if (host->vdd_current != ios->vdd) {
++			host->pdata->glamo_set_mci_power(ios->power_mode,
++							 ios->vdd);
++			host->vdd_current = ios->vdd;
++		}
++		glamo_engine_enable(glamo_mci_def_pdata.pglamo,
++							GLAMO_ENGINE_MMC);
++		glamo_mci_reset(host);
++		break;
++
++	case MMC_POWER_OFF:
++	default:
++		if (host->power_mode_current == MMC_POWER_OFF)
++			break;
++		glamo_engine_disable(glamo_mci_def_pdata.pglamo,
++				     GLAMO_ENGINE_MMC);
++		host->pdata->glamo_set_mci_power(MMC_POWER_OFF, 0);
++		host->vdd_current = -1;
++		break;
++	}
++	host->power_mode_current = ios->power_mode;
++
++	 /* Set clock */
++/*	if (ios->clock) { */
++	for (mci_psc = 0; mci_psc < 256; mci_psc++) {
++		host->real_rate = host->clk_rate / (mci_psc + 1);
++		if (host->real_rate <= ios->clock)
++			break;
++	}
++	if (mci_psc > 255)
++		mci_psc = 255;
++	host->clk_div = mci_psc;
++	/* set the nearest prescaler factor
++	*
++	* register shared with SCLK divisor -- no chance of race because
++	* we don't use sensor interface
++	*/
++	writew_dly((readw(glamo_mci_def_pdata.pglamo->base +
++			GLAMO_REG_CLOCK_GEN8) & 0xff00) | host->clk_div,
++		glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN8);
++	/* enable clock to divider input */
++	writew_dly(readw(glamo_mci_def_pdata.pglamo->base +
++		GLAMO_REG_CLOCK_GEN5_1) | GLAMO_CLOCK_GEN51_EN_DIV_TCLK,
++		glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1);
++#if 0
++	} else { /* stop clock */
++		host->real_rate = 0;
++		/* remove clock from divider input */
++		writew(readw(glamo_mci_def_pdata.pglamo->base +
++		     GLAMO_REG_CLOCK_GEN5_1) & (~GLAMO_CLOCK_GEN51_EN_DIV_TCLK),
++		     glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1);
++	}
++#endif
++	if ((ios->power_mode == MMC_POWER_ON) ||
++	    (ios->power_mode == MMC_POWER_UP)) {
++		dev_info(&host->pdev->dev,
++			"powered (vdd = %d) clk: %lukHz div=%d (req: %ukHz). "
++			"Bus width=%d\n",ios->vdd,
++			host->real_rate / 1000, mci_psc,
++			ios->clock / 1000, ios->bus_width);
++	} else
++		dev_info(&host->pdev->dev, "glamo_mci_set_ios: power down.\n");
++
++	/* set bus width */
++	host->bus_width = ios->bus_width;
++	if (host->bus_width == MMC_BUS_WIDTH_4)
++		n = GLAMO_BASIC_MMC_EN_4BIT_DATA;
++	writew_dly((readw_dly(host->base + GLAMO_REG_MMC_BASIC) &
++					  (~GLAMO_BASIC_MMC_EN_4BIT_DATA)) | n,
++					      host->base + GLAMO_REG_MMC_BASIC);
++}
++
++
++/*
++ * no physical write protect supported by us
++ */
++static int glamo_mci_get_ro(struct mmc_host *mmc)
++{
++	return 0;
++}
++
++static struct mmc_host_ops glamo_mci_ops = {
++	.request	= glamo_mci_request,
++	.set_ios	= glamo_mci_set_ios,
++	.get_ro		= glamo_mci_get_ro,
++};
++
++static int glamo_mci_probe(struct platform_device *pdev)
++{
++	struct mmc_host 	*mmc;
++	struct glamo_mci_host 	*host;
++	int ret;
++
++	dev_info(&pdev->dev, "glamo_mci driver (C)2007 OpenMoko, Inc\n");
++
++	mmc = mmc_alloc_host(sizeof(struct glamo_mci_host), &pdev->dev);
++	if (!mmc) {
++		ret = -ENOMEM;
++		goto probe_out;
++	}
++
++	host = mmc_priv(mmc);
++	host->mmc = mmc;
++	host->pdev = pdev;
++	host->pdata = &glamo_mci_def_pdata;
++	host->power_mode_current = MMC_POWER_OFF;
++
++	host->complete_what = COMPLETION_NONE;
++	host->pio_active = XFER_NONE;
++
++	spin_lock_init(&host->complete_lock);
++
++	host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	if (!host->mem) {
++		dev_err(&pdev->dev,
++			"failed to get io memory region resouce.\n");
++
++		ret = -ENOENT;
++		goto probe_free_host;
++	}
++
++	host->mem = request_mem_region(host->mem->start,
++		RESSIZE(host->mem), pdev->name);
++
++	if (!host->mem) {
++		dev_err(&pdev->dev, "failed to request io memory region.\n");
++		ret = -ENOENT;
++		goto probe_free_host;
++	}
++
++	host->base = ioremap(host->mem->start, RESSIZE(host->mem));
++	if (!host->base) {
++		dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");
++		ret = -EINVAL;
++		goto probe_free_mem_region;
++	}
++
++	/* set the handler for our bit of the shared chip irq register */
++	set_irq_handler(IRQ_GLAMO(GLAMO_IRQIDX_MMC), glamo_mci_irq);
++	/* stash host as our handler's private data */
++	set_irq_data(IRQ_GLAMO(GLAMO_IRQIDX_MMC), host);
++
++	/* Get ahold of our data buffer we use for data in and out on MMC */
++	host->mem_data = platform_get_resource(pdev, IORESOURCE_MEM, 1);
++	if (!host->mem_data) {
++		dev_err(&pdev->dev,
++			"failed to get io memory region resource.\n");
++		ret = -ENOENT;
++		goto probe_iounmap;
++	}
++
++	host->mem_data = request_mem_region(host->mem_data->start,
++		RESSIZE(host->mem_data), pdev->name);
++
++	if (!host->mem_data) {
++		dev_err(&pdev->dev, "failed to request io memory region.\n");
++		ret = -ENOENT;
++		goto probe_iounmap;
++	}
++	host->base_data = ioremap(host->mem_data->start,
++					  RESSIZE(host->mem_data));
++	host->data_max_size = RESSIZE(host->mem_data);
++
++	if (host->base_data == 0) {
++		dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");
++		ret = -EINVAL;
++		goto probe_free_mem_region_data;
++	}
++
++	host->vdd_current = 0;
++	host->clk_rate = 50000000; /* really it's 49152000 */
++	host->clk_div = 16;
++
++	/* explain our host controller capabilities */
++	mmc->ops 	= &glamo_mci_ops;
++	mmc->ocr_avail	= host->pdata->ocr_avail;
++	mmc->caps	= MMC_CAP_4_BIT_DATA |
++			  MMC_CAP_MULTIWRITE |
++			  MMC_CAP_MMC_HIGHSPEED |
++			  MMC_CAP_SD_HIGHSPEED;
++	mmc->f_min 	= host->clk_rate / 256;
++	/*
++	 * held at /4 due to concerns of 100R recommended series resistor
++	 * allows 16MHz @ 4-bit --> 8MBytes/sec raw
++	 */
++	mmc->f_max 	= host->clk_rate / 3;
++
++	mmc->max_blk_count	= (1 << 16) - 1; /* GLAMO_REG_MMC_RB_BLKCNT */
++	mmc->max_blk_size	= (1 << 12) - 1; /* GLAMO_REG_MMC_RB_BLKLEN */
++	mmc->max_req_size	= RESSIZE(host->mem_data) / 2;
++	mmc->max_seg_size	= mmc->max_req_size;
++	mmc->max_phys_segs	= 1; /* hw doesn't talk about segs??? */
++	mmc->max_hw_segs	= 1;
++
++	dev_info(&host->pdev->dev, "probe: mapped mci_base:%p irq:%u.\n",
++		host->base, host->irq);
++
++	if ((ret = mmc_add_host(mmc))) {
++		dev_err(&pdev->dev, "failed to add mmc host.\n");
++		goto probe_free_mem_region_data;
++	}
++
++	platform_set_drvdata(pdev, mmc);
++
++	dev_info(&pdev->dev,"initialisation done.\n");
++	return 0;
++
++ probe_free_mem_region_data:
++	release_mem_region(host->mem_data->start, RESSIZE(host->mem_data));
++
++ probe_iounmap:
++	iounmap(host->base);
++
++ probe_free_mem_region:
++	release_mem_region(host->mem->start, RESSIZE(host->mem));
++
++ probe_free_host:
++	mmc_free_host(mmc);
++ probe_out:
++	return ret;
++}
++
++static int glamo_mci_remove(struct platform_device *pdev)
++{
++	struct mmc_host 	*mmc  = platform_get_drvdata(pdev);
++	struct glamo_mci_host 	*host = mmc_priv(mmc);
++
++	mmc_remove_host(mmc);
++	/* stop using our handler, revert it to default */
++	set_irq_handler(IRQ_GLAMO(GLAMO_IRQIDX_MMC), handle_level_irq);
++	iounmap(host->base);
++	iounmap(host->base_data);
++	release_mem_region(host->mem->start, RESSIZE(host->mem));
++	release_mem_region(host->mem_data->start, RESSIZE(host->mem_data));
++	mmc_free_host(mmc);
++
++	glamo_engine_disable(glamo_mci_def_pdata.pglamo, GLAMO_ENGINE_MMC);
++	return 0;
++}
++
++
++#ifdef CONFIG_PM
++
++static int glamo_mci_suspend(struct platform_device *dev, pm_message_t state)
++{
++	struct mmc_host *mmc = platform_get_drvdata(dev);
++
++	return  mmc_suspend_host(mmc, state);
++}
++
++static int glamo_mci_resume(struct platform_device *dev)
++{
++	struct mmc_host *mmc = platform_get_drvdata(dev);
++
++	return mmc_resume_host(mmc);
++}
++
++#else /* CONFIG_PM */
++#define glamo_mci_suspend NULL
++#define glamo_mci_resume NULL
++#endif /* CONFIG_PM */
++
++
++static struct platform_driver glamo_mci_driver =
++{
++	.driver.name	= "glamo-mci",
++	.probe		= glamo_mci_probe,
++	.remove		= glamo_mci_remove,
++	.suspend	= glamo_mci_suspend,
++	.resume		= glamo_mci_resume,
++};
++
++static int __init glamo_mci_init(void)
++{
++	platform_driver_register(&glamo_mci_driver);
++	return 0;
++}
++
++static void __exit glamo_mci_exit(void)
++{
++	platform_driver_unregister(&glamo_mci_driver);
++}
++
++module_init(glamo_mci_init);
++module_exit(glamo_mci_exit);
++
++MODULE_DESCRIPTION("Glamo MMC/SD Card Interface driver");
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Andy Green <andy at openmoko.com>");
+Index: linux-2.6.24-rc7/drivers/mfd/glamo/glamo-mci.h
+===================================================================
+--- /dev/null
++++ linux-2.6.24-rc7/drivers/mfd/glamo/glamo-mci.h
+@@ -0,0 +1,76 @@
++/*
++ *  linux/drivers/mmc/host/glamo-mmc.h - GLAMO MCI driver
++ *
++ *  Copyright (C) 2007-2008 OpenMoko, Inc, Andy Green <andy at openmoko.com>
++ *   based on S3C MMC driver -->
++ *  Copyright (C) 2004-2006 Thomas Kleffel, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++
++enum glamo_mci_waitfor {
++	COMPLETION_NONE,
++	COMPLETION_FINALIZE,
++	COMPLETION_CMDSENT,
++	COMPLETION_RSPFIN,
++	COMPLETION_XFERFINISH,
++	COMPLETION_XFERFINISH_RSPFIN,
++};
++
++struct glamo_mci_host {
++	struct platform_device	*pdev;
++	struct glamo_mci_pdata  *pdata;
++	struct mmc_host		*mmc;
++	struct resource		*mem;
++	struct resource		*mem_data;
++	struct clk		*clk;
++	void __iomem		*base;
++	u16 __iomem		*base_data;
++	int			irq;
++	int			irq_cd;
++	int			dma;
++	int 			data_max_size;
++
++	int			power_mode_current;
++	unsigned int		vdd_current;
++
++	unsigned long		clk_rate;
++	unsigned long		clk_div;
++	unsigned long		real_rate;
++	u8			prescaler;
++
++	unsigned		sdiimsk;
++	int			dodma;
++
++	volatile int		dmatogo;
++
++	struct mmc_request	*mrq;
++	int			cmd_is_stop;
++
++	spinlock_t		complete_lock;
++	volatile enum glamo_mci_waitfor
++				complete_what;
++
++	volatile int		dma_complete;
++
++	volatile u32		pio_sgptr;
++	volatile u32		pio_words;
++	volatile u32		pio_count;
++	volatile u16		*pio_ptr;
++#define XFER_NONE 0
++#define XFER_READ 1
++#define XFER_WRITE 2
++	volatile u32		pio_active;
++
++	int			bus_width;
++
++	char 			dbgmsg_cmd[301];
++	char 			dbgmsg_dat[301];
++	volatile char		*status;
++
++	unsigned int		ccnt, dcnt;
++	struct tasklet_struct	pio_tasklet;
++};
+Index: linux-2.6.24-rc7/drivers/mfd/glamo/glamo-regs.h
+===================================================================
+--- linux-2.6.24-rc7.orig/drivers/mfd/glamo/glamo-regs.h
++++ linux-2.6.24-rc7/drivers/mfd/glamo/glamo-regs.h
+@@ -150,16 +150,28 @@
+ 	GLAMO_MEM_DRAM2_DEEP_PWRDOWN	= (1 << 12),
+ };
+ 
++enum glamo_irq_index {
++	GLAMO_IRQIDX_HOSTBUS	= 0,
++	GLAMO_IRQIDX_JPEG	= 1,
++	GLAMO_IRQIDX_MPEG	= 2,
++	GLAMO_IRQIDX_MPROC1	= 3,
++	GLAMO_IRQIDX_MPROC0	= 4,
++	GLAMO_IRQIDX_CMDQUEUE	= 5,
++	GLAMO_IRQIDX_2D		= 6,
++	GLAMO_IRQIDX_MMC	= 7,
++	GLAMO_IRQIDX_RISC	= 8,
++};
++
+ enum glamo_irq {
+-	GLAMO_IRQ_HOSTBUS	= 0x0001,
+-	GLAMO_IRQ_JPEG		= 0x0002,
+-	GLAMO_IRQ_MPEG		= 0x0004,
+-	GLAMO_IRQ_MPROC1	= 0x0008,
+-	GLAMO_IRQ_MPROC0	= 0x0010,
+-	GLAMO_IRQ_CMDQUEUE	= 0x0020,
+-	GLAMO_IRQ_2D		= 0x0040,
+-	GLAMO_IRQ_MMC		= 0x0080,
+-	GLAMO_IRQ_RISC		= 0x0100,
++	GLAMO_IRQ_HOSTBUS	= (1 << GLAMO_IRQIDX_HOSTBUS),
++	GLAMO_IRQ_JPEG		= (1 << GLAMO_IRQIDX_JPEG),
++	GLAMO_IRQ_MPEG		= (1 << GLAMO_IRQIDX_MPEG),
++	GLAMO_IRQ_MPROC1	= (1 << GLAMO_IRQIDX_MPROC1),
++	GLAMO_IRQ_MPROC0	= (1 << GLAMO_IRQIDX_MPROC0),
++	GLAMO_IRQ_CMDQUEUE	= (1 << GLAMO_IRQIDX_CMDQUEUE),
++	GLAMO_IRQ_2D		= (1 << GLAMO_IRQIDX_2D),
++	GLAMO_IRQ_MMC		= (1 << GLAMO_IRQIDX_MMC),
++	GLAMO_IRQ_RISC		= (1 << GLAMO_IRQIDX_RISC),
+ };
+ 
+ enum glamo_reg_clock_host {
+@@ -197,6 +209,145 @@
+ 	GLAMO_CLOCK_MMC_RESET		= 0x1000,
+ };
+ 
++enum glamo_reg_basic_mmc {
++	/* set to disable CRC error rejection */
++	GLAMO_BASIC_MMC_DISABLE_CRC	= 0x0001,
++	/* enable completion interrupt */
++	GLAMO_BASIC_MMC_EN_COMPL_INT	= 0x0002,
++	/* stop MMC clock while enforced idle waiting for data from card */
++	GLAMO_BASIC_MMC_NO_CLK_RD_WAIT	= 0x0004,
++	/* 0 = 1-bit bus to card, 1 = use 4-bit bus (has to be negotiated) */
++	GLAMO_BASIC_MMC_EN_4BIT_DATA	= 0x0008,
++	/* enable 75K pullups on D3..D0 */
++	GLAMO_BASIC_MMC_EN_DATA_PUPS	= 0x0010,
++	/* enable 75K pullup on CMD */
++	GLAMO_BASIC_MMC_EN_CMD_PUP	= 0x0020,
++	/* IO drive strength 00=weak -> 11=strongest */
++	GLAMO_BASIC_MMC_EN_DR_STR0	= 0x0040,
++	GLAMO_BASIC_MMC_EN_DR_STR1	= 0x0080,
++	/* TCLK delay stage A, 0000 = 500ps --> 1111 = 8ns */
++	GLAMO_BASIC_MMC_EN_TCLK_DLYA0	= 0x0100,
++	GLAMO_BASIC_MMC_EN_TCLK_DLYA1	= 0x0200,
++	GLAMO_BASIC_MMC_EN_TCLK_DLYA2	= 0x0400,
++	GLAMO_BASIC_MMC_EN_TCLK_DLYA3	= 0x0800,
++	/* TCLK delay stage B (cumulative), 0000 = 500ps --> 1111 = 8ns */
++	GLAMO_BASIC_MMC_EN_TCLK_DLYB0	= 0x1000,
++	GLAMO_BASIC_MMC_EN_TCLK_DLYB1	= 0x2000,
++	GLAMO_BASIC_MMC_EN_TCLK_DLYB2	= 0x4000,
++	GLAMO_BASIC_MMC_EN_TCLK_DLYB3	= 0x8000,
++};
++
++enum glamo_reg_stat1_mmc {
++	/* command "counter" (really: toggle) */
++	GLAMO_STAT1_MMC_CMD_CTR	= 0x8000,
++	/* engine is idle */
++	GLAMO_STAT1_MMC_IDLE	= 0x4000,
++	/* readback response is ready */
++	GLAMO_STAT1_MMC_RB_RRDY	= 0x0200,
++	/* readback data is ready */
++	GLAMO_STAT1_MMC_RB_DRDY	= 0x0100,
++	/* no response timeout */
++	GLAMO_STAT1_MMC_RTOUT	= 0x0020,
++	/* no data timeout */
++	GLAMO_STAT1_MMC_DTOUT	= 0x0010,
++	/* CRC error on block write */
++	GLAMO_STAT1_MMC_BWERR	= 0x0004,
++	/* CRC error on block read */
++	GLAMO_STAT1_MMC_BRERR	= 0x0002
++};
++
++enum glamo_reg_fire_mmc {
++	/* command "counter" (really: toggle)
++	 * the STAT1 register reflects this so you can ensure you don't look
++	 * at status for previous command
++	 */
++	GLAMO_FIRE_MMC_CMD_CTR	= 0x8000,
++	/* sets kind of response expected */
++	GLAMO_FIRE_MMC_RES_MASK	= 0x0700,
++	/* sets command type */
++	GLAMO_FIRE_MMC_TYP_MASK	= 0x00C0,
++	/* sets command class */
++	GLAMO_FIRE_MMC_CLS_MASK	= 0x000F,
++};
++
++enum glamo_fire_mmc_response_types {
++	GLAMO_FIRE_MMC_RSPT_R1	= 0x0000,
++	GLAMO_FIRE_MMC_RSPT_R1b	= 0x0100,
++	GLAMO_FIRE_MMC_RSPT_R2	= 0x0200,
++	GLAMO_FIRE_MMC_RSPT_R3	= 0x0300,
++	GLAMO_FIRE_MMC_RSPT_R4	= 0x0400,
++	GLAMO_FIRE_MMC_RSPT_R5	= 0x0500,
++};
++
++enum glamo_fire_mmc_command_types {
++	/* broadcast, no response */
++	GLAMO_FIRE_MMC_CMDT_BNR	= 0x0000,
++	/* broadcast, with response */
++	GLAMO_FIRE_MMC_CMDT_BR	= 0x0040,
++	/* addressed, no data */
++	GLAMO_FIRE_MMC_CMDT_AND	= 0x0080,
++	/* addressed, with data */
++	GLAMO_FIRE_MMC_CMDT_AD	= 0x00C0,
++};
++
++enum glamo_fire_mmc_command_class {
++	/* "Stream Read" */
++	GLAMO_FIRE_MMC_CC_STRR	= 0x0000,
++	/* Single Block Read */
++	GLAMO_FIRE_MMC_CC_SBR	= 0x0001,
++	/* Multiple Block Read With Stop */
++	GLAMO_FIRE_MMC_CC_MBRS	= 0x0002,
++	/* Multiple Block Read No Stop */
++	GLAMO_FIRE_MMC_CC_MBRNS	= 0x0003,
++	/* RESERVED for "Stream Write" */
++	GLAMO_FIRE_MMC_CC_STRW	= 0x0004,
++	/* "Stream Write" */
++	GLAMO_FIRE_MMC_CC_SBW	= 0x0005,
++	/* RESERVED for Multiple Block Write With Stop */
++	GLAMO_FIRE_MMC_CC_MBWS	= 0x0006,
++	/* Multiple Block Write No Stop */
++	GLAMO_FIRE_MMC_CC_MBWNS	= 0x0007,
++	/* STOP command */
++	GLAMO_FIRE_MMC_CC_STOP	= 0x0008,
++	/* Cancel on Running Command */
++	GLAMO_FIRE_MMC_CC_CANCL	= 0x0009,
++	/* "Basic Command" */
++	GLAMO_FIRE_MMC_CC_BASIC	= 0x000a,
++};
++
++/* these are offsets from the start of the MMC register region */
++enum glamo_register_mmc {
++	/* MMC command, b15..8 = cmd arg b7..0; b7..1 = CRC; b0 = end bit */
++	GLAMO_REG_MMC_CMD_REG1	= 0x00,
++	/* MMC command, b15..0 = cmd arg b23 .. 8 */
++	GLAMO_REG_MMC_CMD_REG2	= 0x02,
++	/* MMC command, b15=start, b14=transmission,
++	 * b13..8=cmd idx, b7..0=cmd arg b31..24
++	 */
++	GLAMO_REG_MMC_CMD_REG3	= 0x04,
++	GLAMO_REG_MMC_CMD_FIRE	= 0x06,
++	GLAMO_REG_MMC_CMD_RSP1	= 0x10,
++	GLAMO_REG_MMC_CMD_RSP2	= 0x12,
++	GLAMO_REG_MMC_CMD_RSP3	= 0x14,
++	GLAMO_REG_MMC_CMD_RSP4	= 0x16,
++	GLAMO_REG_MMC_CMD_RSP5	= 0x18,
++	GLAMO_REG_MMC_CMD_RSP6	= 0x1a,
++	GLAMO_REG_MMC_CMD_RSP7	= 0x1c,
++	GLAMO_REG_MMC_CMD_RSP8	= 0x1e,
++	GLAMO_REG_MMC_RB_STAT1	= 0x20,
++	GLAMO_REG_MMC_RB_BLKCNT	= 0x22,
++	GLAMO_REG_MMC_RB_BLKLEN	= 0x24,
++	GLAMO_REG_MMC_BASIC	= 0x30,
++	GLAMO_REG_MMC_RDATADS1	= 0x34,
++	GLAMO_REG_MMC_RDATADS2	= 0x36,
++	GLAMO_REG_MMC_WDATADS1	= 0x38,
++	GLAMO_REG_MMC_WDATADS2	= 0x3a,
++	GLAMO_REG_MMC_DATBLKCNT	= 0x3c,
++	GLAMO_REG_MMC_DATBLKLEN	= 0x3e,
++	GLAMO_REG_MMC_TIMEOUT	= 0x40,
++
++};
++
+ enum glamo_reg_clock_isp {
+ 	GLAMO_CLOCK_ISP_DG_I1CLK	= 0x0001,
+ 	GLAMO_CLOCK_ISP_EN_I1CLK	= 0x0002,

Modified: branches/src/target/kernel/2.6.24.x/patches/series
===================================================================
--- branches/src/target/kernel/2.6.24.x/patches/series	2008-01-21 02:04:49 UTC (rev 3888)
+++ branches/src/target/kernel/2.6.24.x/patches/series	2008-01-21 02:19:30 UTC (rev 3889)
@@ -45,6 +45,7 @@
 # gta02 feature set
 pcf50633.patch
 smedia-glamo.patch
+glamo-mmc.patch
 gta02-core.patch
 gta02-sound.patch
 lis302dl.patch

Modified: branches/src/target/kernel/2.6.24.x/patches/smedia-glamo.patch
===================================================================
--- branches/src/target/kernel/2.6.24.x/patches/smedia-glamo.patch	2008-01-21 02:04:49 UTC (rev 3888)
+++ branches/src/target/kernel/2.6.24.x/patches/smedia-glamo.patch	2008-01-21 02:19:30 UTC (rev 3889)
@@ -1179,13 +1179,13 @@
 +	 * unreliability is not seen after 4GB of write / read testing
 +	 */
 +	{ GLAMO_REG_MEM_TIMING1,	0x05ad },
-+	{ GLAMO_REG_MEM_TIMING2,	0x0010 }, /* Taa = 3 MCLK */
++	{ GLAMO_REG_MEM_TIMING2,	0x0011 }, /* Taa = 3 MCLK */
 +	{ GLAMO_REG_MEM_TIMING3,	0x0000 },
 +	{ GLAMO_REG_MEM_TIMING4,	0x0000 }, /* CE1# delay fall/rise */
-+	{ GLAMO_REG_MEM_TIMING5,	0x0000 },
-+	{ GLAMO_REG_MEM_TIMING6,	0x0000 },
-+	{ GLAMO_REG_MEM_TIMING7,	0x0000 },
-+	{ GLAMO_REG_MEM_TIMING8,	0x1000 },
++	{ GLAMO_REG_MEM_TIMING5,	0x0000 }, /* UB# LB# */
++	{ GLAMO_REG_MEM_TIMING6,	0x0000 }, /* OE# */
++	{ GLAMO_REG_MEM_TIMING7,	0x0000 }, /* WE# */
++	{ GLAMO_REG_MEM_TIMING8,	0x1008 }, /* MCLK delay, was 0x1000 */
 +	{ GLAMO_REG_MEM_TIMING9,	0x6000 },
 +	{ GLAMO_REG_MEM_TIMING10,	0x00ff },
 +	{ GLAMO_REG_MEM_TIMING11,	0x0000 },





More information about the commitlog mailing list