linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] New AT91 MCI Driver that supports both MCI slots used at the  same time
@ 2009-05-28 21:40 Rob Emanuele
  2009-05-29 15:27 ` Joey Oravec
  2009-06-02  8:49 ` [PATCH] " Nicolas Ferre
  0 siblings, 2 replies; 11+ messages in thread
From: Rob Emanuele @ 2009-05-28 21:40 UTC (permalink / raw)
  To: nicolas.ferre; +Cc: linux-arm-kernel, linux-kernel

Greetings,

This patch creates a new AT91 Multimedia Card Interface (MCI) driver
that supports using both MCI slots at the same time.  I'm looking for
others to test this patch on other boards within the same family of
chips.

This driver is a port the Atmel AVR32 MCI driver which uses similar silicon.

In addition it modifies the AT91SAM9G20-EK's board platform config to
support having MCI Slot A hooked to an SD Slot.  This was the board
that this was developed on. To support MCI Slot A it was needed to not
enable the LEDs on the AT91SAM9G20-EK board if SD/MMC Slot A is
enabled.  The Slot A pins overlap the LEDs on Rev A and B boards.

Please comment and try it out,

Rob Emanuele

--

diff --git a/arch/arm/mach-at91/at91sam9260_devices.c
b/arch/arm/mach-at91/at91sam9260_devices.c
index d74c9ac..258fb9e 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -275,8 +275,103 @@ void __init at91_add_device_mmc(short mmc_id,
struct at91_mmc_data *data)
 	platform_device_register(&at91sam9260_mmc_device);
 }
 #else
+
+/* --------------------------------------------------------------------
+ *  MMC / SD Slot for 2nd Gen Driver
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_MMC_AT91GEN2) || defined(CONFIG_MMC_AT91GEN2_MODULE)
+static u64 mmc_dmamask = DMA_BIT_MASK(32);
+static struct at91_mmc_data mmc_data;
+
+static struct resource mmc_resources[] = {
+	[0] = {
+		.start	= AT91SAM9260_BASE_MCI,
+		.end	= AT91SAM9260_BASE_MCI + SZ_16K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= AT91SAM9260_ID_MCI,
+		.end	= AT91SAM9260_ID_MCI,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device at91sam9260_mmc_device = {
+	.name		= "at91_mci",
+	.id		= -1,
+	.dev		= {
+				.dma_mask		= &mmc_dmamask,
+				.coherent_dma_mask	= DMA_BIT_MASK(32),
+				.platform_data		= &mmc_data,
+	},
+	.resource	= mmc_resources,
+	.num_resources	= ARRAY_SIZE(mmc_resources),
+};
+
+void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data)
+{
+        unsigned int i;
+        unsigned int slot_count = 0;
+
+	if (!data)
+		return;
+
+        for (i = 0; i < AT91_MCI_MAX_NR_SLOTS; i++) {
+                if (data->slot[i].bus_width) {
+                        /* input/irq */
+                        if (data->slot[i].detect_pin) {
+
at91_set_gpio_input(data->slot[i].detect_pin, 1);
+                                at91_set_deglitch(data->slot[i].detect_pin, 1);
+                }
+                if (data->slot[i].wp_pin)
+                        at91_set_gpio_input(data->slot[i].wp_pin, 1);
+                if (data->slot[i].vcc_pin)
+                        at91_set_gpio_output(data->slot[i].vcc_pin, 0);
+
+                switch(i) {
+                case 0:
+                        /* CMD */
+                        at91_set_A_periph(AT91_PIN_PA7, 1);
+                        /* DAT0, maybe DAT1..DAT3 */
+                        at91_set_A_periph(AT91_PIN_PA6, 1);
+                        if (data->slot[i].bus_width == 4) {
+                                at91_set_A_periph(AT91_PIN_PA9, 1);
+                                at91_set_A_periph(AT91_PIN_PA10, 1);
+                                at91_set_A_periph(AT91_PIN_PA11, 1);
+                        }
+                        break;
+                case 1:
+                        /* CMD */
+                        at91_set_B_periph(AT91_PIN_PA1, 1);
+                        /* DAT0, maybe DAT1..DAT3 */
+                        at91_set_B_periph(AT91_PIN_PA0, 1);
+                        if (data->slot[i].bus_width == 4) {
+                                at91_set_B_periph(AT91_PIN_PA5, 1);
+                                at91_set_B_periph(AT91_PIN_PA4, 1);
+                                at91_set_B_periph(AT91_PIN_PA3, 1);
+                        }
+                        break;
+                default:
+                    break;
+                };
+                slot_count++;
+                }
+        }
+
+        if (slot_count) {
+                /* CLK */
+                at91_set_A_periph(AT91_PIN_PA8, 0);
+
+                mmc_data = *data;
+                platform_device_register(&at91sam9260_mmc_device);
+        }
+}
+#else
 void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {}
 #endif
+#endif
+


 /* --------------------------------------------------------------------
diff --git a/arch/arm/mach-at91/board-sam9g20ek.c
b/arch/arm/mach-at91/board-sam9g20ek.c
index 81439fe..c0d8bb7 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -89,7 +89,7 @@ static struct at91_udc_data __initdata ek_udc_data = {
  * SPI devices.
  */
 static struct spi_board_info ek_spi_devices[] = {
-#if !defined(CONFIG_MMC_AT91)
+#if !defined(CONFIG_MMC_AT91) && !defined(CONFIG_MMC_AT91GEN2)
 	{	/* DataFlash chip */
 		.modalias	= "mtd_dataflash",
 		.chip_select	= 1,
@@ -195,11 +195,26 @@ static void __init ek_add_device_nand(void)
  * MCI (SD/MMC)
  * det_pin, wp_pin and vcc_pin are not connected
  */
+#if defined(CONFIG_MMC_AT91) || defined(CONFIG_MMC_AT91_MODULE)
 static struct at91_mmc_data __initdata ek_mmc_data = {
 	.slot_b		= 1,
 	.wire4		= 1,
 };
-
+#endif
+#if defined(CONFIG_MMC_AT91GEN2) || defined(CONFIG_MMC_AT91GEN2_MODULE)
+static struct at91_mmc_data __initdata ek_mmc_data = {
+	.slot[0] = {
+		.bus_width	= 4,
+		.detect_pin	= -ENODEV,
+		.wp_pin		= -ENODEV,
+	},
+	.slot[1] = {
+		.bus_width	= 4,
+		.detect_pin	= -ENODEV,
+		.wp_pin		= -ENODEV,
+	},
+};
+#endif

 /*
  * LEDs
@@ -237,7 +252,15 @@ static void __init ek_board_init(void)
 	/* I2C */
 	at91_add_device_i2c(NULL, 0);
 	/* LEDs */
-	at91_gpio_leds(ek_leds, ARRAY_SIZE(ek_leds));
+        /* If Slot A is enabled, disable the LEDs */
+#if defined(CONFIG_MMC_AT91) || defined(CONFIG_MMC_AT91_MODULE)
+        if (ek_mmc_data.slot_b == 1) {
+#endif
+#if defined(CONFIG_MMC_AT91GEN2) || defined(CONFIG_MMC_AT91GEN2_MODULE)
+	if (ek_mmc_data.slot[0].bus_width == 0) {
+#endif
+                at91_gpio_leds(ek_leds, ARRAY_SIZE(ek_leds));
+        }
 }

 MACHINE_START(AT91SAM9G20EK, "Atmel AT91SAM9G20-EK")
diff --git a/arch/arm/mach-at91/include/mach/at91_mci.h
b/arch/arm/mach-at91/include/mach/at91_mci.h
index 550d503..c9021ea 100644
--- a/arch/arm/mach-at91/include/mach/at91_mci.h
+++ b/arch/arm/mach-at91/include/mach/at91_mci.h
@@ -35,7 +35,9 @@

 #define AT91_MCI_DTOR		0x08		/* Data Timeout Register */
 #define		AT91_MCI_DTOCYC		(0xf << 0)	/* Data Timeout Cycle Number */
+#define		AT91_MCI_DTOCYC_F(n)	((0xf & (n)) << 0)	/* Data Timeout
Cycle Number Function*/
 #define		AT91_MCI_DTOMUL		(7   << 4)	/* Data Timeout Multiplier */
+#define		AT91_MCI_DTOMUL_F(n)	(((n) & 0x7) << 4)	/* Data Timeout
Multiplier Function*/
 #define		AT91_MCI_DTOMUL_1		(0 <<  4)
 #define		AT91_MCI_DTOMUL_16		(1 <<  4)
 #define		AT91_MCI_DTOMUL_128		(2 <<  4)
@@ -80,8 +82,8 @@
 #define		AT91_MCI_BLKR_BLKLEN(n)	((0xffff & (n)) << 16)	/* Block lenght */

 #define AT91_MCI_RSPR(n)	(0x20 + ((n) * 4))	/* Response Registers 0-3 */
-#define AT91_MCR_RDR		0x30		/* Receive Data Register */
-#define AT91_MCR_TDR		0x34		/* Transmit Data Register */
+#define AT91_MCI_RDR		0x30		/* Receive Data Register */
+#define AT91_MCI_TDR		0x34		/* Transmit Data Register */

 #define AT91_MCI_SR		0x40		/* Status Register */
 #define		AT91_MCI_CMDRDY		(1 <<  0)	/* Command Ready */
@@ -110,4 +112,6 @@
 #define AT91_MCI_IDR		0x48		/* Interrupt Disable Register */
 #define AT91_MCI_IMR		0x4c		/* Interrupt Mask Register */

+#define AT91_MCI_REGS_SIZE      0x100
+
 #endif
diff --git a/arch/arm/mach-at91/include/mach/board.h
b/arch/arm/mach-at91/include/mach/board.h
index 793fe7b..831e5b3 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -63,6 +63,7 @@ struct at91_cf_data {
 extern void __init at91_add_device_cf(struct at91_cf_data *data);

  /* MMC / SD */
+#if defined(CONFIG_MMC_AT91) || defined(CONFIG_MMC_AT91_MODULE)
 struct at91_mmc_data {
 	u8		det_pin;	/* card detect IRQ */
 	unsigned	slot_b:1;	/* uses Slot B */
@@ -70,6 +71,25 @@ struct at91_mmc_data {
 	u8		wp_pin;		/* (SD) writeprotect detect */
 	u8		vcc_pin;	/* power switching (high == on) */
 };
+#endif
+
+#if defined(CONFIG_MMC_AT91GEN2) || defined(CONFIG_MMC_AT91GEN2_MODULE)
+#define AT91_MCI_MAX_NR_SLOTS  2
+
+struct dma_slave;
+
+struct at91_mmc_slot_pdata {
+        unsigned int            bus_width;
+        int                     detect_pin;
+        int                     wp_pin;
+        int                     vcc_pin;
+};
+
+struct at91_mmc_data {
+        struct dma_slave        *dma_slave;
+        struct at91_mmc_slot_pdata   slot[AT91_MCI_MAX_NR_SLOTS];
+};
+#endif
 extern void __init at91_add_device_mmc(short mmc_id, struct
at91_mmc_data *data);

  /* Ethernet (EMAC & MACB) */
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 99d4b28..ae6c746 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -114,6 +114,16 @@ config MMC_AT91

 	  If unsure, say N.

+config MMC_AT91GEN2
+	tristate "Atmel AT91 Multimedia Card Interface support 2nd gen"
+	depends on ARCH_AT91
+	help
+	  This selects the Atmel Multimedia Card Interface driver. If
+	  you have an AT91 platform with a Multimedia Card slot,
+	  say Y or M here.
+
+	  If unsure, say N.
+
 config MMC_ATMELMCI
 	tristate "Atmel Multimedia Card Interface support"
 	depends on AVR32
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index dedec55..752f018 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
 obj-$(CONFIG_MMC_OMAP)		+= omap.o
 obj-$(CONFIG_MMC_OMAP_HS)	+= omap_hsmmc.o
 obj-$(CONFIG_MMC_AT91)		+= at91_mci.o
+obj-$(CONFIG_MMC_AT91GEN2)	+= at91gen2-mci.o
 obj-$(CONFIG_MMC_ATMELMCI)	+= atmel-mci.o
 obj-$(CONFIG_MMC_TIFM_SD)	+= tifm_sd.o
 obj-$(CONFIG_MMC_SPI)		+= mmc_spi.o
diff --git a/drivers/mmc/host/at91gen2-mci.c b/drivers/mmc/host/at91gen2-mci.c
new file mode 100644
index 0000000..93b3ca5
--- /dev/null
+++ b/drivers/mmc/host/at91gen2-mci.c
@@ -0,0 +1,1579 @@
+/*
+ * Atmel MultiMedia Card Interface driver
+ *
+ * Copyright (C) 2004-2008 Atmel Corporation
+ * Copyright (C) 2009 Crystalfontz America
+ *
+ * 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/blkdev.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/seq_file.h>
+#include <linux/stat.h>
+
+#include <linux/mmc/host.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/gpio.h>
+#include <asm/unaligned.h>
+
+#include <asm/mach/mmc.h>
+#include <mach/board.h>
+#include <mach/cpu.h>
+#include <mach/at91_mci.h>
+
+#define DRIVER_NAME "at91_mci"
+
+#define AT91MCI_DATA_ERROR_FLAGS	(AT91_MCI_RINDE | AT91_MCI_RDIRE |
AT91_MCI_RCRCE	\
+                                 | AT91_MCI_RENDE | AT91_MCI_RTOE |
AT91_MCI_DCRCE \
+                                 | AT91_MCI_DTOE | AT91_MCI_OVRE |
AT91_MCI_UNRE)
+
+enum {
+	EVENT_CMD_COMPLETE = 0,
+	EVENT_XFER_COMPLETE,
+	EVENT_DATA_COMPLETE,
+	EVENT_DATA_ERROR,
+};
+
+enum at91_mci_state {
+	STATE_IDLE = 0,
+	STATE_SENDING_CMD,
+	STATE_SENDING_DATA,
+	STATE_DATA_BUSY,
+	STATE_SENDING_STOP,
+	STATE_DATA_ERROR,
+};
+
+struct at91_mci_dma {
+#ifdef CONFIG_MMC_AT91GEN2_DMA
+	struct dma_client		client;
+	struct dma_chan			*chan;
+	struct dma_async_tx_descriptor	*data_desc;
+#endif
+};
+
+/**
+ * struct at91_mci - MMC controller state shared between all slots
+ * @lock: Spinlock protecting the queue and associated data.
+ * @regs: Pointer to MMIO registers.
+ * @sg: Scatterlist entry currently being processed by PIO code, if any.
+ * @pio_offset: Offset into the current scatterlist entry.
+ * @cur_slot: The slot which is currently using the controller.
+ * @mrq: The request currently being processed on @cur_slot,
+ *	or NULL if the controller is idle.
+ * @cmd: The command currently being sent to the card, or NULL.
+ * @data: The data currently being transferred, or NULL if no data
+ *	transfer is in progress.
+ * @dma: DMA client state.
+ * @data_chan: DMA channel being used for the current data transfer.
+ * @cmd_status: Snapshot of SR taken upon completion of the current
+ *	command. Only valid when EVENT_CMD_COMPLETE is pending.
+ * @data_status: Snapshot of SR taken upon completion of the current
+ *	data transfer. Only valid when EVENT_DATA_COMPLETE or
+ *	EVENT_DATA_ERROR is pending.
+ * @stop_cmdr: Value to be loaded into CMDR when the stop command is
+ *	to be sent.
+ * @tasklet: Tasklet running the request state machine.
+ * @pending_events: Bitmask of events flagged by the interrupt handler
+ *	to be processed by the tasklet.
+ * @completed_events: Bitmask of events which the state machine has
+ *	processed.
+ * @state: Tasklet state.
+ * @queue: List of slots waiting for access to the controller.
+ * @need_clock_update: Update the clock rate before the next request.
+ * @need_reset: Reset controller before next request.
+ * @mode_reg: Value of the MR register.
+ * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
+ *	rate and timeout calculations.
+ * @mapbase: Physical address of the MMIO registers.
+ * @mck: The peripheral bus clock hooked up to the MMC controller.
+ * @pdev: Platform device associated with the MMC controller.
+ * @slot: Slots sharing this MMC controller.
+ *
+ * Locking
+ * =======
+ *
+ * @lock is a softirq-safe spinlock protecting @queue as well as
+ * @cur_slot, @mrq and @state. These must always be updated
+ * at the same time while holding @lock.
+ *
+ * @lock also protects mode_reg and need_clock_update since these are
+ * used to synchronize mode register updates with the queue
+ * processing.
+ *
+ * The @mrq field of struct at91_mci_slot is also protected by @lock,
+ * and must always be written at the same time as the slot is added to
+ * @queue.
+ *
+ * @pending_events and @completed_events are accessed using atomic bit
+ * operations, so they don't need any locking.
+ *
+ * None of the fields touched by the interrupt handler need any
+ * locking. However, ordering is important: Before EVENT_DATA_ERROR or
+ * EVENT_DATA_COMPLETE is set in @pending_events, all data-related
+ * interrupts must be disabled and @data_status updated with a
+ * snapshot of SR. Similarly, before EVENT_CMD_COMPLETE is set, the
+ * CMDRDY interupt must be disabled and @cmd_status updated with a
+ * snapshot of SR, and before EVENT_XFER_COMPLETE can be set, the
+ * bytes_xfered field of @data must be written. This is ensured by
+ * using barriers.
+ */
+struct at91_mci {
+	spinlock_t		lock;
+	void __iomem		*regs;
+
+	struct scatterlist	*sg;
+	unsigned int		pio_offset;
+
+	struct at91_mci_slot	*cur_slot;
+	struct mmc_request	*mrq;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+
+	struct at91_mci_dma	dma;
+	struct dma_chan		*data_chan;
+
+	u32			cmd_status;
+	u32			data_status;
+	u32			stop_cmdr;
+
+	struct tasklet_struct	tasklet;
+	unsigned long		pending_events;
+	unsigned long		completed_events;
+	enum at91_mci_state	state;
+	struct list_head	queue;
+
+	bool			need_clock_update;
+	bool			need_reset;
+	u32			mode_reg;
+	unsigned long		bus_hz;
+	unsigned long		mapbase;
+	struct clk		*mck;
+	struct platform_device	*pdev;
+
+	struct at91_mci_slot	*slot[AT91_MCI_MAX_NR_SLOTS];
+};
+
+/**
+ * struct at91_mci_slot - MMC slot state
+ * @mmc: The mmc_host representing this slot.
+ * @host: The MMC controller this slot is using.
+ * @sdc_reg: Value of SDCR to be written before using this slot.
+ * @mrq: mmc_request currently being processed or waiting to be
+ *	processed, or NULL when the slot is idle.
+ * @queue_node: List node for placing this node in the @queue list of
+ *	&struct at91_mci.
+ * @clock: Clock rate configured by set_ios(). Protected by host->lock.
+ * @flags: Random state bits associated with the slot.
+ * @detect_pin: GPIO pin used for card detection, or negative if not
+ *	available.
+ * @wp_pin: GPIO pin used for card write protect sending, or negative
+ *	if not available.
+ * @detect_timer: Timer used for debouncing @detect_pin interrupts.
+ */
+struct at91_mci_slot {
+	struct mmc_host		*mmc;
+	struct at91_mci	*host;
+
+	u32			sdc_reg;
+
+	struct mmc_request	*mrq;
+	struct list_head	queue_node;
+
+	unsigned int		clock;
+	unsigned long		flags;
+#define AT91MCI_CARD_PRESENT	0
+#define AT91MCI_CARD_NEED_INIT	1
+#define AT91MCI_SHUTDOWN		2
+
+	int			detect_pin;
+	int			wp_pin;
+
+	struct timer_list	detect_timer;
+};
+
+#define at91mci_test_and_clear_pending(host, event)		\
+	test_and_clear_bit(event, &host->pending_events)
+#define at91mci_set_completed(host, event)			\
+	set_bit(event, &host->completed_events)
+#define at91mci_set_pending(host, event)				\
+	set_bit(event, &host->pending_events)
+
+#define at91_mci_read(host, reg)	__raw_readl((host)->regs + (reg))
+#define at91_mci_write(host, reg, val)	__raw_writel((val),
(host)->regs + (reg))
+
+/*
+ * The debugfs stuff below is mostly optimized away when
+ * CONFIG_DEBUG_FS is not set.
+ */
+static int at91mci_req_show(struct seq_file *s, void *v)
+{
+	struct at91_mci_slot	*slot = s->private;
+	struct mmc_request	*mrq;
+	struct mmc_command	*cmd;
+	struct mmc_command	*stop;
+	struct mmc_data		*data;
+
+	/* Make sure we get a consistent snapshot */
+	spin_lock_bh(&slot->host->lock);
+	mrq = slot->mrq;
+
+	if (mrq) {
+		cmd = mrq->cmd;
+		data = mrq->data;
+		stop = mrq->stop;
+
+		if (cmd)
+			seq_printf(s,
+				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				cmd->opcode, cmd->arg, cmd->flags,
+				cmd->resp[0], cmd->resp[1], cmd->resp[2],
+				cmd->resp[2], cmd->error);
+		if (data)
+			seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
+				data->bytes_xfered, data->blocks,
+				data->blksz, data->flags, data->error);
+		if (stop)
+			seq_printf(s,
+				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				stop->opcode, stop->arg, stop->flags,
+				stop->resp[0], stop->resp[1], stop->resp[2],
+				stop->resp[2], stop->error);
+	}
+
+	spin_unlock_bh(&slot->host->lock);
+
+	return 0;
+}
+
+static int at91mci_req_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, at91mci_req_show, inode->i_private);
+}
+
+static const struct file_operations at91mci_req_fops = {
+	.owner		= THIS_MODULE,
+	.open		= at91mci_req_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void at91mci_show_status_reg(struct seq_file *s,
+		const char *regname, u32 value)
+{
+	static const char	*sr_bit[] = {
+		[0]	= "CMDRDY",
+		[1]	= "RXRDY",
+		[2]	= "TXRDY",
+		[3]	= "BLKE",
+		[4]	= "DTIP",
+		[5]	= "NOTBUSY",
+		[6]	= "ENDRX",
+		[7]	= "ENDTX",
+		[8]	= "SDIOIRQA",
+		[9]	= "SDIOIRQB",
+		[14]	= "RXBUFF",
+		[15]	= "TXBUFE",
+		[16]	= "RINDE",
+		[17]	= "RDIRE",
+		[18]	= "RCRCE",
+		[19]	= "RENDE",
+		[20]	= "RTOE",
+		[21]	= "DCRCE",
+		[22]	= "DTOE",
+		[30]	= "OVRE",
+		[31]	= "UNRE",
+	};
+	unsigned int		i;
+
+	seq_printf(s, "%s:\t0x%08x", regname, value);
+	for (i = 0; i < ARRAY_SIZE(sr_bit); i++) {
+		if (value & (1 << i)) {
+			if (sr_bit[i])
+				seq_printf(s, " %s", sr_bit[i]);
+			else
+				seq_puts(s, " UNKNOWN");
+		}
+	}
+	seq_putc(s, '\n');
+}
+
+static int at91mci_regs_show(struct seq_file *s, void *v)
+{
+	struct at91_mci	*host = s->private;
+	u32			*buf;
+
+	buf = kmalloc(AT91_MCI_REGS_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	/*
+	 * Grab a more or less consistent snapshot. Note that we're
+	 * not disabling interrupts, so IMR and SR may not be
+	 * consistent.
+	 */
+	spin_lock_bh(&host->lock);
+	clk_enable(host->mck);
+	memcpy_fromio(buf, host->regs, AT91_MCI_REGS_SIZE);
+	clk_disable(host->mck);
+	spin_unlock_bh(&host->lock);
+
+	seq_printf(s, "MR:\t0x%08x%s%s CLKDIV=%u\n",
+                   buf[AT91_MCI_MR / 4],
+                   buf[AT91_MCI_MR / 4] & AT91_MCI_RDPROOF ? " RDPROOF" : "",
+                   buf[AT91_MCI_MR / 4] & AT91_MCI_WRPROOF ? " WRPROOF" : "",
+                   buf[AT91_MCI_MR / 4] & 0xff);
+	seq_printf(s, "DTOR:\t0x%08x\n", buf[AT91_MCI_DTOR / 4]);
+	seq_printf(s, "SDCR:\t0x%08x\n", buf[AT91_MCI_SDCR / 4]);
+	seq_printf(s, "ARGR:\t0x%08x\n", buf[AT91_MCI_ARGR / 4]);
+	seq_printf(s, "BLKR:\t0x%08x BCNT=%u BLKLEN=%u\n",
+                   buf[AT91_MCI_BLKR / 4],
+                   (buf[AT91_MCI_BLKR / 4] >> 0) & 0xffff,
+                   (buf[AT91_MCI_BLKR / 4] >> 16) & 0xffff);
+
+	/* Don't read RSPR and RDR; it will consume the data there */
+
+	at91mci_show_status_reg(s, "SR", buf[AT91_MCI_SR / 4]);
+	at91mci_show_status_reg(s, "IMR", buf[AT91_MCI_IMR / 4]);
+
+	kfree(buf);
+
+	return 0;
+}
+
+static int at91mci_regs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, at91mci_regs_show, inode->i_private);
+}
+
+static const struct file_operations at91mci_regs_fops = {
+	.owner		= THIS_MODULE,
+	.open		= at91mci_regs_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void at91mci_init_debugfs(struct at91_mci_slot *slot)
+{
+	struct mmc_host		*mmc = slot->mmc;
+	struct at91_mci	*host = slot->host;
+	struct dentry		*root;
+	struct dentry		*node;
+
+	root = mmc->debugfs_root;
+	if (!root)
+		return;
+
+	node = debugfs_create_file("regs", S_IRUSR, root, host,
+			&at91mci_regs_fops);
+	if (IS_ERR(node))
+		return;
+	if (!node)
+		goto err;
+
+	node = debugfs_create_file("req", S_IRUSR, root, slot, &at91mci_req_fops);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("pending_events", S_IRUSR, root,
+				     (u32 *)&host->pending_events);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("completed_events", S_IRUSR, root,
+				     (u32 *)&host->completed_events);
+	if (!node)
+		goto err;
+
+	return;
+
+err:
+	dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
+}
+
+static inline unsigned int ns_to_clocks(struct at91_mci *host,
+					unsigned int ns)
+{
+	return (ns * (host->bus_hz / 1000000) + 999) / 1000;
+}
+
+static void at91mci_set_timeout(struct at91_mci *host,
+		struct at91_mci_slot *slot, struct mmc_data *data)
+{
+	static unsigned	dtomul_to_shift[] = {
+		0, 4, 7, 8, 10, 12, 16, 20
+	};
+	unsigned	timeout;
+	unsigned	dtocyc;
+	unsigned	dtomul;
+
+	timeout = ns_to_clocks(host, data->timeout_ns) + data->timeout_clks;
+
+	for (dtomul = 0; dtomul < 8; dtomul++) {
+		unsigned shift = dtomul_to_shift[dtomul];
+		dtocyc = (timeout + (1 << shift) - 1) >> shift;
+		if (dtocyc < 15)
+			break;
+	}
+
+	if (dtomul >= 8) {
+		dtomul = 7;
+		dtocyc = 15;
+	}
+
+	dev_vdbg(&slot->mmc->class_dev, "setting timeout to %u cycles\n",
+			dtocyc << dtomul_to_shift[dtomul]);
+	at91_mci_write(host, AT91_MCI_DTOR,
+                       (AT91_MCI_DTOMUL_F(dtomul) |
AT91_MCI_DTOCYC_F(dtocyc)));
+}
+
+/*
+ * Return mask with command flags to be enabled for this command.
+ */
+static u32 at91mci_prepare_command(struct mmc_host *mmc,
+				 struct mmc_command *cmd)
+{
+	struct mmc_data	*data;
+	u32		cmdr;
+
+	cmd->error = -EINPROGRESS;
+
+	cmdr = AT91_MCI_CMDNB & cmd->opcode;
+
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		if (cmd->flags & MMC_RSP_136)
+			cmdr |= AT91_MCI_RSPTYP_136;
+		else
+			cmdr |= AT91_MCI_RSPTYP_48;
+	}
+
+	/*
+	 * This should really be MAXLAT_5 for CMD2 and ACMD41, but
+	 * it's too difficult to determine whether this is an ACMD or
+	 * not. Better make it 64.
+	 */
+	cmdr |= AT91_MCI_MAXLAT;
+
+	if (mmc->ios.bus_mode == MMC_BUSMODE_OPENDRAIN)
+		cmdr |= AT91_MCI_OPDCMD;
+
+	data = cmd->data;
+	if (data) {
+		cmdr |= AT91_MCI_TRCMD_START;
+		if (data->flags & MMC_DATA_STREAM)
+			cmdr |= AT91_MCI_TRTYP_STREAM;
+		else if (data->blocks > 1)
+			cmdr |= AT91_MCI_TRTYP_MULTIPLE;
+		else
+			cmdr |= AT91_MCI_TRTYP_BLOCK;
+
+		if (data->flags & MMC_DATA_READ)
+			cmdr |= AT91_MCI_TRDIR;
+	}
+
+	return cmdr;
+}
+
+static void at91mci_start_command(struct at91_mci *host,
+		struct mmc_command *cmd, u32 cmd_flags)
+{
+	WARN_ON(host->cmd);
+	host->cmd = cmd;
+
+	dev_vdbg(&host->pdev->dev,
+			"start command: ARGR=0x%08x CMDR=0x%08x\n",
+			cmd->arg, cmd_flags);
+
+	at91_mci_write(host, AT91_MCI_ARGR, cmd->arg);
+	at91_mci_write(host, AT91_MCI_CMDR, cmd_flags);
+}
+
+static void send_stop_cmd(struct at91_mci *host, struct mmc_data *data)
+{
+	at91mci_start_command(host, data->stop, host->stop_cmdr);
+	at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY);
+}
+
+#ifdef CONFIG_MMC_AT91GEN2_DMA
+#error "DMA Unsupported for AT91 MMC Gen2 Driver"
+#else /* CONFIG_MMC_AT91GEN2_DMA */
+
+static int at91mci_submit_data_dma(struct at91_mci *host, struct
mmc_data *data)
+{
+	return -ENOSYS;
+}
+
+static void at91mci_stop_dma(struct at91_mci *host)
+{
+	/* Data transfer was stopped by the interrupt handler */
+	at91mci_set_pending(host, EVENT_XFER_COMPLETE);
+	at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
+}
+
+#endif /* CONFIG_MMC_AT91GEN2_DMA */
+
+/*
+ * Returns a mask of interrupt flags to be enabled after the whole
+ * request has been prepared.
+ */
+static u32 at91mci_submit_data(struct at91_mci *host, struct mmc_data *data)
+{
+	u32 iflags;
+
+	data->error = -EINPROGRESS;
+
+	WARN_ON(host->data);
+	host->sg = NULL;
+	host->data = data;
+
+	iflags = AT91MCI_DATA_ERROR_FLAGS;
+	if (at91mci_submit_data_dma(host, data)) {
+		host->data_chan = NULL;
+
+		/*
+		 * Errata: MMC data write operation with less than 12
+		 * bytes is impossible.
+		 */
+		if (data->blocks * data->blksz < 12)
+			host->need_reset = true;
+
+		host->sg = data->sg;
+		host->pio_offset = 0;
+		if (data->flags & MMC_DATA_READ)
+			iflags |= AT91_MCI_RXRDY;
+		else
+			iflags |= AT91_MCI_TXRDY;
+	}
+
+	return iflags;
+}
+
+static void at91mci_start_request(struct at91_mci *host,
+		struct at91_mci_slot *slot)
+{
+	struct mmc_request	*mrq;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+	u32			iflags;
+	u32			cmdflags;
+
+	mrq = slot->mrq;
+	host->cur_slot = slot;
+	host->mrq = mrq;
+
+	host->pending_events = 0;
+	host->completed_events = 0;
+	host->data_status = 0;
+
+	if (host->need_reset) {
+		at91_mci_write(host, AT91_MCI_CR, AT91_MCI_SWRST);
+		at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
+		at91_mci_write(host, AT91_MCI_MR, host->mode_reg);
+		host->need_reset = false;
+	}
+	at91_mci_write(host, AT91_MCI_SDCR, slot->sdc_reg);
+
+	iflags = at91_mci_read(host, AT91_MCI_IMR);
+	if (iflags)
+		dev_warn(&slot->mmc->class_dev, "WARNING: IMR=0x%08x\n",
+				iflags);
+
+	if (unlikely(test_and_clear_bit(AT91MCI_CARD_NEED_INIT, &slot->flags))) {
+		/* Send init sequence (74 clock cycles) */
+		at91_mci_write(host, AT91_MCI_CMDR, AT91_MCI_SPCMD_INIT);
+		while (!(at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_CMDRDY))
+			cpu_relax();
+	}
+	data = mrq->data;
+	if (data) {
+		at91mci_set_timeout(host, slot, data);
+
+		/* Must set block count/size before sending command */
+		at91_mci_write(host, AT91_MCI_BLKR, AT91_MCI_BLKR_BCNT(data->blocks)
+				| AT91_MCI_BLKR_BLKLEN(data->blksz));
+		dev_vdbg(&slot->mmc->class_dev, "BLKR=0x%08x\n",
+			AT91_MCI_BLKR_BCNT(data->blocks) | AT91_MCI_BLKR_BLKLEN(data->blksz));
+	}
+
+	iflags = AT91_MCI_CMDRDY;
+	cmd = mrq->cmd;
+	cmdflags = at91mci_prepare_command(slot->mmc, cmd);
+	at91mci_start_command(host, cmd, cmdflags);
+
+	if (data)
+		iflags |= at91mci_submit_data(host, data);
+
+	if (mrq->stop) {
+		host->stop_cmdr = at91mci_prepare_command(slot->mmc, mrq->stop);
+		host->stop_cmdr |= AT91_MCI_TRCMD_STOP;
+		if (!(data->flags & MMC_DATA_WRITE))
+			host->stop_cmdr |= AT91_MCI_TRDIR;
+		if (data->flags & MMC_DATA_STREAM)
+			host->stop_cmdr |= AT91_MCI_TRTYP_STREAM;
+		else
+			host->stop_cmdr |= AT91_MCI_TRTYP_MULTIPLE;
+	}
+
+	/*
+	 * We could have enabled interrupts earlier, but I suspect
+	 * that would open up a nice can of interesting race
+	 * conditions (e.g. command and data complete, but stop not
+	 * prepared yet.)
+	 */
+	at91_mci_write(host, AT91_MCI_IER, iflags);
+}
+
+static void at91mci_queue_request(struct at91_mci *host,
+		struct at91_mci_slot *slot, struct mmc_request *mrq)
+{
+	dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
+			host->state);
+
+	spin_lock_bh(&host->lock);
+	slot->mrq = mrq;
+	if (host->state == STATE_IDLE) {
+		host->state = STATE_SENDING_CMD;
+		at91mci_start_request(host, slot);
+	} else {
+		list_add_tail(&slot->queue_node, &host->queue);
+	}
+	spin_unlock_bh(&host->lock);
+}
+
+static void at91mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct at91_mci_slot	*slot = mmc_priv(mmc);
+	struct at91_mci	*host = slot->host;
+	struct mmc_data		*data;
+
+	WARN_ON(slot->mrq);
+
+	/*
+	 * We may "know" the card is gone even though there's still an
+	 * electrical connection. If so, we really need to communicate
+	 * this to the MMC core since there won't be any more
+	 * interrupts as the card is completely removed. Otherwise,
+	 * the MMC core might believe the card is still there even
+	 * though the card was just removed very slowly.
+	 */
+	if (!test_bit(AT91MCI_CARD_PRESENT, &slot->flags)) {
+		mrq->cmd->error = -ENOMEDIUM;
+		mmc_request_done(mmc, mrq);
+		return;
+	}
+
+	/* We don't support multiple blocks of weird lengths. */
+	data = mrq->data;
+	if (data && data->blocks > 1 && data->blksz & 3) {
+		mrq->cmd->error = -EINVAL;
+		mmc_request_done(mmc, mrq);
+	}
+
+	at91mci_queue_request(host, slot, mrq);
+}
+
+static void at91mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct at91_mci_slot	*slot = mmc_priv(mmc);
+	struct at91_mci	*host = slot->host;
+	unsigned int		i;
+
+	slot->sdc_reg &= ~AT91_MCI_SDCBUS;
+	switch (ios->bus_width) {
+	case MMC_BUS_WIDTH_4:
+		slot->sdc_reg |= AT91_MCI_SDCBUS;
+		break;
+        default:
+                break;
+	}
+
+	if (ios->clock) {
+		unsigned int clock_min = ~0U;
+		u32 clkdiv;
+
+		spin_lock_bh(&host->lock);
+		if (!host->mode_reg) {
+			clk_enable(host->mck);
+			at91_mci_write(host, AT91_MCI_CR, AT91_MCI_SWRST);
+			at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
+		}
+
+		/*
+		 * Use mirror of ios->clock to prevent race with mmc
+		 * core ios update when finding the minimum.
+		 */
+		slot->clock = ios->clock;
+		for (i = 0; i < AT91_MCI_MAX_NR_SLOTS; i++) {
+			if (host->slot[i] && host->slot[i]->clock
+					&& host->slot[i]->clock < clock_min)
+				clock_min = host->slot[i]->clock;
+		}
+
+		/* Calculate clock divider */
+		clkdiv = DIV_ROUND_UP(host->bus_hz, 2 * clock_min) - 1;
+		if (clkdiv > 255) {
+			dev_warn(&mmc->class_dev,
+				"clock %u too slow; using %lu\n",
+				clock_min, host->bus_hz / (2 * 256));
+			clkdiv = 255;
+		}
+
+		/*
+		 * WRPROOF and RDPROOF prevent overruns/underruns by
+		 * stopping the clock when the FIFO is full/empty.
+		 * This state is not expected to last for long.
+		 */
+		host->mode_reg = (AT91_MCI_CLKDIV & clkdiv) | AT91_MCI_WRPROOF
+					| AT91_MCI_RDPROOF;
+
+		if (list_empty(&host->queue))
+			at91_mci_write(host, AT91_MCI_MR, host->mode_reg);
+		else
+			host->need_clock_update = true;
+
+		spin_unlock_bh(&host->lock);
+	} else {
+		bool any_slot_active = false;
+
+		spin_lock_bh(&host->lock);
+		slot->clock = 0;
+		for (i = 0; i < AT91_MCI_MAX_NR_SLOTS; i++) {
+			if (host->slot[i] && host->slot[i]->clock) {
+				any_slot_active = true;
+				break;
+			}
+		}
+		if (!any_slot_active) {
+			at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS);
+			if (host->mode_reg) {
+				at91_mci_read(host, AT91_MCI_MR);
+				clk_disable(host->mck);
+			}
+			host->mode_reg = 0;
+		}
+		spin_unlock_bh(&host->lock);
+	}
+
+	switch (ios->power_mode) {
+	case MMC_POWER_UP:
+		set_bit(AT91MCI_CARD_NEED_INIT, &slot->flags);
+		break;
+	default:
+		/*
+		 * TODO: None of the currently available AVR32-based
+		 * boards allow MMC power to be turned off. Implement
+		 * power control when this can be tested properly.
+		 *
+		 * We also need to hook this into the clock management
+		 * somehow so that newly inserted cards aren't
+		 * subjected to a fast clock before we have a chance
+		 * to figure out what the maximum rate is. Currently,
+		 * there's no way to avoid this, and there never will
+		 * be for boards that don't support power control.
+		 */
+		break;
+	}
+}
+
+static int at91mci_get_ro(struct mmc_host *mmc)
+{
+	int			read_only = -ENOSYS;
+	struct at91_mci_slot	*slot = mmc_priv(mmc);
+
+	if (gpio_is_valid(slot->wp_pin)) {
+		read_only = gpio_get_value(slot->wp_pin);
+		dev_dbg(&mmc->class_dev, "card is %s\n",
+				read_only ? "read-only" : "read-write");
+	}
+
+	return read_only;
+}
+
+static int at91mci_get_cd(struct mmc_host *mmc)
+{
+	int			present = -ENOSYS;
+	struct at91_mci_slot	*slot = mmc_priv(mmc);
+
+	if (gpio_is_valid(slot->detect_pin)) {
+		present = !gpio_get_value(slot->detect_pin);
+		dev_dbg(&mmc->class_dev, "card is %spresent\n",
+				present ? "" : "not ");
+	}
+
+	return present;
+}
+
+static const struct mmc_host_ops at91mci_ops = {
+	.request	= at91mci_request,
+	.set_ios	= at91mci_set_ios,
+	.get_ro		= at91mci_get_ro,
+	.get_cd		= at91mci_get_cd,
+};
+
+/* Called with host->lock held */
+static void at91mci_request_end(struct at91_mci *host, struct mmc_request *mrq)
+	__releases(&host->lock)
+	__acquires(&host->lock)
+{
+	struct at91_mci_slot	*slot = NULL;
+	struct mmc_host		*prev_mmc = host->cur_slot->mmc;
+
+	WARN_ON(host->cmd || host->data);
+
+	/*
+	 * Update the MMC clock rate if necessary. This may be
+	 * necessary if set_ios() is called when a different slot is
+	 * busy transfering data.
+	 */
+	if (host->need_clock_update)
+		at91_mci_write(host, AT91_MCI_MR, host->mode_reg);
+
+	host->cur_slot->mrq = NULL;
+	host->mrq = NULL;
+	if (!list_empty(&host->queue)) {
+		slot = list_entry(host->queue.next,
+				struct at91_mci_slot, queue_node);
+		list_del(&slot->queue_node);
+		dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
+				mmc_hostname(slot->mmc));
+		host->state = STATE_SENDING_CMD;
+		at91mci_start_request(host, slot);
+	} else {
+		dev_vdbg(&host->pdev->dev, "list empty\n");
+		host->state = STATE_IDLE;
+	}
+
+	spin_unlock(&host->lock);
+	mmc_request_done(prev_mmc, mrq);
+	spin_lock(&host->lock);
+}
+
+static void at91mci_command_complete(struct at91_mci *host,
+			struct mmc_command *cmd)
+{
+	u32		status = host->cmd_status;
+
+	/* Read the response from the card (up to 16 bytes) */
+	cmd->resp[0] = at91_mci_read(host, AT91_MCI_RSPR(0));
+	cmd->resp[1] = at91_mci_read(host, AT91_MCI_RSPR(1));
+	cmd->resp[2] = at91_mci_read(host, AT91_MCI_RSPR(2));
+	cmd->resp[3] = at91_mci_read(host, AT91_MCI_RSPR(3));
+
+	if (status & AT91_MCI_RTOE)
+		cmd->error = -ETIMEDOUT;
+	else if ((cmd->flags & MMC_RSP_CRC) && (status & AT91_MCI_RCRCE))
+		cmd->error = -EILSEQ;
+	else if (status & (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RENDE))
+		cmd->error = -EIO;
+	else
+		cmd->error = 0;
+
+	if (cmd->error) {
+		dev_dbg(&host->pdev->dev,
+			"command error: status=0x%08x\n", status);
+
+		if (cmd->data) {
+			host->data = NULL;
+			at91mci_stop_dma(host);
+			at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_NOTBUSY
+					| AT91_MCI_TXRDY | AT91_MCI_RXRDY
+					| AT91MCI_DATA_ERROR_FLAGS);
+		}
+	}
+}
+
+static void at91mci_detect_change(unsigned long data)
+{
+	struct at91_mci_slot	*slot = (struct at91_mci_slot *)data;
+	bool			present;
+	bool			present_old;
+
+	/*
+	 * at91mci_cleanup_slot() sets the AT91MCI_SHUTDOWN flag before
+	 * freeing the interrupt. We must not re-enable the interrupt
+	 * if it has been freed, and if we're shutting down, it
+	 * doesn't really matter whether the card is present or not.
+	 */
+	smp_rmb();
+	if (test_bit(AT91MCI_SHUTDOWN, &slot->flags))
+		return;
+
+	enable_irq(gpio_to_irq(slot->detect_pin));
+	present = !gpio_get_value(slot->detect_pin);
+	present_old = test_bit(AT91MCI_CARD_PRESENT, &slot->flags);
+
+	dev_vdbg(&slot->mmc->class_dev, "detect change: %d (was %d)\n",
+			present, present_old);
+
+	if (present != present_old) {
+		struct at91_mci	*host = slot->host;
+		struct mmc_request	*mrq;
+
+		dev_dbg(&slot->mmc->class_dev, "card %s\n",
+			present ? "inserted" : "removed");
+
+		spin_lock(&host->lock);
+
+		if (!present)
+			clear_bit(AT91MCI_CARD_PRESENT, &slot->flags);
+		else
+			set_bit(AT91MCI_CARD_PRESENT, &slot->flags);
+
+		/* Clean up queue if present */
+		mrq = slot->mrq;
+		if (mrq) {
+			if (mrq == host->mrq) {
+				/*
+				 * Reset controller to terminate any ongoing
+				 * commands or data transfers.
+				 */
+				at91_mci_write(host, AT91_MCI_CR, AT91_MCI_SWRST);
+				at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
+				at91_mci_write(host, AT91_MCI_MR, host->mode_reg);
+
+				host->data = NULL;
+				host->cmd = NULL;
+
+				switch (host->state) {
+				case STATE_IDLE:
+					break;
+				case STATE_SENDING_CMD:
+					mrq->cmd->error = -ENOMEDIUM;
+					if (!mrq->data)
+						break;
+					/* fall through */
+				case STATE_SENDING_DATA:
+					mrq->data->error = -ENOMEDIUM;
+					at91mci_stop_dma(host);
+					break;
+				case STATE_DATA_BUSY:
+				case STATE_DATA_ERROR:
+					if (mrq->data->error == -EINPROGRESS)
+						mrq->data->error = -ENOMEDIUM;
+					if (!mrq->stop)
+						break;
+					/* fall through */
+				case STATE_SENDING_STOP:
+					mrq->stop->error = -ENOMEDIUM;
+					break;
+				}
+
+				at91mci_request_end(host, mrq);
+			} else {
+				list_del(&slot->queue_node);
+				mrq->cmd->error = -ENOMEDIUM;
+				if (mrq->data)
+					mrq->data->error = -ENOMEDIUM;
+				if (mrq->stop)
+					mrq->stop->error = -ENOMEDIUM;
+
+				spin_unlock(&host->lock);
+				mmc_request_done(slot->mmc, mrq);
+				spin_lock(&host->lock);
+			}
+		}
+		spin_unlock(&host->lock);
+
+		mmc_detect_change(slot->mmc, 0);
+	}
+}
+
+static void at91mci_tasklet_func(unsigned long priv)
+{
+	struct at91_mci	*host = (struct at91_mci *)priv;
+	struct mmc_request	*mrq = host->mrq;
+	struct mmc_data		*data = host->data;
+	struct mmc_command	*cmd = host->cmd;
+	enum at91_mci_state	state = host->state;
+	enum at91_mci_state	prev_state;
+	u32			status;
+
+	spin_lock(&host->lock);
+
+	state = host->state;
+
+	dev_vdbg(&host->pdev->dev,
+		"tasklet: state %u pending/completed/mask %lx/%lx/%x\n",
+		state, host->pending_events, host->completed_events,
+		at91_mci_read(host, AT91_MCI_IMR));
+
+	do {
+		prev_state = state;
+
+		switch (state) {
+		case STATE_IDLE:
+			break;
+
+		case STATE_SENDING_CMD:
+			if (!at91mci_test_and_clear_pending(host,
+						EVENT_CMD_COMPLETE))
+				break;
+
+			host->cmd = NULL;
+			at91mci_set_completed(host, EVENT_CMD_COMPLETE);
+			at91mci_command_complete(host, mrq->cmd);
+			if (!mrq->data || cmd->error) {
+				at91mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_DATA;
+			/* fall through */
+
+		case STATE_SENDING_DATA:
+			if (at91mci_test_and_clear_pending(host,
+						EVENT_DATA_ERROR)) {
+				at91mci_stop_dma(host);
+				if (data->stop)
+					send_stop_cmd(host, data);
+				state = STATE_DATA_ERROR;
+				break;
+			}
+
+			if (!at91mci_test_and_clear_pending(host,
+						EVENT_XFER_COMPLETE))
+				break;
+
+			at91mci_set_completed(host, EVENT_XFER_COMPLETE);
+			prev_state = state = STATE_DATA_BUSY;
+			/* fall through */
+
+		case STATE_DATA_BUSY:
+			if (!at91mci_test_and_clear_pending(host,
+						EVENT_DATA_COMPLETE))
+				break;
+
+			host->data = NULL;
+			at91mci_set_completed(host, EVENT_DATA_COMPLETE);
+			status = host->data_status;
+			if (unlikely(status & AT91MCI_DATA_ERROR_FLAGS)) {
+				if (status & AT91_MCI_DTOE) {
+					dev_dbg(&host->pdev->dev,
+							"data timeout error\n");
+					data->error = -ETIMEDOUT;
+				} else if (status & AT91_MCI_DCRCE) {
+					dev_dbg(&host->pdev->dev,
+							"data CRC error\n");
+					data->error = -EILSEQ;
+				} else {
+					dev_dbg(&host->pdev->dev,
+						"data FIFO error (status=%08x)\n",
+						status);
+					data->error = -EIO;
+				}
+			} else {
+				data->bytes_xfered = data->blocks * data->blksz;
+				data->error = 0;
+			}
+
+			if (!data->stop) {
+				at91mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_STOP;
+			if (!data->error)
+				send_stop_cmd(host, data);
+			/* fall through */
+
+		case STATE_SENDING_STOP:
+			if (!at91mci_test_and_clear_pending(host,
+						EVENT_CMD_COMPLETE))
+				break;
+
+			host->cmd = NULL;
+			at91mci_command_complete(host, mrq->stop);
+			at91mci_request_end(host, host->mrq);
+			goto unlock;
+
+		case STATE_DATA_ERROR:
+			if (!at91mci_test_and_clear_pending(host,
+						EVENT_XFER_COMPLETE))
+				break;
+
+			state = STATE_DATA_BUSY;
+			break;
+		}
+	} while (state != prev_state);
+
+	host->state = state;
+
+unlock:
+	spin_unlock(&host->lock);
+}
+
+static void at91mci_read_data_pio(struct at91_mci *host)
+{
+	struct scatterlist	*sg = host->sg;
+	void			*buf = sg_virt(sg);
+	unsigned int		offset = host->pio_offset;
+	struct mmc_data		*data = host->data;
+	u32			value;
+	u32			status;
+	unsigned int		nbytes = 0;
+
+	do {
+		value = at91_mci_read(host, AT91_MCI_RDR);
+		if (likely(offset + 4 <= sg->length)) {
+			put_unaligned(value, (u32 *)(buf + offset));
+
+			offset += 4;
+			nbytes += 4;
+
+			if (offset == sg->length) {
+				flush_dcache_page(sg_page(sg));
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+			memcpy(buf + offset, &value, remaining);
+			nbytes += remaining;
+
+			flush_dcache_page(sg_page(sg));
+			host->sg = sg = sg_next(sg);
+			if (!sg)
+				goto done;
+
+			offset = 4 - remaining;
+			buf = sg_virt(sg);
+			memcpy(buf, (u8 *)&value + remaining, offset);
+			nbytes += offset;
+		}
+
+		status = at91_mci_read(host, AT91_MCI_SR);
+		if (status & AT91MCI_DATA_ERROR_FLAGS) {
+			at91_mci_write(host, AT91_MCI_IDR, (AT91_MCI_NOTBUSY | AT91_MCI_RXRDY
+						| AT91MCI_DATA_ERROR_FLAGS));
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+			smp_wmb();
+			at91mci_set_pending(host, EVENT_DATA_ERROR);
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+	} while (status & AT91_MCI_RXRDY);
+
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+
+	return;
+
+done:
+	at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_RXRDY);
+	at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	at91mci_set_pending(host, EVENT_XFER_COMPLETE);
+}
+
+static void at91mci_write_data_pio(struct at91_mci *host)
+{
+	struct scatterlist	*sg = host->sg;
+	void			*buf = sg_virt(sg);
+	unsigned int		offset = host->pio_offset;
+	struct mmc_data		*data = host->data;
+	u32			value;
+	u32			status;
+	unsigned int		nbytes = 0;
+
+	do {
+		if (likely(offset + 4 <= sg->length)) {
+			value = get_unaligned((u32 *)(buf + offset));
+			at91_mci_write(host, AT91_MCI_TDR, value);
+
+			offset += 4;
+			nbytes += 4;
+			if (offset == sg->length) {
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+
+			value = 0;
+			memcpy(&value, buf + offset, remaining);
+			nbytes += remaining;
+
+			host->sg = sg = sg_next(sg);
+			if (!sg) {
+				at91_mci_write(host, AT91_MCI_TDR, value);
+				goto done;
+			}
+
+			offset = 4 - remaining;
+			buf = sg_virt(sg);
+			memcpy((u8 *)&value + remaining, buf, offset);
+			at91_mci_write(host, AT91_MCI_TDR, value);
+			nbytes += offset;
+		}
+
+		status = at91_mci_read(host, AT91_MCI_SR);
+		if (status & AT91MCI_DATA_ERROR_FLAGS) {
+			at91_mci_write(host, AT91_MCI_IDR, (AT91_MCI_NOTBUSY | AT91_MCI_TXRDY
+						| AT91MCI_DATA_ERROR_FLAGS));
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+			smp_wmb();
+			at91mci_set_pending(host, EVENT_DATA_ERROR);
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+	} while (status & AT91_MCI_TXRDY);
+
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+
+	return;
+
+done:
+	at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXRDY);
+	at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	at91mci_set_pending(host, EVENT_XFER_COMPLETE);
+}
+
+static void at91mci_cmd_interrupt(struct at91_mci *host, u32 status)
+{
+	at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_CMDRDY);
+
+	host->cmd_status = status;
+	smp_wmb();
+	at91mci_set_pending(host, EVENT_CMD_COMPLETE);
+	tasklet_schedule(&host->tasklet);
+}
+
+static irqreturn_t at91mci_interrupt(int irq, void *dev_id)
+{
+	struct at91_mci	*host = dev_id;
+	u32			status, mask, pending;
+	unsigned int		pass_count = 0;
+
+	do {
+		status = at91_mci_read(host, AT91_MCI_SR);
+		mask = at91_mci_read(host, AT91_MCI_IMR);
+		pending = status & mask;
+		if (!pending)
+			break;
+
+		if (pending & AT91MCI_DATA_ERROR_FLAGS) {
+			at91_mci_write(host, AT91_MCI_IDR, AT91MCI_DATA_ERROR_FLAGS
+					| AT91_MCI_RXRDY | AT91_MCI_TXRDY);
+			pending &= at91_mci_read(host, AT91_MCI_IMR);
+
+			host->data_status = status;
+			smp_wmb();
+			at91mci_set_pending(host, EVENT_DATA_ERROR);
+			tasklet_schedule(&host->tasklet);
+		}
+		if (pending & AT91_MCI_NOTBUSY) {
+			at91_mci_write(host, AT91_MCI_IDR,
+					AT91MCI_DATA_ERROR_FLAGS | AT91_MCI_NOTBUSY);
+			if (!host->data_status)
+				host->data_status = status;
+			smp_wmb();
+			at91mci_set_pending(host, EVENT_DATA_COMPLETE);
+			tasklet_schedule(&host->tasklet);
+		}
+		if (pending & AT91_MCI_RXRDY)
+			at91mci_read_data_pio(host);
+		if (pending & AT91_MCI_TXRDY)
+			at91mci_write_data_pio(host);
+
+		if (pending & AT91_MCI_CMDRDY)
+			at91mci_cmd_interrupt(host, status);
+	} while (pass_count++ < 5);
+
+	return pass_count ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static irqreturn_t at91mci_detect_interrupt(int irq, void *dev_id)
+{
+	struct at91_mci_slot	*slot = dev_id;
+
+	/*
+	 * Disable interrupts until the pin has stabilized and check
+	 * the state then. Use mod_timer() since we may be in the
+	 * middle of the timer routine when this interrupt triggers.
+	 */
+	disable_irq_nosync(irq);
+	mod_timer(&slot->detect_timer, jiffies + msecs_to_jiffies(20));
+
+	return IRQ_HANDLED;
+}
+
+static int __init at91mci_init_slot(struct at91_mci *host,
+		struct at91_mmc_slot_pdata *slot_data, unsigned int id,
+		u32 sdc_reg)
+{
+	struct mmc_host			*mmc;
+	struct at91_mci_slot		*slot;
+
+	mmc = mmc_alloc_host(sizeof(struct at91_mci_slot), &host->pdev->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	slot = mmc_priv(mmc);
+	slot->mmc = mmc;
+	slot->host = host;
+	slot->detect_pin = slot_data->detect_pin;
+	slot->wp_pin = slot_data->wp_pin;
+	slot->sdc_reg = sdc_reg;
+
+	mmc->ops = &at91mci_ops;
+	mmc->f_min = DIV_ROUND_UP(host->bus_hz, 512);
+	mmc->f_max = host->bus_hz / 2;
+	mmc->ocr_avail	= MMC_VDD_32_33 | MMC_VDD_33_34;
+	if (slot_data->bus_width >= 4)
+		mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+	mmc->max_hw_segs = 64;
+	mmc->max_phys_segs = 64;
+	mmc->max_req_size = 32768 * 512;
+	mmc->max_blk_size = 32768;
+	mmc->max_blk_count = 512;
+
+	/* Assume card is present initially */
+	set_bit(AT91MCI_CARD_PRESENT, &slot->flags);
+	if (gpio_is_valid(slot->detect_pin)) {
+		if (gpio_request(slot->detect_pin, "mmc_detect")) {
+			dev_dbg(&mmc->class_dev, "no detect pin available\n");
+			slot->detect_pin = -EBUSY;
+		} else if (gpio_get_value(slot->detect_pin)) {
+			clear_bit(AT91MCI_CARD_PRESENT, &slot->flags);
+		}
+	}
+
+	if (!gpio_is_valid(slot->detect_pin))
+		mmc->caps |= MMC_CAP_NEEDS_POLL;
+
+	if (gpio_is_valid(slot->wp_pin)) {
+		if (gpio_request(slot->wp_pin, "mmc_wp")) {
+			dev_dbg(&mmc->class_dev, "no WP pin available\n");
+			slot->wp_pin = -EBUSY;
+		}
+	}
+
+	host->slot[id] = slot;
+	mmc_add_host(mmc);
+
+	if (gpio_is_valid(slot->detect_pin)) {
+		int ret;
+
+		setup_timer(&slot->detect_timer, at91mci_detect_change,
+				(unsigned long)slot);
+
+		ret = request_irq(gpio_to_irq(slot->detect_pin),
+				at91mci_detect_interrupt,
+				IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+				"mmc-detect", slot);
+		if (ret) {
+			dev_dbg(&mmc->class_dev,
+				"could not request IRQ %d for detect pin\n",
+				gpio_to_irq(slot->detect_pin));
+			gpio_free(slot->detect_pin);
+			slot->detect_pin = -EBUSY;
+		}
+	}
+
+	at91mci_init_debugfs(slot);
+
+	return 0;
+}
+
+static void __exit at91mci_cleanup_slot(struct at91_mci_slot *slot,
+		unsigned int id)
+{
+	/* Debugfs stuff is cleaned up by mmc core */
+
+	set_bit(AT91MCI_SHUTDOWN, &slot->flags);
+	smp_wmb();
+
+	mmc_remove_host(slot->mmc);
+
+	if (gpio_is_valid(slot->detect_pin)) {
+		int pin = slot->detect_pin;
+
+		free_irq(gpio_to_irq(pin), slot);
+		del_timer_sync(&slot->detect_timer);
+		gpio_free(pin);
+	}
+	if (gpio_is_valid(slot->wp_pin))
+		gpio_free(slot->wp_pin);
+
+	slot->host->slot[id] = NULL;
+	mmc_free_host(slot->mmc);
+}
+
+static int __init at91mci_probe(struct platform_device *pdev)
+{
+	struct at91_mmc_data            *pdata;
+	struct at91_mci		*host;
+	struct resource			*regs;
+	unsigned int			nr_slots;
+	int				irq;
+	int				ret;
+
+        printk("Probing for AT91 MMC using Gen2 Driver\n");
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs)
+		return -ENXIO;
+	pdata = pdev->dev.platform_data;
+	if (!pdata)
+		return -ENXIO;
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	host = kzalloc(sizeof(struct at91_mci), GFP_KERNEL);
+	if (!host)
+		return -ENOMEM;
+
+	host->pdev = pdev;
+	spin_lock_init(&host->lock);
+	INIT_LIST_HEAD(&host->queue);
+
+	host->mck = clk_get(&pdev->dev, "mci_clk");
+	if (IS_ERR(host->mck)) {
+		ret = PTR_ERR(host->mck);
+		goto err_clk_get;
+	}
+
+	ret = -ENOMEM;
+	host->regs = ioremap(regs->start, regs->end - regs->start + 1);
+	if (!host->regs)
+		goto err_ioremap;
+
+	clk_enable(host->mck);
+	at91_mci_write(host, AT91_MCI_CR, AT91_MCI_SWRST);
+	host->bus_hz = clk_get_rate(host->mck);
+	clk_disable(host->mck);
+
+	host->mapbase = regs->start;
+
+	tasklet_init(&host->tasklet, at91mci_tasklet_func, (unsigned long)host);
+
+	ret = request_irq(irq, at91mci_interrupt, 0, pdev->dev.bus_id, host);
+	if (ret)
+		goto err_request_irq;
+
+	platform_set_drvdata(pdev, host);
+
+	/* We need at least one slot to succeed */
+	nr_slots = 0;
+	ret = -ENODEV;
+	if (pdata->slot[0].bus_width) {
+		ret = at91mci_init_slot(host, &pdata->slot[0],
+				AT91_MCI_SDCSEL & 0x0, 0);
+		if (!ret)
+			nr_slots++;
+	}
+	if (pdata->slot[1].bus_width) {
+		ret = at91mci_init_slot(host, &pdata->slot[1],
+				AT91_MCI_SDCSEL & 0x1, 1);
+		if (!ret)
+			nr_slots++;
+	}
+
+	if (!nr_slots)
+		goto err_init_slot;
+
+	dev_info(&pdev->dev,
+			"AT91 MCI controller at 0x%08lx irq %d, %u slots\n",
+			host->mapbase, irq, nr_slots);
+
+	return 0;
+
+err_init_slot:
+	free_irq(irq, host);
+err_request_irq:
+	iounmap(host->regs);
+err_ioremap:
+	clk_put(host->mck);
+err_clk_get:
+	kfree(host);
+	return ret;
+}
+
+static int __exit at91mci_remove(struct platform_device *pdev)
+{
+	struct at91_mci	*host = platform_get_drvdata(pdev);
+	unsigned int		i;
+
+	platform_set_drvdata(pdev, NULL);
+
+	for (i = 0; i < AT91_MCI_MAX_NR_SLOTS; i++) {
+		if (host->slot[i])
+			at91mci_cleanup_slot(host->slot[i], i);
+	}
+
+	clk_enable(host->mck);
+	at91_mci_write(host, AT91_MCI_IDR, ~0UL);
+	at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS);
+	at91_mci_read(host, AT91_MCI_SR);
+	clk_disable(host->mck);
+
+	free_irq(platform_get_irq(pdev, 0), host);
+	iounmap(host->regs);
+
+	clk_put(host->mck);
+	kfree(host);
+
+	return 0;
+}
+
+static struct platform_driver at91mci_driver = {
+	.remove		= __exit_p(at91mci_remove),
+	.driver		= {
+		.name		= DRIVER_NAME,
+	},
+};
+
+static int __init at91mci_init(void)
+{
+	return platform_driver_probe(&at91mci_driver, at91mci_probe);
+}
+
+static void __exit at91mci_exit(void)
+{
+	platform_driver_unregister(&at91mci_driver);
+}
+
+module_init(at91mci_init);
+module_exit(at91mci_exit);
+
+MODULE_DESCRIPTION("Atmel AT91 Multimedia Card Interface driver 2nd gen");
+MODULE_AUTHOR("Rob Emanuele <rje@crystalfontz.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:at91_mci");

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH] New AT91 MCI Driver that supports both MCI slots used at the  same time
  2009-05-28 21:40 [PATCH] New AT91 MCI Driver that supports both MCI slots used at the same time Rob Emanuele
@ 2009-05-29 15:27 ` Joey Oravec
  2009-06-02 16:53   ` Rob Emanuele
  2009-06-02  8:49 ` [PATCH] " Nicolas Ferre
  1 sibling, 1 reply; 11+ messages in thread
From: Joey Oravec @ 2009-05-29 15:27 UTC (permalink / raw)
  To: Rob Emanuele; +Cc: nicolas.ferre, linux-arm-kernel, linux-kernel

Rob Emanuele wrote:
> This patch creates a new AT91 Multimedia Card Interface (MCI) driver
> that supports using both MCI slots at the same time.  I'm looking for
> others to test this patch on other boards within the same family of
> chips.
>
> This driver is a port the Atmel AVR32 MCI driver which uses similar silicon.
>   
I made all necessary adjustments to test this on my at91sam9261. 
Unfortunately it didn't want to initialize my card so I wasn't able to 
try some of the failures that I experience with the existing driver. I 
sent you a private email with the logs.

> +config MMC_AT91GEN2
> +	tristate "Atmel AT91 Multimedia Card Interface support 2nd gen"
> +	depends on ARCH_AT91
> +	help
>   
On linux-arm-kernel I've detailed a lot of problems involving 
WRITE_MULTIPLE_BLOCK using the at91 mmc/sd controller. In the end you 
might need to exclude certain processors or else have a gigantic 
warning. You can talk to me offline for details on my testing.

> +struct at91_mmc_slot_pdata {
> +        unsigned int            bus_width;
> +        int                     detect_pin;
> +        int                     wp_pin;
> +        int                     vcc_pin;
> +};
>   
Notice in 2.6.29 that at91_mci and most other structures call it 
"det_pin" rather than "detect_pin"

> +	if (host->need_reset) {
> +		at91_mci_write(host, AT91_MCI_CR, AT91_MCI_SWRST);
> +		at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
> +		at91_mci_write(host, AT91_MCI_MR, host->mode_reg);
> +		host->need_reset = false;
> +	}
>   
In my experience some at91s need a reset after every single command, 
otherwise bad things happen. I think the need_reset logic is too 
conservative in this driver.

> +		/*
> +		 * WRPROOF and RDPROOF prevent overruns/underruns by
> +		 * stopping the clock when the FIFO is full/empty.
> +		 * This state is not expected to last for long.
> +		 */
> +		host->mode_reg = (AT91_MCI_CLKDIV & clkdiv) | AT91_MCI_WRPROOF
> +					| AT91_MCI_RDPROOF;
>   
These registers are not in all at91 chips especially the at91sam9261.

> +	if (pdata->slot[0].bus_width) {
> +		ret = at91mci_init_slot(host, &pdata->slot[0],
> +				AT91_MCI_SDCSEL & 0x0, 0);
> +		if (!ret)
> +			nr_slots++;
> +	}
> +	if (pdata->slot[1].bus_width) {
> +		ret = at91mci_init_slot(host, &pdata->slot[1],
> +				AT91_MCI_SDCSEL & 0x1, 1);
> +		if (!ret)
> +			nr_slots++;
> +	}
> +
> +	if (!nr_slots)
> +		goto err_init_slot;
>   
The original pdata had "wire4" which worked (but defaulted to 1-wire) as 
default uninitialized. With this new variable it's easy to register 
mmc_slot_pdata with bus_width set to zero. The difference bit me when 
testing and at least deserves a printk if not a change.

-joey

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] New AT91 MCI Driver that supports both MCI slots used at the  same time
  2009-05-28 21:40 [PATCH] New AT91 MCI Driver that supports both MCI slots used at the same time Rob Emanuele
  2009-05-29 15:27 ` Joey Oravec
@ 2009-06-02  8:49 ` Nicolas Ferre
  2009-06-02 16:31   ` Rob Emanuele
  1 sibling, 1 reply; 11+ messages in thread
From: Nicolas Ferre @ 2009-06-02  8:49 UTC (permalink / raw)
  To: Rob Emanuele; +Cc: linux-arm-kernel, linux-kernel, Haavard Skinnemoen

Rob Emanuele :
> Greetings,
> 
> This patch creates a new AT91 Multimedia Card Interface (MCI) driver
> that supports using both MCI slots at the same time.  I'm looking for
> others to test this patch on other boards within the same family of
> chips.
> 
> This driver is a port the Atmel AVR32 MCI driver which uses similar silicon.

I am very interested with this work. But, you will have to tell us what
is the precise relation between this code and the Atmel AVR32 MCI driver.
Why did you rename all function and variables to at91... instead of
trying to modify the atmel-mci driver ? Was the needed behavior so far
from the original code that you decided to create another completely new
one ? Please enlighten us.

Moreover, it seems that in your code, DMA is not supported so atmel-mci
without the DMA option enabled should be working without modification at
all (am I missing something ?).

> In addition it modifies the AT91SAM9G20-EK's board platform config to
> support having MCI Slot A hooked to an SD Slot.  This was the board
> that this was developed on. To support MCI Slot A it was needed to not
> enable the LEDs on the AT91SAM9G20-EK board if SD/MMC Slot A is
> enabled.  The Slot A pins overlap the LEDs on Rev A and B boards.

This is a different board, so you will have to create a different board
source file (board-my9g20customboard.c) or find a clever way of sharing
code (true, can be preferable as only a few lines are changing).

Best regards,
-- 
Nicolas Ferre


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] New AT91 MCI Driver that supports both MCI slots used at  the same time
  2009-06-02  8:49 ` [PATCH] " Nicolas Ferre
@ 2009-06-02 16:31   ` Rob Emanuele
  0 siblings, 0 replies; 11+ messages in thread
From: Rob Emanuele @ 2009-06-02 16:31 UTC (permalink / raw)
  To: Nicolas Ferre; +Cc: linux-arm-kernel, linux-kernel, Haavard Skinnemoen

Greetings Nicolas,

Thank you for your interest in this.

On Tue, Jun 2, 2009 at 1:49 AM, Nicolas Ferre <nicolas.ferre@atmel.com> wrote:
> Rob Emanuele :
>> Greetings,
>>
>> This patch creates a new AT91 Multimedia Card Interface (MCI) driver
>> that supports using both MCI slots at the same time.  I'm looking for
>> others to test this patch on other boards within the same family of
>> chips.
>>
>> This driver is a port the Atmel AVR32 MCI driver which uses similar silicon.
>
> I am very interested with this work. But, you will have to tell us what
> is the precise relation between this code and the Atmel AVR32 MCI driver.
> Why did you rename all function and variables to at91... instead of
> trying to modify the atmel-mci driver ? Was the needed behavior so far
> from the original code that you decided to create another completely new
> one ? Please enlighten us.
>
> Moreover, it seems that in your code, DMA is not supported so atmel-mci
> without the DMA option enabled should be working without modification at
> all (am I missing something ?).

Currently the behavior is similar to that of the AVR32.  The DMA
support will be vastly different and the registers are not a perfect
match.  Those were the main reasons for diverging the driver from the
AVR32.  I need to be sure this code can work as well as possible
without regard for the AVR32 (as I do not have the ability to test
against it for regressions).  As the driver improves in performance
and gets to a very stable point I would be happy to look at
re-integrating them.

>
>> In addition it modifies the AT91SAM9G20-EK's board platform config to
>> support having MCI Slot A hooked to an SD Slot.  This was the board
>> that this was developed on. To support MCI Slot A it was needed to not
>> enable the LEDs on the AT91SAM9G20-EK board if SD/MMC Slot A is
>> enabled.  The Slot A pins overlap the LEDs on Rev A and B boards.
>
> This is a different board, so you will have to create a different board
> source file (board-my9g20customboard.c) or find a clever way of sharing
> code (true, can be preferable as only a few lines are changing).

I have one that I can include in the next version of the patch.

As it stands now, I am having trouble writing more than a small file
to both cards simultaneously (from the user's point of view).  I can
write to one card and then the other.  I start to get errors when, for
example, I write continuously to slot B and then try to write anything
more than 16k to slot A.  I'm exploring the causes of this.  I'm
thinking it is either the controller needs to be fully reconfigured
when switching between slots or that the locks in the driver aren't
protecting everything they need to be.

Again thank you for your interest,

Rob Emanuele

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] New AT91 MCI Driver that supports both MCI slots used at  the same time
  2009-05-29 15:27 ` Joey Oravec
@ 2009-06-02 16:53   ` Rob Emanuele
  2009-06-03 15:10     ` Nicolas Ferre
  0 siblings, 1 reply; 11+ messages in thread
From: Rob Emanuele @ 2009-06-02 16:53 UTC (permalink / raw)
  To: Joey Oravec; +Cc: nicolas.ferre, linux-arm-kernel, linux-kernel

Greetings Joey,

On Fri, May 29, 2009 at 8:27 AM, Joey Oravec <joravec@drewtech.com> wrote:
> Rob Emanuele wrote:
>>
>> This patch creates a new AT91 Multimedia Card Interface (MCI) driver
>> that supports using both MCI slots at the same time.  I'm looking for
>> others to test this patch on other boards within the same family of
>> chips.
>>
>> This driver is a port the Atmel AVR32 MCI driver which uses similar
>> silicon.
>>
>
> I made all necessary adjustments to test this on my at91sam9261.
> Unfortunately it didn't want to initialize my card so I wasn't able to try
> some of the failures that I experience with the existing driver. I sent you
> a private email with the logs.
>

I'll have a further look at your logs and see if I can distingush why
the driver does not initialize for for you.  Unfortunately the only
hardware I have access to is a AT91SAM9G20-EK Rev. B and a
AT91SAM9XE-EK (maybe Rev A  which is also the same board as what is
used for the AT91SAM9620).  Either way I'll try to be as much help as
I can.  I will provide in my next version of the patch a full
board-sam9g20-dualslot.c file to show how I am initializing the board.

>> +config MMC_AT91GEN2
>> +       tristate "Atmel AT91 Multimedia Card Interface support 2nd gen"
>> +       depends on ARCH_AT91
>> +       help
>>
>
> On linux-arm-kernel I've detailed a lot of problems involving
> WRITE_MULTIPLE_BLOCK using the at91 mmc/sd controller. In the end you might
> need to exclude certain processors or else have a gigantic warning. You can
> talk to me offline for details on my testing.

I will look into this as it might be important to some of the errors I
see.  If you can point me to those messages in one of the archives,
I'd appreciate it.

>
>> +struct at91_mmc_slot_pdata {
>> +        unsigned int            bus_width;
>> +        int                     detect_pin;
>> +        int                     wp_pin;
>> +        int                     vcc_pin;
>> +};
>>
>
> Notice in 2.6.29 that at91_mci and most other structures call it "det_pin"
> rather than "detect_pin"
>

I changed the pin name for my next patch.

>> +       if (host->need_reset) {
>> +               at91_mci_write(host, AT91_MCI_CR, AT91_MCI_SWRST);
>> +               at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
>> +               at91_mci_write(host, AT91_MCI_MR, host->mode_reg);
>> +               host->need_reset = false;
>> +       }
>>
>
> In my experience some at91s need a reset after every single command,
> otherwise bad things happen. I think the need_reset logic is too
> conservative in this driver.
>

I'll try this and probably make it an option to always reset before
each command.

>> +               /*
>> +                * WRPROOF and RDPROOF prevent overruns/underruns by
>> +                * stopping the clock when the FIFO is full/empty.
>> +                * This state is not expected to last for long.
>> +                */
>> +               host->mode_reg = (AT91_MCI_CLKDIV & clkdiv) |
>> AT91_MCI_WRPROOF
>> +                                       | AT91_MCI_RDPROOF;
>>
>
> These registers are not in all at91 chips especially the at91sam9261.

I'll look into this but will not be able to test every case.

>
>> +       if (pdata->slot[0].bus_width) {
>> +               ret = at91mci_init_slot(host, &pdata->slot[0],
>> +                               AT91_MCI_SDCSEL & 0x0, 0);
>> +               if (!ret)
>> +                       nr_slots++;
>> +       }
>> +       if (pdata->slot[1].bus_width) {
>> +               ret = at91mci_init_slot(host, &pdata->slot[1],
>> +                               AT91_MCI_SDCSEL & 0x1, 1);
>> +               if (!ret)
>> +                       nr_slots++;
>> +       }
>> +
>> +       if (!nr_slots)
>> +               goto err_init_slot;
>>
>
> The original pdata had "wire4" which worked (but defaulted to 1-wire) as
> default uninitialized. With this new variable it's easy to register
> mmc_slot_pdata with bus_width set to zero. The difference bit me when
> testing and at least deserves a printk if not a change.
>

I'm happy to add a printk saying that the port was disabled by a bus
width of zero.

Thanks for you interest in this work,

Rob

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] New AT91 MCI Driver that supports both MCI slots used at  the same time
  2009-06-02 16:53   ` Rob Emanuele
@ 2009-06-03 15:10     ` Nicolas Ferre
  2009-06-03 15:45       ` Joey Oravec
  0 siblings, 1 reply; 11+ messages in thread
From: Nicolas Ferre @ 2009-06-03 15:10 UTC (permalink / raw)
  To: Rob Emanuele
  Cc: Joey Oravec, linux-arm-kernel, linux-kernel, Haavard Skinnemoen

Rob Emanuele :
> Greetings Joey,

>>> +       if (host->need_reset) {
>>> +               at91_mci_write(host, AT91_MCI_CR, AT91_MCI_SWRST);
>>> +               at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
>>> +               at91_mci_write(host, AT91_MCI_MR, host->mode_reg);
>>> +               host->need_reset = false;
>>> +       }
>>>
>> In my experience some at91s need a reset after every single command,
>> otherwise bad things happen. I think the need_reset logic is too
>> conservative in this driver.
> 
> I'll try this and probably make it an option to always reset before
> each command.

This systematic reset was remove by Haavard with this comment:
"Also, don't reset the controller between each transfer. That was an
attempt to work around earlier bugs, and it never really worked very
well."

http://lkml.org/lkml/2008/10/5/152

This reset remains in at91_mci and it does improve things on this driver
but I do not know it a better written driver can prevent from doing this...

>>> +               /*
>>> +                * WRPROOF and RDPROOF prevent overruns/underruns by
>>> +                * stopping the clock when the FIFO is full/empty.
>>> +                * This state is not expected to last for long.
>>> +                */
>>> +               host->mode_reg = (AT91_MCI_CLKDIV & clkdiv) |
>>> AT91_MCI_WRPROOF
>>> +                                       | AT91_MCI_RDPROOF;
>>>
>> These registers are not in all at91 chips especially the at91sam9261.
> 
> I'll look into this but will not be able to test every case.

Only at91rm9200 & at91sam9261/9261s are unable to setup these
functionalities.

Best regards,
-- 
Nicolas Ferre


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] New AT91 MCI Driver that supports both MCI slots used at  the same time
  2009-06-03 15:10     ` Nicolas Ferre
@ 2009-06-03 15:45       ` Joey Oravec
  2009-06-03 19:02         ` [PATCH][Updated] " Rob Emanuele
  0 siblings, 1 reply; 11+ messages in thread
From: Joey Oravec @ 2009-06-03 15:45 UTC (permalink / raw)
  To: Nicolas Ferre
  Cc: Rob Emanuele, linux-arm-kernel, linux-kernel, Haavard Skinnemoen

Nicolas Ferre wrote:
> This systematic reset was remove by Haavard with this comment:
> "Also, don't reset the controller between each transfer. That was an
> attempt to work around earlier bugs, and it never really worked very
> well."
>   
See: 
http://lists.arm.linux.org.uk/lurker/message/20090409.120542.4f74c08d.en.html

I disagree with Haavard. Without the reset my sam9261 would exhibit DAT0 
contention, early interrupts, and other problems. Adding the reset was a 
major improvement. I emailed you some screenshots on April 10 2009 that 
demonstrate my testing. Let me know offline if either you or Havaard did 
not get the email and I can resend.

Maybe the problems are limited to this chip? We should discuss this further.

-joey


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH][Updated] New AT91 MCI Driver that supports both MCI slots  used at the same time
  2009-06-03 15:45       ` Joey Oravec
@ 2009-06-03 19:02         ` Rob Emanuele
  2009-06-08 10:47           ` Haavard Skinnemoen
  0 siblings, 1 reply; 11+ messages in thread
From: Rob Emanuele @ 2009-06-03 19:02 UTC (permalink / raw)
  To: Joey Oravec
  Cc: Nicolas Ferre, linux-arm-kernel, linux-kernel, Haavard Skinnemoen

Greetings,

This updated patch creates a new AT91 Multimedia Card Interface
(MCI) driver that supports using both MCI slots at the same time.
I'm looking for others to test this patch on other boards within the
same family of chips.

This driver is a port the Atmel AVR32 MCI driver which uses similar silicon.

In addition it modifies the AT91SAM9G20-EK's board platform config to
support having MCI Slot A hooked to an SD Slot.  This was the board
that this was developed on. There is a compile-time config option to
support MCI Slot A on this board.  It moves the LEDs and ethernet irq
line to other GPIOs as the Slot A pins overlap those on Rev A and B
boards.  (In related news, Atmel has made AT91SAM9G20-EK rev. D
schematics available on their website that have both MCI slots
exposed.)  If you are using this on other boards and processors you will
need to create a modify your platform config accordingly.  Use the patched
board-sam9g20ek.c as an example.

Joey Oravec has mentioned that he has had better reliability with the chip
he is using (AT91SAM9261) if the MMC controller is reset before each
command.  There is a compile time option to do just that.

Joey Oravec has stated that the Read and Write Proof functionality that
prevents buffer overflows is not available on the AT91SAM9261.  This was
confirmed by Nicolas Ferre and also affects the AT91RM9200.  This new
patch excludes those register config bits.

Please comment and try it out,

Rob Emanuele


diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index 323b47f..7f6c96a 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -326,6 +326,12 @@ config MTD_NAND_ATMEL_BUSWIDTH_16
 	  On AT91SAM926x boards both types of NAND flash can be present
 	  (8 and 16 bit data bus width).

+config AT91_2MMC
+	bool "Use both MMC Ports"
+	depends on MACH_AT91SAM9G20EK
+	help
+	  Select this if you have connected both MMC Slots.  Answer No if unsure.
+
 # ----------------------------------------------------------

 comment "AT91 Feature Selections"
diff --git a/arch/arm/mach-at91/at91sam9260_devices.c
b/arch/arm/mach-at91/at91sam9260_devices.c
index d74c9ac..11f8484 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -275,8 +275,103 @@ void __init at91_add_device_mmc(short mmc_id,
struct at91_mmc_data *data)
 	platform_device_register(&at91sam9260_mmc_device);
 }
 #else
+
+/* --------------------------------------------------------------------
+ *  MMC / SD Slot for 2nd Gen Driver
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_MMC_AT91GEN2) || defined(CONFIG_MMC_AT91GEN2_MODULE)
+static u64 mmc_dmamask = DMA_BIT_MASK(32);
+static struct at91_mmc_data mmc_data;
+
+static struct resource mmc_resources[] = {
+	[0] = {
+		.start	= AT91SAM9260_BASE_MCI,
+		.end	= AT91SAM9260_BASE_MCI + SZ_16K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= AT91SAM9260_ID_MCI,
+		.end	= AT91SAM9260_ID_MCI,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device at91sam9260_mmc_device = {
+	.name		= "at91_mci",
+	.id		= -1,
+	.dev		= {
+				.dma_mask		= &mmc_dmamask,
+				.coherent_dma_mask	= DMA_BIT_MASK(32),
+				.platform_data		= &mmc_data,
+	},
+	.resource	= mmc_resources,
+	.num_resources	= ARRAY_SIZE(mmc_resources),
+};
+
+void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data)
+{
+        unsigned int i;
+        unsigned int slot_count = 0;
+
+	if (!data)
+		return;
+
+        for (i = 0; i < AT91_MCI_MAX_NR_SLOTS; i++) {
+                if (data->slot[i].bus_width) {
+                        /* input/irq */
+                        if (data->slot[i].det_pin) {
+                                at91_set_gpio_input(data->slot[i].det_pin, 1);
+                                at91_set_deglitch(data->slot[i].det_pin, 1);
+                }
+                if (data->slot[i].wp_pin)
+                        at91_set_gpio_input(data->slot[i].wp_pin, 1);
+                if (data->slot[i].vcc_pin)
+                        at91_set_gpio_output(data->slot[i].vcc_pin, 0);
+
+                switch(i) {
+                case 0:
+                        /* CMD */
+                        at91_set_A_periph(AT91_PIN_PA7, 1);
+                        /* DAT0, maybe DAT1..DAT3 */
+                        at91_set_A_periph(AT91_PIN_PA6, 1);
+                        if (data->slot[i].bus_width == 4) {
+                                at91_set_A_periph(AT91_PIN_PA9, 1);
+                                at91_set_A_periph(AT91_PIN_PA10, 1);
+                                at91_set_A_periph(AT91_PIN_PA11, 1);
+                        }
+                        break;
+                case 1:
+                        /* CMD */
+                        at91_set_B_periph(AT91_PIN_PA1, 1);
+                        /* DAT0, maybe DAT1..DAT3 */
+                        at91_set_B_periph(AT91_PIN_PA0, 1);
+                        if (data->slot[i].bus_width == 4) {
+                                at91_set_B_periph(AT91_PIN_PA5, 1);
+                                at91_set_B_periph(AT91_PIN_PA4, 1);
+                                at91_set_B_periph(AT91_PIN_PA3, 1);
+                        }
+                        break;
+                default:
+                    break;
+                };
+                slot_count++;
+                }
+        }
+
+        if (slot_count) {
+                /* CLK */
+                at91_set_A_periph(AT91_PIN_PA8, 0);
+
+                mmc_data = *data;
+                platform_device_register(&at91sam9260_mmc_device);
+        }
+}
+#else
 void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {}
 #endif
+#endif
+


 /* --------------------------------------------------------------------
@@ -329,7 +424,7 @@ void __init at91_add_device_nand(struct
atmel_nand_data *data)
 	if (data->rdy_pin)
 		at91_set_gpio_input(data->rdy_pin, 1);

-	/* card detect pin */
+	/* card det pin */
 	if (data->det_pin)
 		at91_set_gpio_input(data->det_pin, 1);

diff --git a/arch/arm/mach-at91/board-sam9g20ek.c
b/arch/arm/mach-at91/board-sam9g20ek.c
index 81439fe..038bfdd 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -89,7 +89,7 @@ static struct at91_udc_data __initdata ek_udc_data = {
  * SPI devices.
  */
 static struct spi_board_info ek_spi_devices[] = {
-#if !defined(CONFIG_MMC_AT91)
+#if !defined(CONFIG_MMC_AT91) && !defined(CONFIG_MMC_AT91GEN2)
 	{	/* DataFlash chip */
 		.modalias	= "mtd_dataflash",
 		.chip_select	= 1,
@@ -112,7 +112,11 @@ static struct spi_board_info ek_spi_devices[] = {
  * MACB Ethernet device
  */
 static struct at91_eth_data __initdata ek_macb_data = {
+#if defined(CONFIG_AT91_2MMC)
+	.phy_irq_pin	= AT91_PIN_PC12,
+#else
 	.phy_irq_pin	= AT91_PIN_PA7,
+#endif
 	.is_rmii	= 1,
 };

@@ -195,11 +199,30 @@ static void __init ek_add_device_nand(void)
  * MCI (SD/MMC)
  * det_pin, wp_pin and vcc_pin are not connected
  */
+#if defined(CONFIG_MMC_AT91) || defined(CONFIG_MMC_AT91_MODULE)
 static struct at91_mmc_data __initdata ek_mmc_data = {
 	.slot_b		= 1,
 	.wire4		= 1,
 };
-
+#endif
+#if defined(CONFIG_MMC_AT91GEN2) || defined(CONFIG_MMC_AT91GEN2_MODULE)
+static struct at91_mmc_data __initdata ek_mmc_data = {
+	.slot[0] = {
+#if defined(CONFIG_AT91_2MMC)
+		.bus_width	= 4,
+#else
+		.bus_width	= 0,
+#endif
+		.det_pin	= -ENODEV,
+		.wp_pin		= -ENODEV,
+	},
+	.slot[1] = {
+		.bus_width	= 4,
+		.det_pin	= -ENODEV,
+		.wp_pin		= -ENODEV,
+	},
+};
+#endif

 /*
  * LEDs
@@ -207,13 +230,21 @@ static struct at91_mmc_data __initdata ek_mmc_data = {
 static struct gpio_led ek_leds[] = {
 	{	/* "bottom" led, green, userled1 to be defined */
 		.name			= "ds5",
+#if defined(CONFIG_AT91_2MMC)
+		.gpio			= AT91_PIN_PB12,
+#else
 		.gpio			= AT91_PIN_PA6,
+#endif
 		.active_low		= 1,
 		.default_trigger	= "none",
 	},
 	{	/* "power" led, yellow */
 		.name			= "ds1",
+#if defined(CONFIG_AT91_2MMC)
+		.gpio			= AT91_PIN_PB13,
+#else
 		.gpio			= AT91_PIN_PA9,
+#endif
 		.default_trigger	= "heartbeat",
 	}
 };
diff --git a/arch/arm/mach-at91/include/mach/at91_mci.h
b/arch/arm/mach-at91/include/mach/at91_mci.h
index 550d503..c9021ea 100644
--- a/arch/arm/mach-at91/include/mach/at91_mci.h
+++ b/arch/arm/mach-at91/include/mach/at91_mci.h
@@ -35,7 +35,9 @@

 #define AT91_MCI_DTOR		0x08		/* Data Timeout Register */
 #define		AT91_MCI_DTOCYC		(0xf << 0)	/* Data Timeout Cycle Number */
+#define		AT91_MCI_DTOCYC_F(n)	((0xf & (n)) << 0)	/* Data Timeout
Cycle Number Function*/
 #define		AT91_MCI_DTOMUL		(7   << 4)	/* Data Timeout Multiplier */
+#define		AT91_MCI_DTOMUL_F(n)	(((n) & 0x7) << 4)	/* Data Timeout
Multiplier Function*/
 #define		AT91_MCI_DTOMUL_1		(0 <<  4)
 #define		AT91_MCI_DTOMUL_16		(1 <<  4)
 #define		AT91_MCI_DTOMUL_128		(2 <<  4)
@@ -80,8 +82,8 @@
 #define		AT91_MCI_BLKR_BLKLEN(n)	((0xffff & (n)) << 16)	/* Block lenght */

 #define AT91_MCI_RSPR(n)	(0x20 + ((n) * 4))	/* Response Registers 0-3 */
-#define AT91_MCR_RDR		0x30		/* Receive Data Register */
-#define AT91_MCR_TDR		0x34		/* Transmit Data Register */
+#define AT91_MCI_RDR		0x30		/* Receive Data Register */
+#define AT91_MCI_TDR		0x34		/* Transmit Data Register */

 #define AT91_MCI_SR		0x40		/* Status Register */
 #define		AT91_MCI_CMDRDY		(1 <<  0)	/* Command Ready */
@@ -110,4 +112,6 @@
 #define AT91_MCI_IDR		0x48		/* Interrupt Disable Register */
 #define AT91_MCI_IMR		0x4c		/* Interrupt Mask Register */

+#define AT91_MCI_REGS_SIZE      0x100
+
 #endif
diff --git a/arch/arm/mach-at91/include/mach/board.h
b/arch/arm/mach-at91/include/mach/board.h
index 793fe7b..8983072 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -63,6 +63,7 @@ struct at91_cf_data {
 extern void __init at91_add_device_cf(struct at91_cf_data *data);

  /* MMC / SD */
+#if defined(CONFIG_MMC_AT91) || defined(CONFIG_MMC_AT91_MODULE)
 struct at91_mmc_data {
 	u8		det_pin;	/* card detect IRQ */
 	unsigned	slot_b:1;	/* uses Slot B */
@@ -70,6 +71,25 @@ struct at91_mmc_data {
 	u8		wp_pin;		/* (SD) writeprotect detect */
 	u8		vcc_pin;	/* power switching (high == on) */
 };
+#endif
+
+#if defined(CONFIG_MMC_AT91GEN2) || defined(CONFIG_MMC_AT91GEN2_MODULE)
+#define AT91_MCI_MAX_NR_SLOTS  2
+
+struct dma_slave;
+
+struct at91_mmc_slot_pdata {
+        unsigned int            bus_width;
+        int                     det_pin;
+        int                     wp_pin;
+        int                     vcc_pin;
+};
+
+struct at91_mmc_data {
+        struct dma_slave        *dma_slave;
+        struct at91_mmc_slot_pdata   slot[AT91_MCI_MAX_NR_SLOTS];
+};
+#endif
 extern void __init at91_add_device_mmc(short mmc_id, struct
at91_mmc_data *data);

  /* Ethernet (EMAC & MACB) */
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 99d4b28..ddabe75 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -114,6 +114,26 @@ config MMC_AT91

 	  If unsure, say N.

+config MMC_AT91GEN2
+	tristate "Atmel AT91 Multimedia Card Interface support 2nd gen"
+	depends on ARCH_AT91
+	help
+	  This selects the Atmel Multimedia Card Interface driver. If
+	  you have an AT91 platform with a Multimedia Card slot,
+	  say Y or M here.
+
+	  If unsure, say N.
+
+config MMC_AT91GEN2_ALWAYS_RESET
+	bool "Reset before every request.  Sometimes needed for buggy chips."
+	depends on MMC_AT91GEN2
+	help
+	  There are reports that some buggy controllers work better
+	  with a reset before every command.  This may improve your
+	  controller's reliability.
+
+	  If unsure, say N.
+
 config MMC_ATMELMCI
 	tristate "Atmel Multimedia Card Interface support"
 	depends on AVR32
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index dedec55..752f018 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
 obj-$(CONFIG_MMC_OMAP)		+= omap.o
 obj-$(CONFIG_MMC_OMAP_HS)	+= omap_hsmmc.o
 obj-$(CONFIG_MMC_AT91)		+= at91_mci.o
+obj-$(CONFIG_MMC_AT91GEN2)	+= at91gen2-mci.o
 obj-$(CONFIG_MMC_ATMELMCI)	+= atmel-mci.o
 obj-$(CONFIG_MMC_TIFM_SD)	+= tifm_sd.o
 obj-$(CONFIG_MMC_SPI)		+= mmc_spi.o
diff --git a/drivers/mmc/host/at91gen2-mci.c b/drivers/mmc/host/at91gen2-mci.c
new file mode 100644
index 0000000..08520dc
--- /dev/null
+++ b/drivers/mmc/host/at91gen2-mci.c
@@ -0,0 +1,1599 @@
+/*
+ * Atmel MultiMedia Card Interface driver
+ *
+ * Copyright (C) 2004-2008 Atmel Corporation
+ * Copyright (C) 2009 Rob Emanuele
+ *
+ * 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/blkdev.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/seq_file.h>
+#include <linux/stat.h>
+
+#include <linux/mmc/host.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/gpio.h>
+#include <asm/unaligned.h>
+
+#include <asm/mach/mmc.h>
+#include <mach/board.h>
+#include <mach/cpu.h>
+#include <mach/at91_mci.h>
+
+#define DRIVER_NAME "at91_mci"
+
+/*
+ * Enable or disable features/registers based on
+ * whether the processor supports them
+ */
+#if !(CONFIG_ARCH_AT91RM9200 || CONFIG_ARCH_AT91SAM9261)
+#define MMC_SUPPORTS_RW_PROOF
+#endif
+
+#ifdef CONFIG_MMC_AT91GEN2_ALWAYS_RESET
+#define MMC_ALWAYS_RESET 1
+#else
+#define MMC_ALWAYS_RESET 0
+#endif
+
+
+#define AT91MCI_DATA_ERROR_FLAGS	(AT91_MCI_RINDE | AT91_MCI_RDIRE |
AT91_MCI_RCRCE	\
+				 | AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE \
+				 | AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)
+
+enum {
+	EVENT_CMD_COMPLETE = 0,
+	EVENT_XFER_COMPLETE,
+	EVENT_DATA_COMPLETE,
+	EVENT_DATA_ERROR,
+};
+
+enum at91_mci_state {
+	STATE_IDLE = 0,
+	STATE_SENDING_CMD,
+	STATE_SENDING_DATA,
+	STATE_DATA_BUSY,
+	STATE_SENDING_STOP,
+	STATE_DATA_ERROR,
+};
+
+struct at91_mci_dma {
+#ifdef CONFIG_MMC_AT91GEN2_DMA
+	struct dma_client		client;
+	struct dma_chan			*chan;
+	struct dma_async_tx_descriptor	*data_desc;
+#endif
+};
+
+/**
+ * struct at91_mci - MMC controller state shared between all slots
+ * @lock: Spinlock protecting the queue and associated data.
+ * @regs: Pointer to MMIO registers.
+ * @sg: Scatterlist entry currently being processed by PIO code, if any.
+ * @pio_offset: Offset into the current scatterlist entry.
+ * @cur_slot: The slot which is currently using the controller.
+ * @mrq: The request currently being processed on @cur_slot,
+ *	or NULL if the controller is idle.
+ * @cmd: The command currently being sent to the card, or NULL.
+ * @data: The data currently being transferred, or NULL if no data
+ *	transfer is in progress.
+ * @dma: DMA client state.
+ * @data_chan: DMA channel being used for the current data transfer.
+ * @cmd_status: Snapshot of SR taken upon completion of the current
+ *	command. Only valid when EVENT_CMD_COMPLETE is pending.
+ * @data_status: Snapshot of SR taken upon completion of the current
+ *	data transfer. Only valid when EVENT_DATA_COMPLETE or
+ *	EVENT_DATA_ERROR is pending.
+ * @stop_cmdr: Value to be loaded into CMDR when the stop command is
+ *	to be sent.
+ * @tasklet: Tasklet running the request state machine.
+ * @pending_events: Bitmask of events flagged by the interrupt handler
+ *	to be processed by the tasklet.
+ * @completed_events: Bitmask of events which the state machine has
+ *	processed.
+ * @state: Tasklet state.
+ * @queue: List of slots waiting for access to the controller.
+ * @need_clock_update: Update the clock rate before the next request.
+ * @need_reset: Reset controller before next request.
+ * @mode_reg: Value of the MR register.
+ * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
+ *	rate and timeout calculations.
+ * @mapbase: Physical address of the MMIO registers.
+ * @mck: The peripheral bus clock hooked up to the MMC controller.
+ * @pdev: Platform device associated with the MMC controller.
+ * @slot: Slots sharing this MMC controller.
+ *
+ * Locking
+ * =======
+ *
+ * @lock is a softirq-safe spinlock protecting @queue as well as
+ * @cur_slot, @mrq and @state. These must always be updated
+ * at the same time while holding @lock.
+ *
+ * @lock also protects mode_reg and need_clock_update since these are
+ * used to synchronize mode register updates with the queue
+ * processing.
+ *
+ * The @mrq field of struct at91_mci_slot is also protected by @lock,
+ * and must always be written at the same time as the slot is added to
+ * @queue.
+ *
+ * @pending_events and @completed_events are accessed using atomic bit
+ * operations, so they don't need any locking.
+ *
+ * None of the fields touched by the interrupt handler need any
+ * locking. However, ordering is important: Before EVENT_DATA_ERROR or
+ * EVENT_DATA_COMPLETE is set in @pending_events, all data-related
+ * interrupts must be disabled and @data_status updated with a
+ * snapshot of SR. Similarly, before EVENT_CMD_COMPLETE is set, the
+ * CMDRDY interupt must be disabled and @cmd_status updated with a
+ * snapshot of SR, and before EVENT_XFER_COMPLETE can be set, the
+ * bytes_xfered field of @data must be written. This is ensured by
+ * using barriers.
+ */
+struct at91_mci {
+	spinlock_t		lock;
+	void __iomem		*regs;
+
+	struct scatterlist	*sg;
+	unsigned int		pio_offset;
+
+	struct at91_mci_slot	*cur_slot;
+	struct mmc_request	*mrq;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+
+	struct at91_mci_dma	dma;
+	struct dma_chan		*data_chan;
+
+	u32			cmd_status;
+	u32			data_status;
+	u32			stop_cmdr;
+
+	struct tasklet_struct	tasklet;
+	unsigned long		pending_events;
+	unsigned long		completed_events;
+	enum at91_mci_state	state;
+	struct list_head	queue;
+
+	bool			need_clock_update;
+	bool			need_reset;
+	u32			mode_reg;
+	unsigned long		bus_hz;
+	unsigned long		mapbase;
+	struct clk		*mck;
+	struct platform_device	*pdev;
+
+	struct at91_mci_slot	*slot[AT91_MCI_MAX_NR_SLOTS];
+};
+
+/**
+ * struct at91_mci_slot - MMC slot state
+ * @mmc: The mmc_host representing this slot.
+ * @host: The MMC controller this slot is using.
+ * @sdc_reg: Value of SDCR to be written before using this slot.
+ * @mrq: mmc_request currently being processed or waiting to be
+ *	processed, or NULL when the slot is idle.
+ * @queue_node: List node for placing this node in the @queue list of
+ *	&struct at91_mci.
+ * @clock: Clock rate configured by set_ios(). Protected by host->lock.
+ * @flags: Random state bits associated with the slot.
+ * @det_pin: GPIO pin used for card detection, or negative if not
+ *	available.
+ * @wp_pin: GPIO pin used for card write protect sending, or negative
+ *	if not available.
+ * @detect_timer: Timer used for debouncing @detect_pin interrupts.
+ */
+struct at91_mci_slot {
+	struct mmc_host		*mmc;
+	struct at91_mci		*host;
+
+	u32			sdc_reg;
+
+	struct mmc_request	*mrq;
+	struct list_head	queue_node;
+
+	unsigned int		clock;
+	unsigned long		flags;
+#define AT91MCI_CARD_PRESENT	0
+#define AT91MCI_CARD_NEED_INIT	1
+#define AT91MCI_SHUTDOWN		2
+
+	int			det_pin;
+	int			wp_pin;
+
+	struct timer_list	detect_timer;
+};
+
+#define at91mci_test_and_clear_pending(host, event)		\
+	test_and_clear_bit(event, &host->pending_events)
+#define at91mci_set_completed(host, event)			\
+	set_bit(event, &host->completed_events)
+#define at91mci_set_pending(host, event)				\
+	set_bit(event, &host->pending_events)
+
+#define at91_mci_read(host, reg)	__raw_readl((host)->regs + (reg))
+#define at91_mci_write(host, reg, val)	__raw_writel((val),
(host)->regs + (reg))
+
+/*
+ * The debugfs stuff below is mostly optimized away when
+ * CONFIG_DEBUG_FS is not set.
+ */
+static int at91mci_req_show(struct seq_file *s, void *v)
+{
+	struct at91_mci_slot	*slot = s->private;
+	struct mmc_request	*mrq;
+	struct mmc_command	*cmd;
+	struct mmc_command	*stop;
+	struct mmc_data		*data;
+
+	/* Make sure we get a consistent snapshot */
+	spin_lock_bh(&slot->host->lock);
+	mrq = slot->mrq;
+
+	if (mrq) {
+		cmd = mrq->cmd;
+		data = mrq->data;
+		stop = mrq->stop;
+
+		if (cmd)
+			seq_printf(s,
+				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				cmd->opcode, cmd->arg, cmd->flags,
+				cmd->resp[0], cmd->resp[1], cmd->resp[2],
+				cmd->resp[2], cmd->error);
+		if (data)
+			seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
+				data->bytes_xfered, data->blocks,
+				data->blksz, data->flags, data->error);
+		if (stop)
+			seq_printf(s,
+				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				stop->opcode, stop->arg, stop->flags,
+				stop->resp[0], stop->resp[1], stop->resp[2],
+				stop->resp[2], stop->error);
+	}
+
+	spin_unlock_bh(&slot->host->lock);
+
+	return 0;
+}
+
+static int at91mci_req_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, at91mci_req_show, inode->i_private);
+}
+
+static const struct file_operations at91mci_req_fops = {
+	.owner		= THIS_MODULE,
+	.open		= at91mci_req_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void at91mci_show_status_reg(struct seq_file *s,
+		const char *regname, u32 value)
+{
+	static const char	*sr_bit[] = {
+		[0]	= "CMDRDY",
+		[1]	= "RXRDY",
+		[2]	= "TXRDY",
+		[3]	= "BLKE",
+		[4]	= "DTIP",
+		[5]	= "NOTBUSY",
+		[6]	= "ENDRX",
+		[7]	= "ENDTX",
+		[8]	= "SDIOIRQA",
+		[9]	= "SDIOIRQB",
+		[14]	= "RXBUFF",
+		[15]	= "TXBUFE",
+		[16]	= "RINDE",
+		[17]	= "RDIRE",
+		[18]	= "RCRCE",
+		[19]	= "RENDE",
+		[20]	= "RTOE",
+		[21]	= "DCRCE",
+		[22]	= "DTOE",
+		[30]	= "OVRE",
+		[31]	= "UNRE",
+	};
+	unsigned int		i;
+
+	seq_printf(s, "%s:\t0x%08x", regname, value);
+	for (i = 0; i < ARRAY_SIZE(sr_bit); i++) {
+		if (value & (1 << i)) {
+			if (sr_bit[i])
+				seq_printf(s, " %s", sr_bit[i]);
+			else
+				seq_puts(s, " UNKNOWN");
+		}
+	}
+	seq_putc(s, '\n');
+}
+
+static int at91mci_regs_show(struct seq_file *s, void *v)
+{
+	struct at91_mci		*host = s->private;
+	u32			*buf;
+
+	buf = kmalloc(AT91_MCI_REGS_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	/*
+	 * Grab a more or less consistent snapshot. Note that we're
+	 * not disabling interrupts, so IMR and SR may not be
+	 * consistent.
+	 */
+	spin_lock_bh(&host->lock);
+	clk_enable(host->mck);
+	memcpy_fromio(buf, host->regs, AT91_MCI_REGS_SIZE);
+	clk_disable(host->mck);
+	spin_unlock_bh(&host->lock);
+
+	seq_printf(s, "MR:\t0x%08x%s%s CLKDIV=%u\n",
+		   buf[AT91_MCI_MR / 4],
+		   buf[AT91_MCI_MR / 4] & AT91_MCI_RDPROOF ? " RDPROOF" : "",
+		   buf[AT91_MCI_MR / 4] & AT91_MCI_WRPROOF ? " WRPROOF" : "",
+		   buf[AT91_MCI_MR / 4] & 0xff);
+	seq_printf(s, "DTOR:\t0x%08x\n", buf[AT91_MCI_DTOR / 4]);
+	seq_printf(s, "SDCR:\t0x%08x\n", buf[AT91_MCI_SDCR / 4]);
+	seq_printf(s, "ARGR:\t0x%08x\n", buf[AT91_MCI_ARGR / 4]);
+	seq_printf(s, "BLKR:\t0x%08x BCNT=%u BLKLEN=%u\n",
+		   buf[AT91_MCI_BLKR / 4],
+		   (buf[AT91_MCI_BLKR / 4] >> 0) & 0xffff,
+		   (buf[AT91_MCI_BLKR / 4] >> 16) & 0xffff);
+
+	/* Don't read RSPR and RDR; it will consume the data there */
+
+	at91mci_show_status_reg(s, "SR", buf[AT91_MCI_SR / 4]);
+	at91mci_show_status_reg(s, "IMR", buf[AT91_MCI_IMR / 4]);
+
+	kfree(buf);
+
+	return 0;
+}
+
+static int at91mci_regs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, at91mci_regs_show, inode->i_private);
+}
+
+static const struct file_operations at91mci_regs_fops = {
+	.owner		= THIS_MODULE,
+	.open		= at91mci_regs_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void at91mci_init_debugfs(struct at91_mci_slot *slot)
+{
+	struct mmc_host		*mmc = slot->mmc;
+	struct at91_mci		*host = slot->host;
+	struct dentry		*root;
+	struct dentry		*node;
+
+	root = mmc->debugfs_root;
+	if (!root)
+		return;
+
+	node = debugfs_create_file("regs", S_IRUSR, root, host,
+			&at91mci_regs_fops);
+	if (IS_ERR(node))
+		return;
+	if (!node)
+		goto err;
+
+	node = debugfs_create_file("req", S_IRUSR, root, slot, &at91mci_req_fops);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("pending_events", S_IRUSR, root,
+				     (u32 *)&host->pending_events);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("completed_events", S_IRUSR, root,
+				     (u32 *)&host->completed_events);
+	if (!node)
+		goto err;
+
+	return;
+
+err:
+	dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
+}
+
+static inline unsigned int ns_to_clocks(struct at91_mci *host,
+					unsigned int ns)
+{
+	return (ns * (host->bus_hz / 1000000) + 999) / 1000;
+}
+
+static void at91mci_set_timeout(struct at91_mci *host,
+		struct at91_mci_slot *slot, struct mmc_data *data)
+{
+	static unsigned	dtomul_to_shift[] = {
+		0, 4, 7, 8, 10, 12, 16, 20
+	};
+	unsigned	timeout;
+	unsigned	dtocyc;
+	unsigned	dtomul;
+
+	timeout = ns_to_clocks(host, data->timeout_ns) + data->timeout_clks;
+
+	for (dtomul = 0; dtomul < 8; dtomul++) {
+		unsigned shift = dtomul_to_shift[dtomul];
+		dtocyc = (timeout + (1 << shift) - 1) >> shift;
+		if (dtocyc < 15)
+			break;
+	}
+
+	if (dtomul >= 8) {
+		dtomul = 7;
+		dtocyc = 15;
+	}
+
+	dev_vdbg(&slot->mmc->class_dev, "setting timeout to %u cycles\n",
+			dtocyc << dtomul_to_shift[dtomul]);
+	at91_mci_write(host, AT91_MCI_DTOR,
+		       (AT91_MCI_DTOMUL_F(dtomul) | AT91_MCI_DTOCYC_F(dtocyc)));
+}
+
+/*
+ * Return mask with command flags to be enabled for this command.
+ */
+static u32 at91mci_prepare_command(struct mmc_host *mmc,
+				 struct mmc_command *cmd)
+{
+	struct mmc_data	*data;
+	u32		cmdr;
+
+	cmd->error = -EINPROGRESS;
+
+	cmdr = AT91_MCI_CMDNB & cmd->opcode;
+
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		if (cmd->flags & MMC_RSP_136)
+			cmdr |= AT91_MCI_RSPTYP_136;
+		else
+			cmdr |= AT91_MCI_RSPTYP_48;
+	}
+
+	/*
+	 * This should really be MAXLAT_5 for CMD2 and ACMD41, but
+	 * it's too difficult to determine whether this is an ACMD or
+	 * not. Better make it 64.
+	 */
+	cmdr |= AT91_MCI_MAXLAT;
+
+	if (mmc->ios.bus_mode == MMC_BUSMODE_OPENDRAIN)
+		cmdr |= AT91_MCI_OPDCMD;
+
+	data = cmd->data;
+	if (data) {
+		cmdr |= AT91_MCI_TRCMD_START;
+		if (data->flags & MMC_DATA_STREAM)
+			cmdr |= AT91_MCI_TRTYP_STREAM;
+		else if (data->blocks > 1)
+			cmdr |= AT91_MCI_TRTYP_MULTIPLE;
+		else
+			cmdr |= AT91_MCI_TRTYP_BLOCK;
+
+		if (data->flags & MMC_DATA_READ)
+			cmdr |= AT91_MCI_TRDIR;
+	}
+
+	return cmdr;
+}
+
+static void at91mci_start_command(struct at91_mci *host,
+		struct mmc_command *cmd, u32 cmd_flags)
+{
+	WARN_ON(host->cmd);
+	host->cmd = cmd;
+
+	dev_vdbg(&host->pdev->dev,
+			"start command: ARGR=0x%08x CMDR=0x%08x\n",
+			cmd->arg, cmd_flags);
+
+	at91_mci_write(host, AT91_MCI_ARGR, cmd->arg);
+	at91_mci_write(host, AT91_MCI_CMDR, cmd_flags);
+}
+
+static void send_stop_cmd(struct at91_mci *host, struct mmc_data *data)
+{
+	at91mci_start_command(host, data->stop, host->stop_cmdr);
+	at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY);
+}
+
+#ifdef CONFIG_MMC_AT91GEN2_DMA
+#error "DMA Unsupported for AT91 MMC Gen2 Driver"
+#else /* CONFIG_MMC_AT91GEN2_DMA */
+
+static int at91mci_submit_data_dma(struct at91_mci *host, struct
mmc_data *data)
+{
+	return -ENOSYS;
+}
+
+static void at91mci_stop_dma(struct at91_mci *host)
+{
+	/* Data transfer was stopped by the interrupt handler */
+	at91mci_set_pending(host, EVENT_XFER_COMPLETE);
+	at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
+}
+
+#endif /* CONFIG_MMC_AT91GEN2_DMA */
+
+/*
+ * Returns a mask of interrupt flags to be enabled after the whole
+ * request has been prepared.
+ */
+static u32 at91mci_submit_data(struct at91_mci *host, struct mmc_data *data)
+{
+	u32 iflags;
+
+	data->error = -EINPROGRESS;
+
+	WARN_ON(host->data);
+	host->sg = NULL;
+	host->data = data;
+
+	iflags = AT91MCI_DATA_ERROR_FLAGS;
+	if (at91mci_submit_data_dma(host, data)) {
+		host->data_chan = NULL;
+
+		/*
+		 * Errata: MMC data write operation with less than 12
+		 * bytes is impossible.
+		 */
+		if (data->blocks * data->blksz < 12)
+			host->need_reset = true;
+
+		host->sg = data->sg;
+		host->pio_offset = 0;
+		if (data->flags & MMC_DATA_READ)
+			iflags |= AT91_MCI_RXRDY;
+		else
+			iflags |= AT91_MCI_TXRDY;
+	}
+
+	return iflags;
+}
+
+static void at91mci_start_request(struct at91_mci *host,
+		struct at91_mci_slot *slot)
+{
+	struct mmc_request	*mrq;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+	u32			iflags;
+	u32			cmdflags;
+
+	mrq = slot->mrq;
+	host->cur_slot = slot;
+	host->mrq = mrq;
+
+	host->pending_events = 0;
+	host->completed_events = 0;
+	host->data_status = 0;
+
+	if (host->need_reset || MMC_ALWAYS_RESET) {
+		at91_mci_write(host, AT91_MCI_CR, AT91_MCI_SWRST);
+		at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
+		at91_mci_write(host, AT91_MCI_MR, host->mode_reg);
+		host->need_reset = false;
+	}
+	at91_mci_write(host, AT91_MCI_SDCR, slot->sdc_reg);
+
+	iflags = at91_mci_read(host, AT91_MCI_IMR);
+	if (iflags)
+		dev_warn(&slot->mmc->class_dev, "WARNING: IMR=0x%08x\n",
+				iflags);
+
+	if (unlikely(test_and_clear_bit(AT91MCI_CARD_NEED_INIT, &slot->flags))) {
+		/* Send init sequence (74 clock cycles) */
+		at91_mci_write(host, AT91_MCI_CMDR, AT91_MCI_SPCMD_INIT);
+		while (!(at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_CMDRDY))
+			cpu_relax();
+	}
+	data = mrq->data;
+	if (data) {
+		at91mci_set_timeout(host, slot, data);
+
+		/* Must set block count/size before sending command */
+		at91_mci_write(host, AT91_MCI_BLKR, AT91_MCI_BLKR_BCNT(data->blocks)
+				| AT91_MCI_BLKR_BLKLEN(data->blksz));
+		dev_vdbg(&slot->mmc->class_dev, "BLKR=0x%08x\n",
+			AT91_MCI_BLKR_BCNT(data->blocks) | AT91_MCI_BLKR_BLKLEN(data->blksz));
+	}
+
+	iflags = AT91_MCI_CMDRDY;
+	cmd = mrq->cmd;
+	cmdflags = at91mci_prepare_command(slot->mmc, cmd);
+	at91mci_start_command(host, cmd, cmdflags);
+
+	if (data)
+		iflags |= at91mci_submit_data(host, data);
+
+	if (mrq->stop) {
+		host->stop_cmdr = at91mci_prepare_command(slot->mmc, mrq->stop);
+		host->stop_cmdr |= AT91_MCI_TRCMD_STOP;
+		if (!(data->flags & MMC_DATA_WRITE))
+			host->stop_cmdr |= AT91_MCI_TRDIR;
+		if (data->flags & MMC_DATA_STREAM)
+			host->stop_cmdr |= AT91_MCI_TRTYP_STREAM;
+		else
+			host->stop_cmdr |= AT91_MCI_TRTYP_MULTIPLE;
+	}
+
+	/*
+	 * We could have enabled interrupts earlier, but I suspect
+	 * that would open up a nice can of interesting race
+	 * conditions (e.g. command and data complete, but stop not
+	 * prepared yet.)
+	 */
+	at91_mci_write(host, AT91_MCI_IER, iflags);
+}
+
+static void at91mci_queue_request(struct at91_mci *host,
+		struct at91_mci_slot *slot, struct mmc_request *mrq)
+{
+	dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
+			host->state);
+
+	spin_lock_bh(&host->lock);
+	slot->mrq = mrq;
+	if (host->state == STATE_IDLE) {
+		host->state = STATE_SENDING_CMD;
+		at91mci_start_request(host, slot);
+	} else {
+		list_add_tail(&slot->queue_node, &host->queue);
+	}
+	spin_unlock_bh(&host->lock);
+}
+
+static void at91mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct at91_mci_slot	*slot = mmc_priv(mmc);
+	struct at91_mci		*host = slot->host;
+	struct mmc_data		*data;
+
+	WARN_ON(slot->mrq);
+
+	/*
+	 * We may "know" the card is gone even though there's still an
+	 * electrical connection. If so, we really need to communicate
+	 * this to the MMC core since there won't be any more
+	 * interrupts as the card is completely removed. Otherwise,
+	 * the MMC core might believe the card is still there even
+	 * though the card was just removed very slowly.
+	 */
+	if (!test_bit(AT91MCI_CARD_PRESENT, &slot->flags)) {
+		mrq->cmd->error = -ENOMEDIUM;
+		mmc_request_done(mmc, mrq);
+		return;
+	}
+
+	/* We don't support multiple blocks of weird lengths. */
+	data = mrq->data;
+	if (data && data->blocks > 1 && data->blksz & 3) {
+		mrq->cmd->error = -EINVAL;
+		mmc_request_done(mmc, mrq);
+	}
+
+	at91mci_queue_request(host, slot, mrq);
+}
+
+static void at91mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct at91_mci_slot	*slot = mmc_priv(mmc);
+	struct at91_mci		*host = slot->host;
+	unsigned int		i;
+
+	slot->sdc_reg &= ~AT91_MCI_SDCBUS;
+	switch (ios->bus_width) {
+	case MMC_BUS_WIDTH_4:
+		slot->sdc_reg |= AT91_MCI_SDCBUS;
+		break;
+	default:
+		break;
+	}
+
+	if (ios->clock) {
+		unsigned int clock_min = ~0U;
+		u32 clkdiv;
+
+		spin_lock_bh(&host->lock);
+		if (!host->mode_reg) {
+			clk_enable(host->mck);
+			at91_mci_write(host, AT91_MCI_CR, AT91_MCI_SWRST);
+			at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
+		}
+
+		/*
+		 * Use mirror of ios->clock to prevent race with mmc
+		 * core ios update when finding the minimum.
+		 */
+		slot->clock = ios->clock;
+		for (i = 0; i < AT91_MCI_MAX_NR_SLOTS; i++) {
+			if (host->slot[i] && host->slot[i]->clock
+					&& host->slot[i]->clock < clock_min)
+				clock_min = host->slot[i]->clock;
+		}
+
+		/* Calculate clock divider */
+		clkdiv = DIV_ROUND_UP(host->bus_hz, 2 * clock_min) - 1;
+		if (clkdiv > 255) {
+			dev_warn(&mmc->class_dev,
+				"clock %u too slow; using %lu\n",
+				clock_min, host->bus_hz / (2 * 256));
+			clkdiv = 255;
+		}
+
+		/*
+		 * WRPROOF and RDPROOF prevent overruns/underruns by
+		 * stopping the clock when the FIFO is full/empty.
+		 * This state is not expected to last for long.
+		 */
+#ifdef MMC_SUPPORTS_RW_PROOF
+		host->mode_reg = (AT91_MCI_CLKDIV & clkdiv) | AT91_MCI_WRPROOF
+					| AT91_MCI_RDPROOF;
+#else
+		host->mode_reg = (AT91_MCI_CLKDIV & clkdiv);
+#endif
+		if (list_empty(&host->queue))
+			at91_mci_write(host, AT91_MCI_MR, host->mode_reg);
+		else
+			host->need_clock_update = true;
+
+		spin_unlock_bh(&host->lock);
+	} else {
+		bool any_slot_active = false;
+
+		spin_lock_bh(&host->lock);
+		slot->clock = 0;
+		for (i = 0; i < AT91_MCI_MAX_NR_SLOTS; i++) {
+			if (host->slot[i] && host->slot[i]->clock) {
+				any_slot_active = true;
+				break;
+			}
+		}
+		if (!any_slot_active) {
+			at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS);
+			if (host->mode_reg) {
+				at91_mci_read(host, AT91_MCI_MR);
+				clk_disable(host->mck);
+			}
+			host->mode_reg = 0;
+		}
+		spin_unlock_bh(&host->lock);
+	}
+
+	switch (ios->power_mode) {
+	case MMC_POWER_UP:
+		set_bit(AT91MCI_CARD_NEED_INIT, &slot->flags);
+		break;
+	default:
+		/*
+		 * TODO: None of the currently available AVR32-based
+		 * boards allow MMC power to be turned off. Implement
+		 * power control when this can be tested properly.
+		 *
+		 * We also need to hook this into the clock management
+		 * somehow so that newly inserted cards aren't
+		 * subjected to a fast clock before we have a chance
+		 * to figure out what the maximum rate is. Currently,
+		 * there's no way to avoid this, and there never will
+		 * be for boards that don't support power control.
+		 */
+		break;
+	}
+}
+
+static int at91mci_get_ro(struct mmc_host *mmc)
+{
+	int			read_only = -ENOSYS;
+	struct at91_mci_slot	*slot = mmc_priv(mmc);
+
+	if (gpio_is_valid(slot->wp_pin)) {
+		read_only = gpio_get_value(slot->wp_pin);
+		dev_dbg(&mmc->class_dev, "card is %s\n",
+				read_only ? "read-only" : "read-write");
+	}
+
+	return read_only;
+}
+
+static int at91mci_get_cd(struct mmc_host *mmc)
+{
+	int			present = -ENOSYS;
+	struct at91_mci_slot	*slot = mmc_priv(mmc);
+
+	if (gpio_is_valid(slot->det_pin)) {
+		present = !gpio_get_value(slot->det_pin);
+		dev_dbg(&mmc->class_dev, "card is %spresent\n",
+				present ? "" : "not ");
+	}
+
+	return present;
+}
+
+static const struct mmc_host_ops at91mci_ops = {
+	.request	= at91mci_request,
+	.set_ios	= at91mci_set_ios,
+	.get_ro		= at91mci_get_ro,
+	.get_cd		= at91mci_get_cd,
+};
+
+/* Called with host->lock held */
+static void at91mci_request_end(struct at91_mci *host, struct mmc_request *mrq)
+	__releases(&host->lock)
+	__acquires(&host->lock)
+{
+	struct at91_mci_slot	*slot = NULL;
+	struct mmc_host		*prev_mmc = host->cur_slot->mmc;
+
+	WARN_ON(host->cmd || host->data);
+
+	/*
+	 * Update the MMC clock rate if necessary. This may be
+	 * necessary if set_ios() is called when a different slot is
+	 * busy transfering data.
+	 */
+	if (host->need_clock_update)
+		at91_mci_write(host, AT91_MCI_MR, host->mode_reg);
+
+	host->cur_slot->mrq = NULL;
+	host->mrq = NULL;
+	if (!list_empty(&host->queue)) {
+		slot = list_entry(host->queue.next,
+				struct at91_mci_slot, queue_node);
+		list_del(&slot->queue_node);
+		dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
+				mmc_hostname(slot->mmc));
+		host->state = STATE_SENDING_CMD;
+		at91mci_start_request(host, slot);
+	} else {
+		dev_vdbg(&host->pdev->dev, "list empty\n");
+		host->state = STATE_IDLE;
+	}
+
+	spin_unlock(&host->lock);
+	mmc_request_done(prev_mmc, mrq);
+	spin_lock(&host->lock);
+}
+
+static void at91mci_command_complete(struct at91_mci *host,
+			struct mmc_command *cmd)
+{
+	u32		status = host->cmd_status;
+
+	/* Read the response from the card (up to 16 bytes) */
+	cmd->resp[0] = at91_mci_read(host, AT91_MCI_RSPR(0));
+	cmd->resp[1] = at91_mci_read(host, AT91_MCI_RSPR(1));
+	cmd->resp[2] = at91_mci_read(host, AT91_MCI_RSPR(2));
+	cmd->resp[3] = at91_mci_read(host, AT91_MCI_RSPR(3));
+
+	if (status & AT91_MCI_RTOE)
+		cmd->error = -ETIMEDOUT;
+	else if ((cmd->flags & MMC_RSP_CRC) && (status & AT91_MCI_RCRCE))
+		cmd->error = -EILSEQ;
+	else if (status & (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RENDE))
+		cmd->error = -EIO;
+	else
+		cmd->error = 0;
+
+	if (cmd->error) {
+		dev_dbg(&host->pdev->dev,
+			"command error: status=0x%08x\n", status);
+
+		if (cmd->data) {
+			host->data = NULL;
+			at91mci_stop_dma(host);
+			at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_NOTBUSY
+					| AT91_MCI_TXRDY | AT91_MCI_RXRDY
+					| AT91MCI_DATA_ERROR_FLAGS);
+		}
+	}
+}
+
+static void at91mci_detect_change(unsigned long data)
+{
+	struct at91_mci_slot	*slot = (struct at91_mci_slot *)data;
+	bool			present;
+	bool			present_old;
+
+	/*
+	 * at91mci_cleanup_slot() sets the AT91MCI_SHUTDOWN flag before
+	 * freeing the interrupt. We must not re-enable the interrupt
+	 * if it has been freed, and if we're shutting down, it
+	 * doesn't really matter whether the card is present or not.
+	 */
+	smp_rmb();
+	if (test_bit(AT91MCI_SHUTDOWN, &slot->flags))
+		return;
+
+	enable_irq(gpio_to_irq(slot->det_pin));
+	present = !gpio_get_value(slot->det_pin);
+	present_old = test_bit(AT91MCI_CARD_PRESENT, &slot->flags);
+
+	dev_vdbg(&slot->mmc->class_dev, "detect change: %d (was %d)\n",
+			present, present_old);
+
+	if (present != present_old) {
+		struct at91_mci	*host = slot->host;
+		struct mmc_request	*mrq;
+
+		dev_dbg(&slot->mmc->class_dev, "card %s\n",
+			present ? "inserted" : "removed");
+
+		spin_lock(&host->lock);
+
+		if (!present)
+			clear_bit(AT91MCI_CARD_PRESENT, &slot->flags);
+		else
+			set_bit(AT91MCI_CARD_PRESENT, &slot->flags);
+
+		/* Clean up queue if present */
+		mrq = slot->mrq;
+		if (mrq) {
+			if (mrq == host->mrq) {
+				/*
+				 * Reset controller to terminate any ongoing
+				 * commands or data transfers.
+				 */
+				at91_mci_write(host, AT91_MCI_CR, AT91_MCI_SWRST);
+				at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
+				at91_mci_write(host, AT91_MCI_MR, host->mode_reg);
+
+				host->data = NULL;
+				host->cmd = NULL;
+
+				switch (host->state) {
+				case STATE_IDLE:
+					break;
+				case STATE_SENDING_CMD:
+					mrq->cmd->error = -ENOMEDIUM;
+					if (!mrq->data)
+						break;
+					/* fall through */
+				case STATE_SENDING_DATA:
+					mrq->data->error = -ENOMEDIUM;
+					at91mci_stop_dma(host);
+					break;
+				case STATE_DATA_BUSY:
+				case STATE_DATA_ERROR:
+					if (mrq->data->error == -EINPROGRESS)
+						mrq->data->error = -ENOMEDIUM;
+					if (!mrq->stop)
+						break;
+					/* fall through */
+				case STATE_SENDING_STOP:
+					mrq->stop->error = -ENOMEDIUM;
+					break;
+				}
+
+				at91mci_request_end(host, mrq);
+			} else {
+				list_del(&slot->queue_node);
+				mrq->cmd->error = -ENOMEDIUM;
+				if (mrq->data)
+					mrq->data->error = -ENOMEDIUM;
+				if (mrq->stop)
+					mrq->stop->error = -ENOMEDIUM;
+
+				spin_unlock(&host->lock);
+				mmc_request_done(slot->mmc, mrq);
+				spin_lock(&host->lock);
+			}
+		}
+		spin_unlock(&host->lock);
+
+		mmc_detect_change(slot->mmc, 0);
+	}
+}
+
+static void at91mci_tasklet_func(unsigned long priv)
+{
+	struct at91_mci		*host = (struct at91_mci *)priv;
+	struct mmc_request	*mrq = host->mrq;
+	struct mmc_data		*data = host->data;
+	struct mmc_command	*cmd = host->cmd;
+	enum at91_mci_state	state = host->state;
+	enum at91_mci_state	prev_state;
+	u32			status;
+
+	spin_lock(&host->lock);
+
+	state = host->state;
+
+	dev_vdbg(&host->pdev->dev,
+		"tasklet: state %u pending/completed/mask %lx/%lx/%x\n",
+		state, host->pending_events, host->completed_events,
+		at91_mci_read(host, AT91_MCI_IMR));
+
+	do {
+		prev_state = state;
+
+		switch (state) {
+		case STATE_IDLE:
+			break;
+
+		case STATE_SENDING_CMD:
+			if (!at91mci_test_and_clear_pending(host,
+						EVENT_CMD_COMPLETE))
+				break;
+
+			host->cmd = NULL;
+			at91mci_set_completed(host, EVENT_CMD_COMPLETE);
+			at91mci_command_complete(host, mrq->cmd);
+			if (!mrq->data || cmd->error) {
+				at91mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_DATA;
+			/* fall through */
+
+		case STATE_SENDING_DATA:
+			if (at91mci_test_and_clear_pending(host,
+						EVENT_DATA_ERROR)) {
+				at91mci_stop_dma(host);
+				if (data->stop)
+					send_stop_cmd(host, data);
+				state = STATE_DATA_ERROR;
+				break;
+			}
+
+			if (!at91mci_test_and_clear_pending(host,
+						EVENT_XFER_COMPLETE))
+				break;
+
+			at91mci_set_completed(host, EVENT_XFER_COMPLETE);
+			prev_state = state = STATE_DATA_BUSY;
+			/* fall through */
+
+		case STATE_DATA_BUSY:
+			if (!at91mci_test_and_clear_pending(host,
+						EVENT_DATA_COMPLETE))
+				break;
+
+			host->data = NULL;
+			at91mci_set_completed(host, EVENT_DATA_COMPLETE);
+			status = host->data_status;
+			if (unlikely(status & AT91MCI_DATA_ERROR_FLAGS)) {
+				if (status & AT91_MCI_DTOE) {
+					dev_dbg(&host->pdev->dev,
+							"data timeout error\n");
+					data->error = -ETIMEDOUT;
+				} else if (status & AT91_MCI_DCRCE) {
+					dev_dbg(&host->pdev->dev,
+							"data CRC error\n");
+					data->error = -EILSEQ;
+				} else {
+					dev_dbg(&host->pdev->dev,
+						"data FIFO error (status=%08x)\n",
+						status);
+					data->error = -EIO;
+				}
+			} else {
+				data->bytes_xfered = data->blocks * data->blksz;
+				data->error = 0;
+			}
+
+			if (!data->stop) {
+				at91mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_STOP;
+			if (!data->error)
+				send_stop_cmd(host, data);
+			/* fall through */
+
+		case STATE_SENDING_STOP:
+			if (!at91mci_test_and_clear_pending(host,
+						EVENT_CMD_COMPLETE))
+				break;
+
+			host->cmd = NULL;
+			at91mci_command_complete(host, mrq->stop);
+			at91mci_request_end(host, host->mrq);
+			goto unlock;
+
+		case STATE_DATA_ERROR:
+			if (!at91mci_test_and_clear_pending(host,
+						EVENT_XFER_COMPLETE))
+				break;
+
+			state = STATE_DATA_BUSY;
+			break;
+		}
+	} while (state != prev_state);
+
+	host->state = state;
+
+unlock:
+	spin_unlock(&host->lock);
+}
+
+static void at91mci_read_data_pio(struct at91_mci *host)
+{
+	struct scatterlist	*sg = host->sg;
+	void			*buf = sg_virt(sg);
+	unsigned int		offset = host->pio_offset;
+	struct mmc_data		*data = host->data;
+	u32			value;
+	u32			status;
+	unsigned int		nbytes = 0;
+
+	do {
+		value = at91_mci_read(host, AT91_MCI_RDR);
+		if (likely(offset + 4 <= sg->length)) {
+			put_unaligned(value, (u32 *)(buf + offset));
+
+			offset += 4;
+			nbytes += 4;
+
+			if (offset == sg->length) {
+				flush_dcache_page(sg_page(sg));
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+			memcpy(buf + offset, &value, remaining);
+			nbytes += remaining;
+
+			flush_dcache_page(sg_page(sg));
+			host->sg = sg = sg_next(sg);
+			if (!sg)
+				goto done;
+
+			offset = 4 - remaining;
+			buf = sg_virt(sg);
+			memcpy(buf, (u8 *)&value + remaining, offset);
+			nbytes += offset;
+		}
+
+		status = at91_mci_read(host, AT91_MCI_SR);
+		if (status & AT91MCI_DATA_ERROR_FLAGS) {
+			at91_mci_write(host, AT91_MCI_IDR, (AT91_MCI_NOTBUSY | AT91_MCI_RXRDY
+						| AT91MCI_DATA_ERROR_FLAGS));
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+			smp_wmb();
+			at91mci_set_pending(host, EVENT_DATA_ERROR);
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+	} while (status & AT91_MCI_RXRDY);
+
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+
+	return;
+
+done:
+	at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_RXRDY);
+	at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	at91mci_set_pending(host, EVENT_XFER_COMPLETE);
+}
+
+static void at91mci_write_data_pio(struct at91_mci *host)
+{
+	struct scatterlist	*sg = host->sg;
+	void			*buf = sg_virt(sg);
+	unsigned int		offset = host->pio_offset;
+	struct mmc_data		*data = host->data;
+	u32			value;
+	u32			status;
+	unsigned int		nbytes = 0;
+
+	do {
+		if (likely(offset + 4 <= sg->length)) {
+			value = get_unaligned((u32 *)(buf + offset));
+			at91_mci_write(host, AT91_MCI_TDR, value);
+
+			offset += 4;
+			nbytes += 4;
+			if (offset == sg->length) {
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+
+			value = 0;
+			memcpy(&value, buf + offset, remaining);
+			nbytes += remaining;
+
+			host->sg = sg = sg_next(sg);
+			if (!sg) {
+				at91_mci_write(host, AT91_MCI_TDR, value);
+				goto done;
+			}
+
+			offset = 4 - remaining;
+			buf = sg_virt(sg);
+			memcpy((u8 *)&value + remaining, buf, offset);
+			at91_mci_write(host, AT91_MCI_TDR, value);
+			nbytes += offset;
+		}
+
+		status = at91_mci_read(host, AT91_MCI_SR);
+		if (status & AT91MCI_DATA_ERROR_FLAGS) {
+			at91_mci_write(host, AT91_MCI_IDR, (AT91_MCI_NOTBUSY | AT91_MCI_TXRDY
+						| AT91MCI_DATA_ERROR_FLAGS));
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+			smp_wmb();
+			at91mci_set_pending(host, EVENT_DATA_ERROR);
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+	} while (status & AT91_MCI_TXRDY);
+
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+
+	return;
+
+done:
+	at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXRDY);
+	at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	at91mci_set_pending(host, EVENT_XFER_COMPLETE);
+}
+
+static void at91mci_cmd_interrupt(struct at91_mci *host, u32 status)
+{
+	at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_CMDRDY);
+
+	host->cmd_status = status;
+	smp_wmb();
+	at91mci_set_pending(host, EVENT_CMD_COMPLETE);
+	tasklet_schedule(&host->tasklet);
+}
+
+static irqreturn_t at91mci_interrupt(int irq, void *dev_id)
+{
+	struct at91_mci		*host = dev_id;
+	u32			status, mask, pending;
+	unsigned int		pass_count = 0;
+
+	do {
+		status = at91_mci_read(host, AT91_MCI_SR);
+		mask = at91_mci_read(host, AT91_MCI_IMR);
+		pending = status & mask;
+		if (!pending)
+			break;
+
+		if (pending & AT91MCI_DATA_ERROR_FLAGS) {
+			at91_mci_write(host, AT91_MCI_IDR, AT91MCI_DATA_ERROR_FLAGS
+					| AT91_MCI_RXRDY | AT91_MCI_TXRDY);
+			pending &= at91_mci_read(host, AT91_MCI_IMR);
+
+			host->data_status = status;
+			smp_wmb();
+			at91mci_set_pending(host, EVENT_DATA_ERROR);
+			tasklet_schedule(&host->tasklet);
+		}
+		if (pending & AT91_MCI_NOTBUSY) {
+			at91_mci_write(host, AT91_MCI_IDR,
+					AT91MCI_DATA_ERROR_FLAGS | AT91_MCI_NOTBUSY);
+			if (!host->data_status)
+				host->data_status = status;
+			smp_wmb();
+			at91mci_set_pending(host, EVENT_DATA_COMPLETE);
+			tasklet_schedule(&host->tasklet);
+		}
+		if (pending & AT91_MCI_RXRDY)
+			at91mci_read_data_pio(host);
+		if (pending & AT91_MCI_TXRDY)
+			at91mci_write_data_pio(host);
+
+		if (pending & AT91_MCI_CMDRDY)
+			at91mci_cmd_interrupt(host, status);
+	} while (pass_count++ < 5);
+
+	return pass_count ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static irqreturn_t at91mci_detect_interrupt(int irq, void *dev_id)
+{
+	struct at91_mci_slot	*slot = dev_id;
+
+	/*
+	 * Disable interrupts until the pin has stabilized and check
+	 * the state then. Use mod_timer() since we may be in the
+	 * middle of the timer routine when this interrupt triggers.
+	 */
+	disable_irq_nosync(irq);
+	mod_timer(&slot->detect_timer, jiffies + msecs_to_jiffies(20));
+
+	return IRQ_HANDLED;
+}
+
+static int __init at91mci_init_slot(struct at91_mci *host,
+		struct at91_mmc_slot_pdata *slot_data, unsigned int id,
+		u32 sdc_reg)
+{
+	struct mmc_host			*mmc;
+	struct at91_mci_slot		*slot;
+
+	mmc = mmc_alloc_host(sizeof(struct at91_mci_slot), &host->pdev->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	slot = mmc_priv(mmc);
+	slot->mmc = mmc;
+	slot->host = host;
+	slot->det_pin = slot_data->det_pin;
+	slot->wp_pin = slot_data->wp_pin;
+	slot->sdc_reg = sdc_reg;
+
+	mmc->ops = &at91mci_ops;
+	mmc->f_min = DIV_ROUND_UP(host->bus_hz, 512);
+	mmc->f_max = host->bus_hz / 2;
+	mmc->ocr_avail	= MMC_VDD_32_33 | MMC_VDD_33_34;
+	if (slot_data->bus_width >= 4)
+		mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+	mmc->max_hw_segs = 64;
+	mmc->max_phys_segs = 64;
+	mmc->max_req_size = 32768 * 512;
+	mmc->max_blk_size = 32768;
+	mmc->max_blk_count = 512;
+
+	/* Assume card is present initially */
+	set_bit(AT91MCI_CARD_PRESENT, &slot->flags);
+	if (gpio_is_valid(slot->det_pin)) {
+		if (gpio_request(slot->det_pin, "mmc_detect")) {
+			dev_dbg(&mmc->class_dev, "no detect pin available\n");
+			slot->det_pin = -EBUSY;
+		} else if (gpio_get_value(slot->det_pin)) {
+			clear_bit(AT91MCI_CARD_PRESENT, &slot->flags);
+		}
+	}
+
+	if (!gpio_is_valid(slot->det_pin))
+		mmc->caps |= MMC_CAP_NEEDS_POLL;
+
+	if (gpio_is_valid(slot->wp_pin)) {
+		if (gpio_request(slot->wp_pin, "mmc_wp")) {
+			dev_dbg(&mmc->class_dev, "no WP pin available\n");
+			slot->wp_pin = -EBUSY;
+		}
+	}
+
+	host->slot[id] = slot;
+	mmc_add_host(mmc);
+
+	if (gpio_is_valid(slot->det_pin)) {
+		int ret;
+
+		setup_timer(&slot->detect_timer, at91mci_detect_change,
+				(unsigned long)slot);
+
+		ret = request_irq(gpio_to_irq(slot->det_pin),
+				at91mci_detect_interrupt,
+				IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+				"mmc-detect", slot);
+		if (ret) {
+			dev_dbg(&mmc->class_dev,
+				"could not request IRQ %d for detect pin\n",
+				gpio_to_irq(slot->det_pin));
+			gpio_free(slot->det_pin);
+			slot->det_pin = -EBUSY;
+		}
+	}
+
+	at91mci_init_debugfs(slot);
+
+	return 0;
+}
+
+static void __exit at91mci_cleanup_slot(struct at91_mci_slot *slot,
+		unsigned int id)
+{
+	/* Debugfs stuff is cleaned up by mmc core */
+
+	set_bit(AT91MCI_SHUTDOWN, &slot->flags);
+	smp_wmb();
+
+	mmc_remove_host(slot->mmc);
+
+	if (gpio_is_valid(slot->det_pin)) {
+		int pin = slot->det_pin;
+
+		free_irq(gpio_to_irq(pin), slot);
+		del_timer_sync(&slot->detect_timer);
+		gpio_free(pin);
+	}
+	if (gpio_is_valid(slot->wp_pin))
+		gpio_free(slot->wp_pin);
+
+	slot->host->slot[id] = NULL;
+	mmc_free_host(slot->mmc);
+}
+
+static int __init at91mci_probe(struct platform_device *pdev)
+{
+	struct at91_mmc_data		*pdata;
+	struct at91_mci			*host;
+	struct resource			*regs;
+	unsigned int			nr_slots;
+	int				irq;
+	int				ret;
+
+	printk("Probing for AT91 MMC using Gen2 Driver\n");
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs)
+		return -ENXIO;
+	pdata = pdev->dev.platform_data;
+	if (!pdata)
+		return -ENXIO;
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	host = kzalloc(sizeof(struct at91_mci), GFP_KERNEL);
+	if (!host)
+		return -ENOMEM;
+
+	host->pdev = pdev;
+	spin_lock_init(&host->lock);
+	INIT_LIST_HEAD(&host->queue);
+
+	host->mck = clk_get(&pdev->dev, "mci_clk");
+	if (IS_ERR(host->mck)) {
+		ret = PTR_ERR(host->mck);
+		goto err_clk_get;
+	}
+
+	ret = -ENOMEM;
+	host->regs = ioremap(regs->start, regs->end - regs->start + 1);
+	if (!host->regs)
+		goto err_ioremap;
+
+	clk_enable(host->mck);
+	at91_mci_write(host, AT91_MCI_CR, AT91_MCI_SWRST);
+	host->bus_hz = clk_get_rate(host->mck);
+	clk_disable(host->mck);
+
+	host->mapbase = regs->start;
+
+	tasklet_init(&host->tasklet, at91mci_tasklet_func, (unsigned long)host);
+
+	ret = request_irq(irq, at91mci_interrupt, 0, pdev->dev.bus_id, host);
+	if (ret)
+		goto err_request_irq;
+
+	platform_set_drvdata(pdev, host);
+
+	/* We need at least one slot to succeed */
+	nr_slots = 0;
+	ret = -ENODEV;
+	if (pdata->slot[0].bus_width) {
+		ret = at91mci_init_slot(host, &pdata->slot[0],
+				AT91_MCI_SDCSEL & 0x0, 0);
+		if (!ret)
+			nr_slots++;
+	}
+	if (pdata->slot[1].bus_width) {
+		ret = at91mci_init_slot(host, &pdata->slot[1],
+				AT91_MCI_SDCSEL & 0x1, 1);
+		if (!ret)
+			nr_slots++;
+	}
+
+	if (!nr_slots) {
+		printk(KERN_ERR "AT91 MCI controller init failed.
at91mci_init_slot error or no slots with bus_width > 0.\n");
+		goto err_init_slot;
+	}
+
+	dev_info(&pdev->dev,
+			"AT91 MCI controller at 0x%08lx irq %d, %u slots\n",
+			host->mapbase, irq, nr_slots);
+
+	return 0;
+
+err_init_slot:
+	free_irq(irq, host);
+err_request_irq:
+	iounmap(host->regs);
+err_ioremap:
+	clk_put(host->mck);
+err_clk_get:
+	kfree(host);
+	return ret;
+}
+
+static int __exit at91mci_remove(struct platform_device *pdev)
+{
+	struct at91_mci		*host = platform_get_drvdata(pdev);
+	unsigned int		i;
+
+	platform_set_drvdata(pdev, NULL);
+
+	for (i = 0; i < AT91_MCI_MAX_NR_SLOTS; i++) {
+		if (host->slot[i])
+			at91mci_cleanup_slot(host->slot[i], i);
+	}
+
+	clk_enable(host->mck);
+	at91_mci_write(host, AT91_MCI_IDR, ~0UL);
+	at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS);
+	at91_mci_read(host, AT91_MCI_SR);
+	clk_disable(host->mck);
+
+	free_irq(platform_get_irq(pdev, 0), host);
+	iounmap(host->regs);
+
+	clk_put(host->mck);
+	kfree(host);
+
+	return 0;
+}
+
+static struct platform_driver at91mci_driver = {
+	.remove		= __exit_p(at91mci_remove),
+	.driver		= {
+		.name		= DRIVER_NAME,
+	},
+};
+
+static int __init at91mci_init(void)
+{
+	return platform_driver_probe(&at91mci_driver, at91mci_probe);
+}
+
+static void __exit at91mci_exit(void)
+{
+	platform_driver_unregister(&at91mci_driver);
+}
+
+module_init(at91mci_init);
+module_exit(at91mci_exit);
+
+MODULE_DESCRIPTION("Atmel AT91 Multimedia Card Interface driver 2nd gen");
+MODULE_AUTHOR("Rob Emanuele <poorarm@shoreis.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:at91_mci");

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH][Updated] New AT91 MCI Driver that supports both MCI slots used at the same time
  2009-06-03 19:02         ` [PATCH][Updated] " Rob Emanuele
@ 2009-06-08 10:47           ` Haavard Skinnemoen
  2009-06-08 16:58             ` Rob Emanuele
  0 siblings, 1 reply; 11+ messages in thread
From: Haavard Skinnemoen @ 2009-06-08 10:47 UTC (permalink / raw)
  To: Rob Emanuele
  Cc: Joey Oravec, Nicolas Ferre, linux-arm-kernel, linux-kernel,
	Haavard Skinnemoen

Rob Emanuele wrote:
> This driver is a port the Atmel AVR32 MCI driver which uses similar silicon.

While I do understand the motivation for this patch, I was kind of
hoping we could reduce the number of drivers for the Atmel MCI
hardware, not increase it...

So I think it would be much better if your changes were integrated into
one of the existing drivers.

> Joey Oravec has mentioned that he has had better reliability with the chip
> he is using (AT91SAM9261) if the MMC controller is reset before each
> command.  There is a compile time option to do just that.

This could be a useful option for atmel-mci too.

> Joey Oravec has stated that the Read and Write Proof functionality that
> prevents buffer overflows is not available on the AT91SAM9261.  This was
> confirmed by Nicolas Ferre and also affects the AT91RM9200.  This new
> patch excludes those register config bits.

Preferably use the cpu_is_xxx() functions to achieve that. Perhaps even
wrap those tests inside a mci_has_xxproof() function to make it
clearer what you're testing for, and easier to change if we decide to
do the test based on version registers, etc. later.

Haavard

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH][Updated] New AT91 MCI Driver that supports both MCI slots  used at the same time
  2009-06-08 10:47           ` Haavard Skinnemoen
@ 2009-06-08 16:58             ` Rob Emanuele
  2009-06-08 18:59               ` Rob Emanuele
  0 siblings, 1 reply; 11+ messages in thread
From: Rob Emanuele @ 2009-06-08 16:58 UTC (permalink / raw)
  To: Haavard Skinnemoen
  Cc: Joey Oravec, Nicolas Ferre, linux-arm-kernel, linux-kernel,
	Haavard Skinnemoen

Hi Haavard,

I'd be happy to reintegrate my changes to this back into the Atmel
driver once I get it working correct on the at91.  Do you have the
equipment to give my changes a try?

Converting my #defines for the the features to cpu_is_xxx is easy
enough.  I'll take care of that.

Thanks,

Rob

On Mon, Jun 8, 2009 at 3:47 AM, Haavard
Skinnemoen<haavard.skinnemoen@atmel.com> wrote:
> Rob Emanuele wrote:
>> This driver is a port the Atmel AVR32 MCI driver which uses similar silicon.
>
> While I do understand the motivation for this patch, I was kind of
> hoping we could reduce the number of drivers for the Atmel MCI
> hardware, not increase it...
>
> So I think it would be much better if your changes were integrated into
> one of the existing drivers.
>
>> Joey Oravec has mentioned that he has had better reliability with the chip
>> he is using (AT91SAM9261) if the MMC controller is reset before each
>> command.  There is a compile time option to do just that.
>
> This could be a useful option for atmel-mci too.
>
>> Joey Oravec has stated that the Read and Write Proof functionality that
>> prevents buffer overflows is not available on the AT91SAM9261.  This was
>> confirmed by Nicolas Ferre and also affects the AT91RM9200.  This new
>> patch excludes those register config bits.
>
> Preferably use the cpu_is_xxx() functions to achieve that. Perhaps even
> wrap those tests inside a mci_has_xxproof() function to make it
> clearer what you're testing for, and easier to change if we decide to
> do the test based on version registers, etc. later.
>
> Haavard
>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH][Updated] New AT91 MCI Driver that supports both MCI slots  used at the same time
  2009-06-08 16:58             ` Rob Emanuele
@ 2009-06-08 18:59               ` Rob Emanuele
  0 siblings, 0 replies; 11+ messages in thread
From: Rob Emanuele @ 2009-06-08 18:59 UTC (permalink / raw)
  To: Haavard Skinnemoen
  Cc: Joey Oravec, Nicolas Ferre, linux-arm-kernel, linux-kernel,
	Haavard Skinnemoen

Haavard,

I'll revise my previous thought about integrating my changes back into
the Atmel driver.  I'll start that today.  I figure this way, the
changes I make can get in front of a wider audience.

Thanks,

Rob

On Mon, Jun 8, 2009 at 9:58 AM, Rob Emanuele<poorarm@shoreis.com> wrote:
> Hi Haavard,
>
> I'd be happy to reintegrate my changes to this back into the Atmel
> driver once I get it working correct on the at91.  Do you have the
> equipment to give my changes a try?
>
> Converting my #defines for the the features to cpu_is_xxx is easy
> enough.  I'll take care of that.
>
> Thanks,
>
> Rob
>
> On Mon, Jun 8, 2009 at 3:47 AM, Haavard
> Skinnemoen<haavard.skinnemoen@atmel.com> wrote:
>> Rob Emanuele wrote:
>>> This driver is a port the Atmel AVR32 MCI driver which uses similar silicon.
>>
>> While I do understand the motivation for this patch, I was kind of
>> hoping we could reduce the number of drivers for the Atmel MCI
>> hardware, not increase it...
>>
>> So I think it would be much better if your changes were integrated into
>> one of the existing drivers.
>>
>>> Joey Oravec has mentioned that he has had better reliability with the chip
>>> he is using (AT91SAM9261) if the MMC controller is reset before each
>>> command.  There is a compile time option to do just that.
>>
>> This could be a useful option for atmel-mci too.
>>
>>> Joey Oravec has stated that the Read and Write Proof functionality that
>>> prevents buffer overflows is not available on the AT91SAM9261.  This was
>>> confirmed by Nicolas Ferre and also affects the AT91RM9200.  This new
>>> patch excludes those register config bits.
>>
>> Preferably use the cpu_is_xxx() functions to achieve that. Perhaps even
>> wrap those tests inside a mci_has_xxproof() function to make it
>> clearer what you're testing for, and easier to change if we decide to
>> do the test based on version registers, etc. later.
>>
>> Haavard
>>
>

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2009-06-08 18:59 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-05-28 21:40 [PATCH] New AT91 MCI Driver that supports both MCI slots used at the same time Rob Emanuele
2009-05-29 15:27 ` Joey Oravec
2009-06-02 16:53   ` Rob Emanuele
2009-06-03 15:10     ` Nicolas Ferre
2009-06-03 15:45       ` Joey Oravec
2009-06-03 19:02         ` [PATCH][Updated] " Rob Emanuele
2009-06-08 10:47           ` Haavard Skinnemoen
2009-06-08 16:58             ` Rob Emanuele
2009-06-08 18:59               ` Rob Emanuele
2009-06-02  8:49 ` [PATCH] " Nicolas Ferre
2009-06-02 16:31   ` Rob Emanuele

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).