mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH] mci: add Atmel AT91 MCI driver
@ 2011-05-31 15:02 Hubert Feurstein
  2011-06-01  5:45 ` Jean-Christophe PLAGNIOL-VILLARD
  0 siblings, 1 reply; 9+ messages in thread
From: Hubert Feurstein @ 2011-05-31 15:02 UTC (permalink / raw)
  To: barebox; +Cc: Patrice Vilchez, Nicolas Ferre

The driver supports push and pull transfers.
Tested on at91sam9m10 SoC.

Signed-off-by: Hubert Feurstein <h.feurstein@gmail.com>
Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
---
 arch/arm/mach-at91/at91sam9g45_devices.c   |   92 +++++
 arch/arm/mach-at91/include/mach/at91_mci.h |  121 +++++++
 arch/arm/mach-at91/include/mach/board.h    |   10 +
 drivers/mci/Kconfig                        |    7 +
 drivers/mci/Makefile                       |    1 +
 drivers/mci/atmel_mci.c                    |  519 ++++++++++++++++++++++++++++
 6 files changed, 750 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-at91/include/mach/at91_mci.h
 create mode 100644 drivers/mci/atmel_mci.c

diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
index ddb005a..950d083 100644
--- a/arch/arm/mach-at91/at91sam9g45_devices.c
+++ b/arch/arm/mach-at91/at91sam9g45_devices.c
@@ -10,6 +10,7 @@
  *
  */
 #include <common.h>
+#include <sizes.h>
 #include <asm/armlinux.h>
 #include <asm/hardware.h>
 #include <mach/at91_pmc.h>
@@ -240,3 +241,94 @@ void at91_register_uart(unsigned id, unsigned pins)
 	}
 
 }
+
+#if defined(CONFIG_MCI_ATMEL)
+static struct device_d mci0_device = {
+	.id	  = 0,
+	.name     = "atmel_mci",
+	.map_base = AT91SAM9G45_BASE_MCI0,
+	.size     = SZ_16K,
+};
+
+static struct device_d mci1_device = {
+	.id	  = 1,
+	.name     = "atmel_mci",
+	.map_base = AT91SAM9G45_BASE_MCI1,
+	.size     = SZ_16K,
+};
+
+/* Consider only one slot : slot 0 */
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data)
+{
+
+	if (!data)
+		return;
+
+	/* Must have at least one usable slot */
+	if (!data->bus_width)
+		return;
+
+	/* input/irq */
+	if (data->detect_pin) {
+		at91_set_gpio_input(data->detect_pin, 1);
+		at91_set_deglitch(data->detect_pin, 1);
+	}
+	if (data->wp_pin)
+		at91_set_gpio_input(data->wp_pin, 1);
+
+	if (mmc_id == 0) {		/* MCI0 */
+
+		/* CLK */
+		at91_set_A_periph(AT91_PIN_PA0, 0);
+
+		/* CMD */
+		at91_set_A_periph(AT91_PIN_PA1, 1);
+
+		/* DAT0, maybe DAT1..DAT3 and maybe DAT4..DAT7 */
+		at91_set_A_periph(AT91_PIN_PA2, 1);
+		if (data->bus_width == 4) {
+			at91_set_A_periph(AT91_PIN_PA3, 1);
+			at91_set_A_periph(AT91_PIN_PA4, 1);
+			at91_set_A_periph(AT91_PIN_PA5, 1);
+			if (data->bus_width == 8) {
+				at91_set_A_periph(AT91_PIN_PA6, 1);
+				at91_set_A_periph(AT91_PIN_PA7, 1);
+				at91_set_A_periph(AT91_PIN_PA8, 1);
+				at91_set_A_periph(AT91_PIN_PA9, 1);
+			}
+		}
+
+		mci0_device.platform_data = data;
+		at91_clock_associate("mci0_clk", &mci0_device, "mci_clk");
+		register_device(&mci0_device);
+
+	} else {			/* MCI1 */
+		/* CLK */
+		at91_set_A_periph(AT91_PIN_PA31, 0);
+
+		/* CMD */
+		at91_set_A_periph(AT91_PIN_PA22, 1);
+
+		/* DAT0, maybe DAT1..DAT3 and maybe DAT4..DAT7 */
+		at91_set_A_periph(AT91_PIN_PA23, 1);
+		if (data->bus_width == 4) {
+			at91_set_A_periph(AT91_PIN_PA24, 1);
+			at91_set_A_periph(AT91_PIN_PA25, 1);
+			at91_set_A_periph(AT91_PIN_PA26, 1);
+			if (data->bus_width == 8) {
+				at91_set_A_periph(AT91_PIN_PA27, 1);
+				at91_set_A_periph(AT91_PIN_PA28, 1);
+				at91_set_A_periph(AT91_PIN_PA29, 1);
+				at91_set_A_periph(AT91_PIN_PA30, 1);
+			}
+		}
+
+		mci1_device.platform_data = data;
+		at91_clock_associate("mci1_clk", &mci1_device, "mci_clk");
+		register_device(&mci1_device);
+	}
+}
+#else
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data) {}
+#endif
+
diff --git a/arch/arm/mach-at91/include/mach/at91_mci.h b/arch/arm/mach-at91/include/mach/at91_mci.h
new file mode 100644
index 0000000..6aa50aa
--- /dev/null
+++ b/arch/arm/mach-at91/include/mach/at91_mci.h
@@ -0,0 +1,121 @@
+/*
+ * [origin: Linux kernel arch/arm/mach-at91/include/mach/at91_mci.h]
+ *
+ * Copyright (C) 2005 Ivan Kokshaysky
+ * Copyright (C) SAN People
+ *
+ * MultiMedia Card Interface (MCI) registers.
+ * Based on AT91RM9200 datasheet revision F.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef AT91_MCI_H
+#define AT91_MCI_H
+
+#define AT91_MCI_CR		0x00		/* Control Register */
+#define		AT91_MCI_MCIEN		(1 <<  0)	/* Multi-Media Interface Enable */
+#define		AT91_MCI_MCIDIS		(1 <<  1)	/* Multi-Media Interface Disable */
+#define		AT91_MCI_PWSEN		(1 <<  2)	/* Power Save Mode Enable */
+#define		AT91_MCI_PWSDIS		(1 <<  3)	/* Power Save Mode Disable */
+#define		AT91_MCI_SWRST		(1 <<  7)	/* Software Reset */
+
+#define AT91_MCI_MR		0x04		/* Mode Register */
+#define		AT91_MCI_CLKDIV		(0xff  <<  0)	/* Clock Divider */
+#define		AT91_MCI_PWSDIV		(7     <<  8)	/* Power Saving Divider */
+#define		AT91_MCI_RDPROOF	(1     << 11)	/* Read Proof Enable [SAM926[03] only] */
+#define		AT91_MCI_WRPROOF	(1     << 12)	/* Write Proof Enable [SAM926[03] only] */
+#define		AT91_MCI_PDCFBYTE	(1     << 13)	/* PDC Force Byte Transfer [SAM926[03] only] */
+#define		AT91_MCI_PDCPADV	(1     << 14)	/* PDC Padding Value */
+#define		AT91_MCI_PDCMODE	(1     << 15)	/* PDC-orientated Mode */
+#define		AT91_MCI_BLKLEN		(0xfff << 18)	/* Data Block Length */
+
+#define AT91_MCI_DTOR		0x08		/* Data Timeout Register */
+#define		AT91_MCI_DTOCYC		(0xf << 0)	/* Data Timeout Cycle Number */
+#define		AT91_MCI_DTOMUL		(7   << 4)	/* Data Timeout Multiplier */
+#define		AT91_MCI_DTOMUL_1		(0 <<  4)
+#define		AT91_MCI_DTOMUL_16		(1 <<  4)
+#define		AT91_MCI_DTOMUL_128		(2 <<  4)
+#define		AT91_MCI_DTOMUL_256		(3 <<  4)
+#define		AT91_MCI_DTOMUL_1K		(4 <<  4)
+#define		AT91_MCI_DTOMUL_4K		(5 <<  4)
+#define		AT91_MCI_DTOMUL_64K		(6 <<  4)
+#define		AT91_MCI_DTOMUL_1M		(7 <<  4)
+
+#define AT91_MCI_SDCR		0x0c		/* SD Card Register */
+#define		AT91_MCI_SDCSEL		(3 << 0)	/* SD Card Selector */
+#define		AT91_MCI_SDCBUS		(3 << 6)	/* 1-bit, 4-bit, or 8-bit bus */
+#define			AT91_MCI_SDCBUS_1BIT	(0 << 6)	/* 1-bit bus */
+#define			AT91_MCI_SDCBUS_4BIT	(2 << 6)	/* 4-bit bus */
+#define			AT91_MCI_SDCBUS_8BIT	(3 << 6)	/* 4-bit bus */
+
+#define AT91_MCI_ARGR		0x10		/* Argument Register */
+
+#define AT91_MCI_CMDR		0x14		/* Command Register */
+#define		AT91_MCI_CMDNB		(0x3f << 0)	/* Command Number */
+#define		AT91_MCI_RSPTYP		(3    << 6)	/* Response Type */
+#define			AT91_MCI_RSPTYP_NONE	(0 <<  6)
+#define			AT91_MCI_RSPTYP_48	(1 <<  6)
+#define			AT91_MCI_RSPTYP_136	(2 <<  6)
+#define			AT91_MCI_RSPTYP_R1B	(3 <<  6)
+#define		AT91_MCI_SPCMD		(7    << 8)	/* Special Command */
+#define			AT91_MCI_SPCMD_NONE	(0 <<  8)
+#define			AT91_MCI_SPCMD_INIT	(1 <<  8)
+#define			AT91_MCI_SPCMD_SYNC	(2 <<  8)
+#define			AT91_MCI_SPCMD_ICMD	(4 <<  8)
+#define			AT91_MCI_SPCMD_IRESP	(5 <<  8)
+#define		AT91_MCI_OPDCMD		(1 << 11)	/* Open Drain Command */
+#define		AT91_MCI_MAXLAT		(1 << 12)	/* Max Latency for Command to Response */
+#define		AT91_MCI_TRCMD		(3 << 16)	/* Transfer Command */
+#define			AT91_MCI_TRCMD_NONE	(0 << 16)
+#define			AT91_MCI_TRCMD_START	(1 << 16)
+#define			AT91_MCI_TRCMD_STOP	(2 << 16)
+#define		AT91_MCI_TRDIR		(1 << 18)	/* Transfer Direction */
+#define			AT91_MCI_TRDIR_RX	(1 << 18)	/* Read Transfer Direction */
+#define			AT91_MCI_TRDIR_TX	(0 << 18)	/* Write Transfer Direction */
+#define		AT91_MCI_TRTYP		(3 << 19)	/* Transfer Type */
+#define			AT91_MCI_TRTYP_BLOCK	(0 << 19)
+#define			AT91_MCI_TRTYP_MULTIPLE	(1 << 19)
+#define			AT91_MCI_TRTYP_STREAM	(2 << 19)
+#define			AT91_MCI_TRTYP_SDIO_BYTE	(4 << 19)
+#define			AT91_MCI_TRTYP_SDIO_BLOCK	(5 << 19)
+
+#define AT91_MCI_BLKR		0x18		/* Block Register */
+#define		AT91_MCI_BLKR_BCNT(n)	((0xffff & (n)) << 0)	/* Block count */
+#define		AT91_MCI_BLKR_BLKLEN(n)	((0xffff & (n)) << 16)	/* Block length */
+
+#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_SR		0x40		/* Status Register */
+#define		AT91_MCI_CMDRDY		(1 <<  0)	/* Command Ready */
+#define		AT91_MCI_RXRDY		(1 <<  1)	/* Receiver Ready */
+#define		AT91_MCI_TXRDY		(1 <<  2)	/* Transmit Ready */
+#define		AT91_MCI_BLKE		(1 <<  3)	/* Data Block Ended */
+#define		AT91_MCI_DTIP		(1 <<  4)	/* Data Transfer in Progress */
+#define		AT91_MCI_NOTBUSY	(1 <<  5)	/* Data Not Busy */
+#define		AT91_MCI_ENDRX		(1 <<  6)	/* End of RX Buffer */
+#define		AT91_MCI_ENDTX		(1 <<  7)	/* End fo TX Buffer */
+#define		AT91_MCI_SDIOIRQA	(1 <<  8)	/* SDIO Interrupt for Slot A */
+#define		AT91_MCI_SDIOIRQB	(1 <<  9)	/* SDIO Interrupt for Slot B */
+#define		AT91_MCI_RXBUFF		(1 << 14)	/* RX Buffer Full */
+#define		AT91_MCI_TXBUFE		(1 << 15)	/* TX Buffer Empty */
+#define		AT91_MCI_RINDE		(1 << 16)	/* Response Index Error */
+#define		AT91_MCI_RDIRE		(1 << 17)	/* Response Direction Error */
+#define		AT91_MCI_RCRCE		(1 << 18)	/* Response CRC Error */
+#define		AT91_MCI_RENDE		(1 << 19)	/* Response End Bit Error */
+#define		AT91_MCI_RTOE		(1 << 20)	/* Response Time-out Error */
+#define		AT91_MCI_DCRCE		(1 << 21)	/* Data CRC Error */
+#define		AT91_MCI_DTOE		(1 << 22)	/* Data Time-out Error */
+#define		AT91_MCI_OVRE		(1 << 30)	/* Overrun */
+#define		AT91_MCI_UNRE		(1 << 31)	/* Underrun */
+
+#define AT91_MCI_IER		0x44		/* Interrupt Enable Register */
+#define AT91_MCI_IDR		0x48		/* Interrupt Disable Register */
+#define AT91_MCI_IMR		0x4c		/* Interrupt Mask Register */
+
+#endif
diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index 1ab05ad..89c1746 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -63,4 +63,14 @@ void at91_add_device_sdram(u32 size);
 #define ATMEL_UART_RI	0x20
 
 void at91_register_uart(unsigned id, unsigned pins);
+
+/* Multimedia Card Interface */
+struct atmel_mci_platform_data {
+	unsigned bus_width;
+	unsigned host_caps; /* MCI_MODE_* from mci.h */
+	unsigned detect_pin;
+	unsigned wp_pin;
+};
+
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data);
 #endif
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index 5d8adbd..7b71b99 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -73,4 +73,11 @@ config MCI_OMAP_HSMMC
 	  Enable this entry to add support to read and write SD cards on a
 	  OMAP4 based system.
 
+config MCI_ATMEL
+	bool "ATMEL (AT91)"
+	depends on ARCH_AT91
+	help
+	  Enable this entry to add support to read and write SD cards on a
+	  Atmel AT91.
+
 endif
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index 2bb9a93..4fc0046 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_MCI_S3C) += s3c.o
 obj-$(CONFIG_MCI_IMX) += imx.o
 obj-$(CONFIG_MCI_IMX_ESDHC) += imx-esdhc.o
 obj-$(CONFIG_MCI_OMAP_HSMMC) += omap_hsmmc.o
+obj-$(CONFIG_MCI_ATMEL) += atmel_mci.o
diff --git a/drivers/mci/atmel_mci.c b/drivers/mci/atmel_mci.c
new file mode 100644
index 0000000..62e2822
--- /dev/null
+++ b/drivers/mci/atmel_mci.c
@@ -0,0 +1,519 @@
+/*
+ * Atmel AT91 MCI driver
+ *
+ * Copyright (C) 2011 Hubert Feurstein <h.feurstein@gmail.com>
+ *
+ * heavily based on imx.c by:
+ * Copyright (C) 2009 Ilya Yanok, <yanok@emcraft.com>
+ * Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com>
+ *
+ * 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.
+ * 
+ */
+
+/* #define DEBUG */
+
+#include <common.h>
+#include <init.h>
+#include <mci.h>
+#include <errno.h>
+#include <clock.h>
+#include <gpio.h>
+#include <asm/io.h>
+#include <mach/board.h>
+#include <linux/clk.h>
+
+#include <mach/at91_mci.h>
+
+/*
+ * Structure for struct SoC access.
+ * Names starting with '_' are fillers.
+ */
+struct atmel_mci_regs {
+	/*	reg	Offset */
+	u32	cr;	/* 0x00 */
+	u32	mr;	/* 0x04 */
+	u32	dtor;	/* 0x08 */
+	u32	sdcr;	/* 0x0c */
+	u32	argr;	/* 0x10 */
+	u32	cmdr;	/* 0x14 */
+	u32	blkr;	/* 0x18 */
+	u32	_1c;	/* 0x1c */
+	u32	rspr;	/* 0x20 */
+	u32	rspr1;	/* 0x24 */
+	u32	rspr2;	/* 0x28 */
+	u32	rspr3;	/* 0x2c */
+	u32	rdr;	/* 0x30 */
+	u32	tdr;	/* 0x34 */
+	u32	_38;	/* 0x38 */
+	u32	_3c;	/* 0x3c */
+	u32	sr;	/* 0x40 */
+	u32	ier;	/* 0x44 */
+	u32	idr;	/* 0x48 */
+	u32	imr;	/* 0x4c */
+};
+
+struct atmel_mci_host {
+	struct mci_host mci;
+	struct atmel_mci_regs volatile __iomem *base;
+	struct device_d *hw_dev;
+	struct clk *clk;
+	
+	u32 datasize;
+	struct mci_cmd *cmd;
+	struct mci_data *data;
+};
+
+#define to_mci_host(mci)	container_of(mci, struct atmel_mci_host, mci)
+
+#define STATUS_ERROR_MASK	(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)
+
+static void atmel_mci_reset(struct atmel_mci_host *host)
+{
+	writel(AT91_MCI_SWRST | AT91_MCI_MCIDIS, &host->base->cr);
+	writel(0x5f, &host->base->dtor);
+	writel(~0UL, &host->base->idr);
+}
+
+static void atmel_set_clk_rate(struct atmel_mci_host *host, unsigned int clk_ios)
+{
+	unsigned int divider;
+	unsigned int clk_in = clk_get_rate(host->clk);
+
+	if (clk_ios > 0)
+		divider = (clk_in / clk_ios) / 2 - 1;
+
+	if (divider > 255 || clk_ios == 0)
+		divider = 255;
+
+	pr_debug("atmel_set_clk_rate: clkIn=%d clkIos=%d divider=%d\n",
+		clk_in, clk_ios, divider);
+
+	writel(((AT91_MCI_CLKDIV & divider)
+		| AT91_MCI_RDPROOF
+		| AT91_MCI_WRPROOF), &host->base->mr);
+}
+
+static int atmel_poll_status(struct atmel_mci_host *host, u32 mask)
+{
+	u32 stat;
+	uint64_t start = get_time_ns();
+
+	do {
+		stat = readl(&host->base->sr);
+		if (stat & STATUS_ERROR_MASK)
+			return stat;
+		if (is_timeout(start, SECOND))
+			return AT91_MCI_RTOE | stat;
+		if (stat & mask)
+			return 0;
+	} while (1);
+}
+
+static int atmel_pull(struct atmel_mci_host *host, void *_buf, int bytes)
+{
+	unsigned int stat;
+	u32 *buf = _buf;
+
+	while (bytes > 3) {
+		stat = atmel_poll_status(host, AT91_MCI_RXRDY);
+		if (stat)
+			return stat;
+		
+		*buf++ = readl(&host->base->rdr);
+		bytes -= 4;
+	}
+
+	if (bytes) {
+		u8 *b = (u8 *)buf;
+		u32 tmp;
+
+		stat = atmel_poll_status(host, AT91_MCI_RXRDY);
+		if (stat)
+			return stat;
+		
+		tmp = readl(&host->base->rdr);
+		memcpy(b, &tmp, bytes);
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_MCI_WRITE
+static int atmel_push(struct atmel_mci_host *host, const void *_buf, int bytes)
+{
+	unsigned int stat;
+	const u32 *buf = _buf;
+
+	while (bytes > 3) {
+		stat = atmel_poll_status(host, AT91_MCI_TXRDY);
+		if (stat)
+			return stat;
+		
+		writel(*buf++, &host->base->tdr);
+		bytes -= 4;
+	}
+
+	if (bytes) {
+		const u8 *b = (u8 *)buf;
+		u32 tmp;
+
+		stat = atmel_poll_status(host, AT91_MCI_TXRDY);
+		if (stat)
+			return stat;
+
+		memcpy(&tmp, b, bytes);
+		writel(tmp, &host->base->tdr);
+	}
+
+	stat = atmel_poll_status(host, AT91_MCI_TXRDY);
+	if (stat)
+		return stat;
+
+	return 0;
+}
+#endif /* CONFIG_MCI_WRITE */
+
+static int atmel_transfer_data(struct atmel_mci_host *host)
+{
+	struct mci_data *data = host->data;
+	int stat;
+	unsigned long length;
+
+	length = data->blocks * data->blocksize;
+	host->datasize = 0;
+
+	if (data->flags & MMC_DATA_READ) {
+		stat = atmel_pull(host, data->dest, length);
+		if (stat)
+			return stat;
+
+		stat = atmel_poll_status(host, AT91_MCI_NOTBUSY);
+		if (stat)
+			return stat;
+
+		host->datasize += length;
+	} else {
+#ifdef CONFIG_MCI_WRITE
+		stat = atmel_push(host, (const void *)(data->src), length);
+		if (stat)
+			return stat;
+
+		host->datasize += length;
+		stat = atmel_poll_status(host, AT91_MCI_NOTBUSY);
+		if (stat)
+			return stat;
+#endif /* CONFIG_MCI_WRITE */
+	}
+	return 0;
+}
+
+static void atmel_finish_request(struct atmel_mci_host *host)
+{
+	host->cmd = NULL;
+	host->data = NULL;
+}
+
+static int atmel_finish_data(struct atmel_mci_host *host, unsigned int stat)
+{
+	int data_error = 0;
+
+	if (stat & STATUS_ERROR_MASK) {
+		pr_err("atmel_mci: request failed (status=0x%08x)\n",
+				stat);
+		if (stat & AT91_MCI_DCRCE) {
+			data_error = -EILSEQ;
+		} else if (stat & (AT91_MCI_RTOE | AT91_MCI_DTOE)) {
+			data_error = -ETIMEDOUT;
+		} else {
+			data_error = -EIO;
+		}
+	}
+
+	host->data = NULL;
+
+	return data_error;
+}
+
+static void atmel_setup_data(struct atmel_mci_host *host, struct mci_data *data)
+{
+	unsigned int nob = data->blocks;
+	unsigned int blksz = data->blocksize;
+	unsigned int datasize = nob * blksz;
+
+	BUG_ON(data->blocksize & 3);
+	BUG_ON(nob == 0);
+	
+	host->data = data;
+
+	pr_debug("atmel_setup_data: nob=%d blksz=%d\n", nob, blksz);
+	
+	writel(AT91_MCI_BLKR_BCNT(nob)
+		| AT91_MCI_BLKR_BLKLEN(blksz), &host->base->blkr);
+
+	host->datasize = datasize;
+}
+
+static int atmel_read_response(struct atmel_mci_host *host, unsigned int stat)
+{
+	struct mci_cmd *cmd = host->cmd;
+	int i;
+	u32 *resp = (u32 *)cmd->response;
+
+	if (!cmd)
+		return 0;
+
+	if (stat & (AT91_MCI_RTOE | AT91_MCI_DTOE)) {
+		pr_err("atmel_mci: command/data timeout\n");
+		return -ETIMEDOUT;
+	} else if ((stat & AT91_MCI_RCRCE) && (cmd->resp_type & MMC_RSP_CRC)) {
+		pr_err("atmel_mci: cmd crc error\n");
+		return -EILSEQ;
+	}
+
+	if (cmd->resp_type & MMC_RSP_PRESENT) {
+		if (cmd->resp_type & MMC_RSP_136) {
+			for (i = 0; i < 4; i++)
+				resp[i] =readl(&host->base->rspr);
+		} else {
+			resp[0] = readl(&host->base->rspr);
+		}
+	}
+
+	return 0;
+}
+
+static int atmel_cmd_done(struct atmel_mci_host *host, unsigned int stat)
+{
+	int datastat;
+	int ret;
+
+	ret = atmel_read_response(host, stat);
+
+	if (ret) {
+		atmel_finish_request(host);
+		return ret;
+	}
+
+	if (!host->data) {
+		atmel_finish_request(host);
+		return 0;
+	}
+
+	datastat = atmel_transfer_data(host);
+	ret = atmel_finish_data(host, datastat);
+	atmel_finish_request(host);
+	return ret;
+}
+
+static int atmel_start_cmd(struct atmel_mci_host *host, struct mci_cmd *cmd,
+		unsigned int cmdat)
+{
+	unsigned flags = 0;
+	unsigned cmdval = 0;
+	
+	if (host->cmd != NULL)
+		pr_err("atmel_mci: error!\n");
+
+	if ((readl(&host->base->sr) & AT91_MCI_CMDRDY) == 0) {
+		pr_err("atmel_mci: mci not ready!\n");
+		return -EBUSY;
+	}
+
+	host->cmd = cmd;
+	cmdval = AT91_MCI_CMDNB & cmd->cmdidx;
+	
+	switch (cmd->resp_type) {
+	case MMC_RSP_R1: /* short CRC, OPCODE */
+	case MMC_RSP_R1b:/* short CRC, OPCODE, BUSY */
+		flags |= AT91_MCI_RSPTYP_48;
+		break;
+	case MMC_RSP_R2: /* long 136 bit + CRC */
+		flags |= AT91_MCI_RSPTYP_136;
+		break;
+	case MMC_RSP_R3: /* short */
+		flags |= AT91_MCI_RSPTYP_48;
+		break;
+	case MMC_RSP_NONE:
+		flags |= AT91_MCI_RSPTYP_NONE;
+		break;
+	default:
+		pr_err("atmel_mci: unhandled response type 0x%x\n",
+				cmd->resp_type);
+		return -EINVAL;
+	}
+	cmdval |= AT91_MCI_RSPTYP & flags;
+	cmdval |= cmdat & ~(AT91_MCI_CMDNB | AT91_MCI_RSPTYP);
+	
+	writel(cmd->cmdarg, &host->base->argr);
+	writel(cmdval, &host->base->cmdr);
+
+	return 0;
+}
+
+/** init the host interface */
+static int mci_reset(struct mci_host* mci, struct device_d* mci_dev)
+{
+	int ret;
+	struct atmel_mci_host* host = to_mci_host(mci);
+	struct atmel_mci_platform_data* pd = host->hw_dev->platform_data;
+
+	ret = gpio_get_value(pd->detect_pin);
+	pr_debug("atmel_mci: card %sdetected\n", ret != 0 ? "not " : "");
+	
+	if (pd->detect_pin && ret == 1)
+		return -ENODEV;
+		
+	clk_enable(host->clk);
+	atmel_mci_reset(host);
+	
+	return 0;
+}
+
+/** change host interface settings */
+static void mci_set_ios(struct mci_host* mci, struct device_d* mci_dev, unsigned bus_width, unsigned clock)
+{
+	struct atmel_mci_host* host = to_mci_host(mci);
+	
+	pr_debug("atmel_mci_set_ios: bus_width=%d clk=%d\n", bus_width, clock);
+
+	if (bus_width == 8)
+		writel(AT91_MCI_SDCBUS_8BIT, &host->base->sdcr);
+	if (bus_width == 4)
+		writel(AT91_MCI_SDCBUS_4BIT, &host->base->sdcr);
+	else
+		writel(AT91_MCI_SDCBUS_1BIT, &host->base->sdcr);
+
+	if (clock) {
+		atmel_set_clk_rate(host, clock);
+		writel(AT91_MCI_MCIEN, &host->base->cr);
+	} else {
+		writel(AT91_MCI_MCIDIS, &host->base->cr);
+	}
+
+	return;
+}
+
+/** handle a command */
+static int mci_request(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
+{
+	struct atmel_mci_host *host = to_mci_host(mci);
+	u32 stat, cmdat = 0;
+	int ret;
+
+	if (cmd->resp_type != MMC_RSP_NONE)
+		cmdat |= AT91_MCI_MAXLAT;
+
+	if (data) {
+		atmel_setup_data(host, data);
+
+		cmdat |= AT91_MCI_TRCMD_START | AT91_MCI_TRTYP_MULTIPLE;
+
+		if (data->flags & MMC_DATA_READ)
+			cmdat |= AT91_MCI_TRDIR_RX;
+	}
+
+	if ((ret = atmel_start_cmd(host, cmd, cmdat))) {
+		atmel_finish_request(host);
+		return ret;
+	}
+
+	stat = atmel_poll_status(host, AT91_MCI_CMDRDY);
+	return atmel_cmd_done(host, stat);
+}
+	
+
+#ifdef CONFIG_MCI_INFO
+static void mci_info(struct device_d *mci_dev)
+{
+	struct atmel_mci_host *host = mci_dev->priv;
+	struct atmel_mci_platform_data *pd = host->hw_dev->platform_data;
+
+	printf("  Bus data width: %d bit\n", host->mci.bus_width);
+
+	printf("  Bus frequency: %u Hz\n", host->mci.clock);
+	printf("  Frequency limits: ");
+	if (host->mci.f_min == 0)
+		printf("no lower limit ");
+	else
+		printf("%u Hz lower limit ", host->mci.f_min);
+	if (host->mci.f_max == 0)
+		printf("- no upper limit");
+	else
+		printf("- %u Hz upper limit", host->mci.f_max);
+
+	printf("\n  Card detection support: %s\n",
+		pd->detect_pin != 0 ? "yes" : "no");
+
+}
+#endif /* CONFIG_MCI_INFO */
+
+static int mci_probe(struct device_d *hw_dev)
+{
+	unsigned long clk_rate;
+	struct atmel_mci_host *host;
+	struct atmel_mci_platform_data *pd = hw_dev->platform_data;
+
+	if (pd == NULL) {
+		pr_err("atmel_mci: missing platform data\n");
+		return -EINVAL;
+	}
+
+	host = xzalloc(sizeof(*host));
+	host->mci.send_cmd = mci_request;
+	host->mci.set_ios = mci_set_ios;
+	host->mci.init = mci_reset;
+	
+	host->mci.host_caps = pd->host_caps;
+	if (pd->bus_width == 4)
+		host->mci.host_caps |= MMC_MODE_4BIT;
+	else if (pd->bus_width == 8)
+		host->mci.host_caps |= MMC_MODE_8BIT;
+	
+	host->base = (struct atmel_mci_regs *)hw_dev->map_base;
+	host->hw_dev = hw_dev;
+	hw_dev->priv = host;
+	host->clk = clk_get(hw_dev, "mci_clk");
+	if (host->clk == NULL) {
+		pr_err("atmel_mci: no mci_clk\n");
+		return -EINVAL;
+	}
+
+	clk_rate = clk_get_rate(host->clk);
+
+	host->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+	host->mci.f_min = clk_rate >> 9;
+	host->mci.f_max = clk_rate >> 1;
+
+	mci_register(&host->mci);
+
+	return 0;
+}
+
+static struct driver_d atmel_mci_driver = {
+        .name  = "atmel_mci",
+        .probe = mci_probe,
+#ifdef CONFIG_MCI_INFO
+	.info = mci_info,
+#endif
+};
+
+static int atmel_mci_init_driver(void)
+{
+        register_driver(&atmel_mci_driver);
+        return 0;
+}
+
+device_initcall(atmel_mci_init_driver);
-- 
1.7.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH] mci: add Atmel AT91 MCI driver
  2011-05-31 15:02 [PATCH] mci: add Atmel AT91 MCI driver Hubert Feurstein
@ 2011-06-01  5:45 ` Jean-Christophe PLAGNIOL-VILLARD
  2011-06-01 11:48   ` [PATCH v2] " Hubert Feurstein
  0 siblings, 1 reply; 9+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2011-06-01  5:45 UTC (permalink / raw)
  To: Hubert Feurstein; +Cc: Patrice Vilchez, barebox, Nicolas Ferre

On 17:02 Tue 31 May     , Hubert Feurstein wrote:
> The driver supports push and pull transfers.
> Tested on at91sam9m10 SoC.
> 
> Signed-off-by: Hubert Feurstein <h.feurstein@gmail.com>
> Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> ---
>  arch/arm/mach-at91/at91sam9g45_devices.c   |   92 +++++
I was working on similar patch too
please add other chips too

I'll test it this week
please check the patch with checkpatch
I see some whitespace issue
>  arch/arm/mach-at91/include/mach/at91_mci.h |  121 +++++++
>  arch/arm/mach-at91/include/mach/board.h    |   10 +
>  drivers/mci/Kconfig                        |    7 +
>  drivers/mci/Makefile                       |    1 +
>  drivers/mci/atmel_mci.c                    |  519 ++++++++++++++++++++++++++++
>  6 files changed, 750 insertions(+), 0 deletions(-)
>  create mode 100644 arch/arm/mach-at91/include/mach/at91_mci.h
>  create mode 100644 drivers/mci/atmel_mci.c
> 
> diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
> index ddb005a..950d083 100644
> --- a/arch/arm/mach-at91/at91sam9g45_devices.c
> +++ b/arch/arm/mach-at91/at91sam9g45_devices.c
> @@ -10,6 +10,7 @@
>   *
>   */
>  #include <common.h>
> +#include <sizes.h>
>  #include <asm/armlinux.h>
>  #include <asm/hardware.h>
>  #include <mach/at91_pmc.h>
> @@ -240,3 +241,94 @@ void at91_register_uart(unsigned id, unsigned pins)
>  	}
>  
>  }
> +
> +#if defined(CONFIG_MCI_ATMEL)
> +static struct device_d mci0_device = {
> +	.id	  = 0,
please use tab for indent
> +	.name     = "atmel_mci",
> +	.map_base = AT91SAM9G45_BASE_MCI0,
> +	.size     = SZ_16K,
> +};
> +
> +static struct device_d mci1_device = {
> +	.id	  = 1,
> +	.name     = "atmel_mci",
> +	.map_base = AT91SAM9G45_BASE_MCI1,
> +	.size     = SZ_16K,
> +};
> +
....
> +
> diff --git a/arch/arm/mach-at91/include/mach/at91_mci.h b/arch/arm/mach-at91/include/mach/at91_mci.h
> new file mode 100644
> index 0000000..6aa50aa
> --- /dev/null
> +++ b/arch/arm/mach-at91/include/mach/at91_mci.h
> @@ -0,0 +1,121 @@
> +/*
> + * [origin: Linux kernel arch/arm/mach-at91/include/mach/at91_mci.h]
please move this with the driver I'm going to the same in the kernel
> + *
> + * Copyright (C) 2005 Ivan Kokshaysky
> + * Copyright (C) SAN People
> + *
> + * MultiMedia Card Interface (MCI) registers.
> + * Based on AT91RM9200 datasheet revision F.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#ifndef AT91_MCI_H
> +#define AT91_MCI_H
> +
...
> +
> +	pr_debug("atmel_set_clk_rate: clkIn=%d clkIos=%d divider=%d\n",
> +		clk_in, clk_ios, divider);
dev_dbg here?
> +
> +	writel(((AT91_MCI_CLKDIV & divider)
> +		| AT91_MCI_RDPROOF
> +		| AT91_MCI_WRPROOF), &host->base->mr);
> +}
> +
> +static int atmel_poll_status(struct atmel_mci_host *host, u32 mask)
> +{
> +	u32 stat;
> +	uint64_t start = get_time_ns();
> +
> +	do {
> +		stat = readl(&host->base->sr);
> +		if (stat & STATUS_ERROR_MASK)
> +			return stat;
> +		if (is_timeout(start, SECOND))
> +			return AT91_MCI_RTOE | stat;
> +		if (stat & mask)
> +			return 0;
> +	} while (1);
> +}
> +
> +static int atmel_pull(struct atmel_mci_host *host, void *_buf, int bytes)
> +{
> +	unsigned int stat;
> +	u32 *buf = _buf;
> +
> +	while (bytes > 3) {
> +		stat = atmel_poll_status(host, AT91_MCI_RXRDY);
> +		if (stat)
> +			return stat;
> +		
> +		*buf++ = readl(&host->base->rdr);
> +		bytes -= 4;
> +	}
> +
> +	if (bytes) {
> +		u8 *b = (u8 *)buf;
> +		u32 tmp;
> +
> +		stat = atmel_poll_status(host, AT91_MCI_RXRDY);
> +		if (stat)
> +			return stat;
> +		
> +		tmp = readl(&host->base->rdr);
> +		memcpy(b, &tmp, bytes);
we could use  __iowrite32 here to speed up the copy
I'll send a patch to add it to barebox
> +	}
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_MCI_WRITE
> +static int atmel_push(struct atmel_mci_host *host, const void *_buf, int bytes)
> +{
> +	unsigned int stat;
> +	const u32 *buf = _buf;
> +
> +	while (bytes > 3) {
> +		stat = atmel_poll_status(host, AT91_MCI_TXRDY);
> +		if (stat)
> +			return stat;
> +		
> +		writel(*buf++, &host->base->tdr);
> +		bytes -= 4;
> +	}
> +
> +	if (bytes) {
> +		const u8 *b = (u8 *)buf;
> +		u32 tmp;
> +
> +		stat = atmel_poll_status(host, AT91_MCI_TXRDY);
> +		if (stat)
> +			return stat;
> +
> +		memcpy(&tmp, b, bytes);
ditto
> +		writel(tmp, &host->base->tdr);
> +	}
> +
> +	stat = atmel_poll_status(host, AT91_MCI_TXRDY);
> +	if (stat)
> +		return stat;
> +
> +	return 0;
> +}
> +#endif /* CONFIG_MCI_WRITE */
> +
> +static int atmel_transfer_data(struct atmel_mci_host *host)
> +{
> +	struct mci_data *data = host->data;
> +	int stat;
> +	unsigned long length;
> +
> +	length = data->blocks * data->blocksize;
> +	host->datasize = 0;
> +
> +	if (data->flags & MMC_DATA_READ) {
> +		stat = atmel_pull(host, data->dest, length);
> +		if (stat)
> +			return stat;
> +
> +		stat = atmel_poll_status(host, AT91_MCI_NOTBUSY);
> +		if (stat)
> +			return stat;
> +
> +		host->datasize += length;
> +	} else {
> +	BUG_ON(data->blocksize & 3);
> +	BUG_ON(nob == 0);
> +	
> +	host->data = data;
> +
> +	pr_debug("atmel_setup_data: nob=%d blksz=%d\n", nob, blksz);
deb_dbg?
> +	
> +	writel(AT91_MCI_BLKR_BCNT(nob)
> +		| AT91_MCI_BLKR_BLKLEN(blksz), &host->base->blkr);
> +
> +	host->datasize = datasize;
> +}
> +
otherwise look good

Best Regards,
J.

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH v2] mci: add Atmel AT91 MCI driver
  2011-06-01  5:45 ` Jean-Christophe PLAGNIOL-VILLARD
@ 2011-06-01 11:48   ` Hubert Feurstein
  2011-06-02 16:04     ` Jean-Christophe PLAGNIOL-VILLARD
  2011-06-02 16:05     ` [PATCH] at91sam9263ek: add mci1 support Jean-Christophe PLAGNIOL-VILLARD
  0 siblings, 2 replies; 9+ messages in thread
From: Hubert Feurstein @ 2011-06-01 11:48 UTC (permalink / raw)
  To: barebox; +Cc: Patrice Vilchez, Nicolas Ferre

The driver supports push and pull transfers.
Tested on at91sam9m10 SoC.

Signed-off-by: Hubert Feurstein <h.feurstein@gmail.com>
Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
---
 Changes against v1:
 - Code cleanup according to checkpatch.pl.
 - Add support for devices: 9260, 9261 and 9263 (but not tested!)
 - Use dev_* function instead of pr_*

 arch/arm/mach-at91/at91sam9260_devices.c |   50 +++
 arch/arm/mach-at91/at91sam9261_devices.c |   50 +++
 arch/arm/mach-at91/at91sam9263_devices.c |   78 +++++
 arch/arm/mach-at91/at91sam9g45_devices.c |   91 +++++
 arch/arm/mach-at91/include/mach/board.h  |   10 +
 drivers/mci/Kconfig                      |    7 +
 drivers/mci/Makefile                     |    1 +
 drivers/mci/at91_mci.h                   |  121 +++++++
 drivers/mci/atmel_mci.c                  |  526 ++++++++++++++++++++++++++++++
 9 files changed, 934 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mci/at91_mci.h
 create mode 100644 drivers/mci/atmel_mci.c

diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index fc8f828..d44e280 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -10,6 +10,7 @@
  *
  */
 #include <common.h>
+#include <sizes.h>
 #include <asm/armlinux.h>
 #include <asm/hardware.h>
 #include <mach/board.h>
@@ -280,3 +281,52 @@ void at91_register_uart(unsigned id, unsigned pins)
 			return;
 	}
 }
+
+#if defined(CONFIG_MCI_ATMEL)
+static struct device_d mci_device = {
+	.id		= -1,
+	.name		= "atmel_mci",
+	.map_base	= AT91SAM9260_BASE_MCI,
+	.size		= SZ_16K,
+};
+
+/* Consider only one slot : slot 0 */
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data)
+{
+	if (!data)
+		return;
+
+	/* need bus_width */
+	if (!data->bus_width)
+		return;
+
+	/* input/irq */
+	if (data->detect_pin) {
+		at91_set_gpio_input(data->detect_pin, 1);
+		at91_set_deglitch(data->detect_pin, 1);
+	}
+
+	if (data->wp_pin)
+		at91_set_gpio_input(data->wp_pin, 1);
+
+	/* CLK */
+	at91_set_A_periph(AT91_PIN_PA8, 0);
+
+	/* CMD */
+	at91_set_A_periph(AT91_PIN_PA7, 1);
+
+	/* DAT0, maybe DAT1..DAT3 */
+	at91_set_A_periph(AT91_PIN_PA6, 1);
+	if (data->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);
+	}
+
+	mci_device.platform_data = data;
+	at91_clock_associate("mci_clk", &mci_device, "mci_clk");
+	register_device(&mci_device);
+}
+#else
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data) {}
+#endif
diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c
index 66bf3a8..d8b70a3 100644
--- a/arch/arm/mach-at91/at91sam9261_devices.c
+++ b/arch/arm/mach-at91/at91sam9261_devices.c
@@ -10,6 +10,7 @@
  *
  */
 #include <common.h>
+#include <sizes.h>
 #include <asm/armlinux.h>
 #include <asm/hardware.h>
 #include <mach/at91_pmc.h>
@@ -173,3 +174,52 @@ void at91_register_uart(unsigned id, unsigned pins)
 			return;
 	}
 }
+
+#if defined(CONFIG_MCI_ATMEL)
+static struct device_d mci_device = {
+	.id		= -1,
+	.name		= "atmel_mci",
+	.map_base	= AT91SAM9261_BASE_MCI,
+	.size		= SZ_16K,
+};
+
+/* Consider only one slot : slot 0 */
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data)
+{
+	if (!data)
+		return;
+
+	/* need bus_width */
+	if (!data->bus_width)
+		return;
+
+	/* input/irq */
+	if (data->detect_pin) {
+		at91_set_gpio_input(data->detect_pin, 1);
+		at91_set_deglitch(data->detect_pin, 1);
+	}
+
+	if (data->wp_pin)
+		at91_set_gpio_input(data->wp_pin, 1);
+
+	/* CLK */
+	at91_set_B_periph(AT91_PIN_PA2, 0);
+
+	/* CMD */
+	at91_set_B_periph(AT91_PIN_PA1, 1);
+
+	/* DAT0, maybe DAT1..DAT3 */
+	at91_set_B_periph(AT91_PIN_PA0, 1);
+	if (data->bus_width == 4) {
+		at91_set_B_periph(AT91_PIN_PA4, 1);
+		at91_set_B_periph(AT91_PIN_PA5, 1);
+		at91_set_B_periph(AT91_PIN_PA6, 1);
+	}
+
+	mci_device.platform_data = data;
+	at91_clock_associate("mci_clk", &mci_device, "mci_clk");
+	register_device(&mci_device);
+}
+#else
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data) {}
+#endif
diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c
index 346426c..04fb79e 100644
--- a/arch/arm/mach-at91/at91sam9263_devices.c
+++ b/arch/arm/mach-at91/at91sam9263_devices.c
@@ -10,6 +10,7 @@
  *
  */
 #include <common.h>
+#include <sizes.h>
 #include <asm/armlinux.h>
 #include <asm/hardware.h>
 #include <mach/at91_pmc.h>
@@ -213,3 +214,80 @@ void at91_register_uart(unsigned id, unsigned pins)
 	}
 
 }
+
+#if defined(CONFIG_MCI_ATMEL)
+static struct device_d mci0_device = {
+	.id		= 0,
+	.name		= "atmel_mci",
+	.map_base	= AT91SAM9263_BASE_MCI0,
+	.size		= SZ_16K,
+};
+
+static struct device_d mci1_device = {
+	.id		= 1,
+	.name		= "atmel_mci",
+	.map_base	= AT91SAM9263_BASE_MCI1,
+	.size		= SZ_16K,
+};
+
+/* Consider only one slot : slot 0 */
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data)
+{
+	if (!data)
+		return;
+
+	/* need bus_width */
+	if (!data->bus_width)
+		return;
+
+	/* input/irq */
+	if (data->detect_pin) {
+		at91_set_gpio_input(data->detect_pin, 1);
+		at91_set_deglitch(data->detect_pin, 1);
+	}
+
+	if (data->wp_pin)
+		at91_set_gpio_input(data->wp_pin, 1);
+
+	if (mmc_id == 0) {		/* MCI0 */
+		/* CLK */
+		at91_set_A_periph(AT91_PIN_PA12, 0);
+
+		/* CMD */
+		at91_set_A_periph(AT91_PIN_PA1, 1);
+
+		/* DAT0, maybe DAT1..DAT3 and maybe DAT4..DAT7 */
+		at91_set_A_periph(AT91_PIN_PA0, 1);
+		if (data->bus_width == 4) {
+			at91_set_A_periph(AT91_PIN_PA3, 1);
+			at91_set_A_periph(AT91_PIN_PA4, 1);
+			at91_set_A_periph(AT91_PIN_PA5, 1);
+		}
+
+		mci0_device.platform_data = data;
+		at91_clock_associate("mci0_clk", &mci0_device, "mci_clk");
+		register_device(&mci0_device);
+
+	} else {			/* MCI1 */
+		/* CLK */
+		at91_set_A_periph(AT91_PIN_PA6, 0);
+
+		/* CMD */
+		at91_set_A_periph(AT91_PIN_PA7, 1);
+
+		/* DAT0, maybe DAT1..DAT3 */
+		at91_set_A_periph(AT91_PIN_PA8, 1);
+		if (data->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);
+		}
+
+		mci1_device.platform_data = data;
+		at91_clock_associate("mci1_clk", &mci1_device, "mci_clk");
+		register_device(&mci1_device);
+	}
+}
+#else
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data) {}
+#endif
diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
index ddb005a..dc01705 100644
--- a/arch/arm/mach-at91/at91sam9g45_devices.c
+++ b/arch/arm/mach-at91/at91sam9g45_devices.c
@@ -10,6 +10,7 @@
  *
  */
 #include <common.h>
+#include <sizes.h>
 #include <asm/armlinux.h>
 #include <asm/hardware.h>
 #include <mach/at91_pmc.h>
@@ -240,3 +241,93 @@ void at91_register_uart(unsigned id, unsigned pins)
 	}
 
 }
+
+#if defined(CONFIG_MCI_ATMEL)
+static struct device_d mci0_device = {
+	.id		= 0,
+	.name		= "atmel_mci",
+	.map_base	= AT91SAM9G45_BASE_MCI0,
+	.size		= SZ_16K,
+};
+
+static struct device_d mci1_device = {
+	.id		= 1,
+	.name		= "atmel_mci",
+	.map_base	= AT91SAM9G45_BASE_MCI1,
+	.size		= SZ_16K,
+};
+
+/* Consider only one slot : slot 0 */
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data)
+{
+	if (!data)
+		return;
+
+	/* need bus_width */
+	if (!data->bus_width)
+		return;
+
+	/* input/irq */
+	if (data->detect_pin) {
+		at91_set_gpio_input(data->detect_pin, 1);
+		at91_set_deglitch(data->detect_pin, 1);
+	}
+
+	if (data->wp_pin)
+		at91_set_gpio_input(data->wp_pin, 1);
+
+	if (mmc_id == 0) {		/* MCI0 */
+		/* CLK */
+		at91_set_A_periph(AT91_PIN_PA0, 0);
+
+		/* CMD */
+		at91_set_A_periph(AT91_PIN_PA1, 1);
+
+		/* DAT0, maybe DAT1..DAT3 and maybe DAT4..DAT7 */
+		at91_set_A_periph(AT91_PIN_PA2, 1);
+		if (data->bus_width == 4) {
+			at91_set_A_periph(AT91_PIN_PA3, 1);
+			at91_set_A_periph(AT91_PIN_PA4, 1);
+			at91_set_A_periph(AT91_PIN_PA5, 1);
+			if (data->bus_width == 8) {
+				at91_set_A_periph(AT91_PIN_PA6, 1);
+				at91_set_A_periph(AT91_PIN_PA7, 1);
+				at91_set_A_periph(AT91_PIN_PA8, 1);
+				at91_set_A_periph(AT91_PIN_PA9, 1);
+			}
+		}
+
+		mci0_device.platform_data = data;
+		at91_clock_associate("mci0_clk", &mci0_device, "mci_clk");
+		register_device(&mci0_device);
+
+	} else {			/* MCI1 */
+		/* CLK */
+		at91_set_A_periph(AT91_PIN_PA31, 0);
+
+		/* CMD */
+		at91_set_A_periph(AT91_PIN_PA22, 1);
+
+		/* DAT0, maybe DAT1..DAT3 and maybe DAT4..DAT7 */
+		at91_set_A_periph(AT91_PIN_PA23, 1);
+		if (data->bus_width == 4) {
+			at91_set_A_periph(AT91_PIN_PA24, 1);
+			at91_set_A_periph(AT91_PIN_PA25, 1);
+			at91_set_A_periph(AT91_PIN_PA26, 1);
+			if (data->bus_width == 8) {
+				at91_set_A_periph(AT91_PIN_PA27, 1);
+				at91_set_A_periph(AT91_PIN_PA28, 1);
+				at91_set_A_periph(AT91_PIN_PA29, 1);
+				at91_set_A_periph(AT91_PIN_PA30, 1);
+			}
+		}
+
+		mci1_device.platform_data = data;
+		at91_clock_associate("mci1_clk", &mci1_device, "mci_clk");
+		register_device(&mci1_device);
+	}
+}
+#else
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data) {}
+#endif
+
diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index 1ab05ad..89c1746 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -63,4 +63,14 @@ void at91_add_device_sdram(u32 size);
 #define ATMEL_UART_RI	0x20
 
 void at91_register_uart(unsigned id, unsigned pins);
+
+/* Multimedia Card Interface */
+struct atmel_mci_platform_data {
+	unsigned bus_width;
+	unsigned host_caps; /* MCI_MODE_* from mci.h */
+	unsigned detect_pin;
+	unsigned wp_pin;
+};
+
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data);
 #endif
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index 5d8adbd..7b71b99 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -73,4 +73,11 @@ config MCI_OMAP_HSMMC
 	  Enable this entry to add support to read and write SD cards on a
 	  OMAP4 based system.
 
+config MCI_ATMEL
+	bool "ATMEL (AT91)"
+	depends on ARCH_AT91
+	help
+	  Enable this entry to add support to read and write SD cards on a
+	  Atmel AT91.
+
 endif
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index 2bb9a93..4fc0046 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_MCI_S3C) += s3c.o
 obj-$(CONFIG_MCI_IMX) += imx.o
 obj-$(CONFIG_MCI_IMX_ESDHC) += imx-esdhc.o
 obj-$(CONFIG_MCI_OMAP_HSMMC) += omap_hsmmc.o
+obj-$(CONFIG_MCI_ATMEL) += atmel_mci.o
diff --git a/drivers/mci/at91_mci.h b/drivers/mci/at91_mci.h
new file mode 100644
index 0000000..f526da8
--- /dev/null
+++ b/drivers/mci/at91_mci.h
@@ -0,0 +1,121 @@
+/*
+ * [origin: Linux kernel arch/arm/mach-at91/include/mach/at91_mci.h]
+ *
+ * Copyright (C) 2005 Ivan Kokshaysky
+ * Copyright (C) SAN People
+ *
+ * MultiMedia Card Interface (MCI) registers.
+ * Based on AT91RM9200 datasheet revision F.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef AT91_MCI_H
+#define AT91_MCI_H
+
+#define AT91_MCI_CR		0x00		/* Control Register */
+#define		AT91_MCI_MCIEN		(1 <<  0)	/* Multi-Media Interface Enable */
+#define		AT91_MCI_MCIDIS		(1 <<  1)	/* Multi-Media Interface Disable */
+#define		AT91_MCI_PWSEN		(1 <<  2)	/* Power Save Mode Enable */
+#define		AT91_MCI_PWSDIS		(1 <<  3)	/* Power Save Mode Disable */
+#define		AT91_MCI_SWRST		(1 <<  7)	/* Software Reset */
+
+#define AT91_MCI_MR		0x04		/* Mode Register */
+#define		AT91_MCI_CLKDIV		(0xff  <<  0)	/* Clock Divider */
+#define		AT91_MCI_PWSDIV		(7     <<  8)	/* Power Saving Divider */
+#define		AT91_MCI_RDPROOF	(1     << 11)	/* Read Proof Enable [SAM926[03] only] */
+#define		AT91_MCI_WRPROOF	(1     << 12)	/* Write Proof Enable [SAM926[03] only] */
+#define		AT91_MCI_PDCFBYTE	(1     << 13)	/* PDC Force Byte Transfer [SAM926[03] only] */
+#define		AT91_MCI_PDCPADV	(1     << 14)	/* PDC Padding Value */
+#define		AT91_MCI_PDCMODE	(1     << 15)	/* PDC-orientated Mode */
+#define		AT91_MCI_BLKLEN		(0xfff << 18)	/* Data Block Length */
+
+#define AT91_MCI_DTOR		0x08		/* Data Timeout Register */
+#define		AT91_MCI_DTOCYC		(0xf << 0)	/* Data Timeout Cycle Number */
+#define		AT91_MCI_DTOMUL		(7   << 4)	/* Data Timeout Multiplier */
+#define		AT91_MCI_DTOMUL_1		(0 <<  4)
+#define		AT91_MCI_DTOMUL_16		(1 <<  4)
+#define		AT91_MCI_DTOMUL_128		(2 <<  4)
+#define		AT91_MCI_DTOMUL_256		(3 <<  4)
+#define		AT91_MCI_DTOMUL_1K		(4 <<  4)
+#define		AT91_MCI_DTOMUL_4K		(5 <<  4)
+#define		AT91_MCI_DTOMUL_64K		(6 <<  4)
+#define		AT91_MCI_DTOMUL_1M		(7 <<  4)
+
+#define AT91_MCI_SDCR		0x0c		/* SD Card Register */
+#define		AT91_MCI_SDCSEL		(3 << 0)	/* SD Card Selector */
+#define		AT91_MCI_SDCBUS		(3 << 6)	/* 1-bit, 4-bit, or 8-bit bus */
+#define			AT91_MCI_SDCBUS_1BIT	(0 << 6)	/* 1-bit bus */
+#define			AT91_MCI_SDCBUS_4BIT	(2 << 6)	/* 4-bit bus */
+#define			AT91_MCI_SDCBUS_8BIT	(3 << 6)	/* 8-bit bus */
+
+#define AT91_MCI_ARGR		0x10		/* Argument Register */
+
+#define AT91_MCI_CMDR		0x14		/* Command Register */
+#define		AT91_MCI_CMDNB		(0x3f << 0)	/* Command Number */
+#define		AT91_MCI_RSPTYP		(3    << 6)	/* Response Type */
+#define			AT91_MCI_RSPTYP_NONE	(0 <<  6)
+#define			AT91_MCI_RSPTYP_48	(1 <<  6)
+#define			AT91_MCI_RSPTYP_136	(2 <<  6)
+#define			AT91_MCI_RSPTYP_R1B	(3 <<  6)
+#define		AT91_MCI_SPCMD		(7    << 8)	/* Special Command */
+#define			AT91_MCI_SPCMD_NONE	(0 <<  8)
+#define			AT91_MCI_SPCMD_INIT	(1 <<  8)
+#define			AT91_MCI_SPCMD_SYNC	(2 <<  8)
+#define			AT91_MCI_SPCMD_ICMD	(4 <<  8)
+#define			AT91_MCI_SPCMD_IRESP	(5 <<  8)
+#define		AT91_MCI_OPDCMD		(1 << 11)	/* Open Drain Command */
+#define		AT91_MCI_MAXLAT		(1 << 12)	/* Max Latency for Command to Response */
+#define		AT91_MCI_TRCMD		(3 << 16)	/* Transfer Command */
+#define			AT91_MCI_TRCMD_NONE	(0 << 16)
+#define			AT91_MCI_TRCMD_START	(1 << 16)
+#define			AT91_MCI_TRCMD_STOP	(2 << 16)
+#define		AT91_MCI_TRDIR		(1 << 18)	/* Transfer Direction */
+#define			AT91_MCI_TRDIR_RX	(1 << 18)	/* Read Transfer Direction */
+#define			AT91_MCI_TRDIR_TX	(0 << 18)	/* Write Transfer Direction */
+#define		AT91_MCI_TRTYP		(3 << 19)	/* Transfer Type */
+#define			AT91_MCI_TRTYP_BLOCK	(0 << 19)
+#define			AT91_MCI_TRTYP_MULTIPLE	(1 << 19)
+#define			AT91_MCI_TRTYP_STREAM	(2 << 19)
+#define			AT91_MCI_TRTYP_SDIO_BYTE	(4 << 19)
+#define			AT91_MCI_TRTYP_SDIO_BLOCK	(5 << 19)
+
+#define AT91_MCI_BLKR		0x18		/* Block Register */
+#define		AT91_MCI_BLKR_BCNT(n)	((0xffff & (n)) << 0)	/* Block count */
+#define		AT91_MCI_BLKR_BLKLEN(n)	((0xffff & (n)) << 16)	/* Block length */
+
+#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_SR		0x40		/* Status Register */
+#define		AT91_MCI_CMDRDY		(1 <<  0)	/* Command Ready */
+#define		AT91_MCI_RXRDY		(1 <<  1)	/* Receiver Ready */
+#define		AT91_MCI_TXRDY		(1 <<  2)	/* Transmit Ready */
+#define		AT91_MCI_BLKE		(1 <<  3)	/* Data Block Ended */
+#define		AT91_MCI_DTIP		(1 <<  4)	/* Data Transfer in Progress */
+#define		AT91_MCI_NOTBUSY	(1 <<  5)	/* Data Not Busy */
+#define		AT91_MCI_ENDRX		(1 <<  6)	/* End of RX Buffer */
+#define		AT91_MCI_ENDTX		(1 <<  7)	/* End fo TX Buffer */
+#define		AT91_MCI_SDIOIRQA	(1 <<  8)	/* SDIO Interrupt for Slot A */
+#define		AT91_MCI_SDIOIRQB	(1 <<  9)	/* SDIO Interrupt for Slot B */
+#define		AT91_MCI_RXBUFF		(1 << 14)	/* RX Buffer Full */
+#define		AT91_MCI_TXBUFE		(1 << 15)	/* TX Buffer Empty */
+#define		AT91_MCI_RINDE		(1 << 16)	/* Response Index Error */
+#define		AT91_MCI_RDIRE		(1 << 17)	/* Response Direction Error */
+#define		AT91_MCI_RCRCE		(1 << 18)	/* Response CRC Error */
+#define		AT91_MCI_RENDE		(1 << 19)	/* Response End Bit Error */
+#define		AT91_MCI_RTOE		(1 << 20)	/* Response Time-out Error */
+#define		AT91_MCI_DCRCE		(1 << 21)	/* Data CRC Error */
+#define		AT91_MCI_DTOE		(1 << 22)	/* Data Time-out Error */
+#define		AT91_MCI_OVRE		(1 << 30)	/* Overrun */
+#define		AT91_MCI_UNRE		(1 << 31)	/* Underrun */
+
+#define AT91_MCI_IER		0x44		/* Interrupt Enable Register */
+#define AT91_MCI_IDR		0x48		/* Interrupt Disable Register */
+#define AT91_MCI_IMR		0x4c		/* Interrupt Mask Register */
+
+#endif
diff --git a/drivers/mci/atmel_mci.c b/drivers/mci/atmel_mci.c
new file mode 100644
index 0000000..46a6fce
--- /dev/null
+++ b/drivers/mci/atmel_mci.c
@@ -0,0 +1,526 @@
+/*
+ * Atmel AT91 MCI driver
+ *
+ * Copyright (C) 2011 Hubert Feurstein <h.feurstein@gmail.com>
+ *
+ * heavily based on imx.c by:
+ * Copyright (C) 2009 Ilya Yanok, <yanok@emcraft.com>
+ * Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com>
+ *
+ * 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.
+ *
+ */
+
+/* #define DEBUG */
+
+#include <common.h>
+#include <init.h>
+#include <mci.h>
+#include <errno.h>
+#include <clock.h>
+#include <gpio.h>
+#include <asm/io.h>
+#include <mach/board.h>
+#include <linux/clk.h>
+
+#include "at91_mci.h"
+
+/*
+ * Structure for struct SoC access.
+ * Names starting with '_' are fillers.
+ */
+struct atmel_mci_regs {
+	/*	reg	Offset */
+	u32	cr;	/* 0x00 */
+	u32	mr;	/* 0x04 */
+	u32	dtor;	/* 0x08 */
+	u32	sdcr;	/* 0x0c */
+	u32	argr;	/* 0x10 */
+	u32	cmdr;	/* 0x14 */
+	u32	blkr;	/* 0x18 */
+	u32	_1c;	/* 0x1c */
+	u32	rspr;	/* 0x20 */
+	u32	rspr1;	/* 0x24 */
+	u32	rspr2;	/* 0x28 */
+	u32	rspr3;	/* 0x2c */
+	u32	rdr;	/* 0x30 */
+	u32	tdr;	/* 0x34 */
+	u32	_38;	/* 0x38 */
+	u32	_3c;	/* 0x3c */
+	u32	sr;	/* 0x40 */
+	u32	ier;	/* 0x44 */
+	u32	idr;	/* 0x48 */
+	u32	imr;	/* 0x4c */
+};
+
+struct atmel_mci_host {
+	struct mci_host mci;
+	struct atmel_mci_regs volatile __iomem *base;
+	struct device_d *hw_dev;
+	struct clk *clk;
+
+	u32 datasize;
+	struct mci_cmd *cmd;
+	struct mci_data *data;
+};
+
+#define to_mci_host(mci)	container_of(mci, struct atmel_mci_host, mci)
+
+#define STATUS_ERROR_MASK	(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)
+
+static void atmel_mci_reset(struct atmel_mci_host *host)
+{
+	writel(AT91_MCI_SWRST | AT91_MCI_MCIDIS, &host->base->cr);
+	writel(0x7f, &host->base->dtor);
+	writel(~0UL, &host->base->idr);
+}
+
+static void atmel_set_clk_rate(struct atmel_mci_host *host,
+			       unsigned int clk_ios)
+{
+	unsigned int divider;
+	unsigned int clk_in = clk_get_rate(host->clk);
+
+	if (clk_ios > 0) {
+		divider = (clk_in / clk_ios) / 2;
+		if (divider > 0)
+			divider -= 1;
+	}
+
+	if (clk_ios == 0 || divider > 255)
+		divider = 255;
+
+	dev_dbg(host->hw_dev, "atmel_set_clk_rate: clkIn=%d clkIos=%d divider=%d\n",
+		clk_in, clk_ios, divider);
+
+	writel(((AT91_MCI_CLKDIV & divider)
+		| AT91_MCI_RDPROOF
+		| AT91_MCI_WRPROOF), &host->base->mr);
+}
+
+static int atmel_poll_status(struct atmel_mci_host *host, u32 mask)
+{
+	u32 stat;
+	uint64_t start = get_time_ns();
+
+	do {
+		stat = readl(&host->base->sr);
+		if (stat & STATUS_ERROR_MASK)
+			return stat;
+		if (is_timeout(start, SECOND)) {
+			dev_err(host->hw_dev, "timeout\n");
+			return AT91_MCI_RTOE | stat;
+		}
+		if (stat & mask)
+			return 0;
+	} while (1);
+}
+
+static int atmel_pull(struct atmel_mci_host *host, void *_buf, int bytes)
+{
+	unsigned int stat;
+	u32 *buf = _buf;
+
+	while (bytes > 3) {
+		stat = atmel_poll_status(host, AT91_MCI_RXRDY);
+		if (stat)
+			return stat;
+
+		*buf++ = readl(&host->base->rdr);
+		bytes -= 4;
+	}
+
+	if (bytes) {
+		u8 *b = (u8 *)buf;
+		u32 tmp;
+
+		stat = atmel_poll_status(host, AT91_MCI_RXRDY);
+		if (stat)
+			return stat;
+
+		tmp = readl(&host->base->rdr);
+		memcpy(b, &tmp, bytes);
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_MCI_WRITE
+static int atmel_push(struct atmel_mci_host *host, const void *_buf, int bytes)
+{
+	unsigned int stat;
+	const u32 *buf = _buf;
+
+	while (bytes > 3) {
+		stat = atmel_poll_status(host, AT91_MCI_TXRDY);
+		if (stat)
+			return stat;
+
+		writel(*buf++, &host->base->tdr);
+		bytes -= 4;
+	}
+
+	if (bytes) {
+		const u8 *b = (u8 *)buf;
+		u32 tmp;
+
+		stat = atmel_poll_status(host, AT91_MCI_TXRDY);
+		if (stat)
+			return stat;
+
+		memcpy(&tmp, b, bytes);
+		writel(tmp, &host->base->tdr);
+	}
+
+	stat = atmel_poll_status(host, AT91_MCI_TXRDY);
+	if (stat)
+		return stat;
+
+	return 0;
+}
+#endif /* CONFIG_MCI_WRITE */
+
+static int atmel_transfer_data(struct atmel_mci_host *host)
+{
+	struct mci_data *data = host->data;
+	int stat;
+	unsigned long length;
+
+	length = data->blocks * data->blocksize;
+	host->datasize = 0;
+
+	if (data->flags & MMC_DATA_READ) {
+		stat = atmel_pull(host, data->dest, length);
+		if (stat)
+			return stat;
+
+		stat = atmel_poll_status(host, AT91_MCI_NOTBUSY);
+		if (stat)
+			return stat;
+
+		host->datasize += length;
+	} else {
+#ifdef CONFIG_MCI_WRITE
+		stat = atmel_push(host, (const void *)(data->src), length);
+		if (stat)
+			return stat;
+
+		host->datasize += length;
+		stat = atmel_poll_status(host, AT91_MCI_NOTBUSY);
+		if (stat)
+			return stat;
+#endif /* CONFIG_MCI_WRITE */
+	}
+	return 0;
+}
+
+static void atmel_finish_request(struct atmel_mci_host *host)
+{
+	host->cmd = NULL;
+	host->data = NULL;
+}
+
+static int atmel_finish_data(struct atmel_mci_host *host, unsigned int stat)
+{
+	int data_error = 0;
+
+	if (stat & STATUS_ERROR_MASK) {
+		dev_err(host->hw_dev, "request failed (status=0x%08x)\n", stat);
+		if (stat & AT91_MCI_DCRCE)
+			data_error = -EILSEQ;
+		else if (stat & (AT91_MCI_RTOE | AT91_MCI_DTOE))
+			data_error = -ETIMEDOUT;
+		else
+			data_error = -EIO;
+	}
+
+	host->data = NULL;
+
+	return data_error;
+}
+
+static void atmel_setup_data(struct atmel_mci_host *host, struct mci_data *data)
+{
+	unsigned int nob = data->blocks;
+	unsigned int blksz = data->blocksize;
+	unsigned int datasize = nob * blksz;
+
+	BUG_ON(data->blocksize & 3);
+	BUG_ON(nob == 0);
+
+	host->data = data;
+
+	dev_dbg(host->hw_dev, "atmel_setup_data: nob=%d blksz=%d\n",
+		nob, blksz);
+
+	writel(AT91_MCI_BLKR_BCNT(nob)
+		| AT91_MCI_BLKR_BLKLEN(blksz), &host->base->blkr);
+
+	host->datasize = datasize;
+}
+
+static int atmel_read_response(struct atmel_mci_host *host, unsigned int stat)
+{
+	struct mci_cmd *cmd = host->cmd;
+	int i;
+	u32 *resp = (u32 *)cmd->response;
+
+	if (!cmd)
+		return 0;
+
+	if (stat & (AT91_MCI_RTOE | AT91_MCI_DTOE)) {
+		dev_err(host->hw_dev, "command/data timeout\n");
+		return -ETIMEDOUT;
+	} else if ((stat & AT91_MCI_RCRCE) && (cmd->resp_type & MMC_RSP_CRC)) {
+		dev_err(host->hw_dev, "cmd crc error\n");
+		return -EILSEQ;
+	}
+
+	if (cmd->resp_type & MMC_RSP_PRESENT) {
+		if (cmd->resp_type & MMC_RSP_136) {
+			for (i = 0; i < 4; i++)
+				resp[i] = readl(&host->base->rspr);
+		} else {
+			resp[0] = readl(&host->base->rspr);
+		}
+	}
+
+	return 0;
+}
+
+static int atmel_cmd_done(struct atmel_mci_host *host, unsigned int stat)
+{
+	int datastat;
+	int ret;
+
+	ret = atmel_read_response(host, stat);
+
+	if (ret) {
+		atmel_finish_request(host);
+		return ret;
+	}
+
+	if (!host->data) {
+		atmel_finish_request(host);
+		return 0;
+	}
+
+	datastat = atmel_transfer_data(host);
+	ret = atmel_finish_data(host, datastat);
+	atmel_finish_request(host);
+	return ret;
+}
+
+static int atmel_start_cmd(struct atmel_mci_host *host, struct mci_cmd *cmd,
+			   unsigned int cmdat)
+{
+	unsigned flags = 0;
+	unsigned cmdval = 0;
+
+	if (host->cmd != NULL)
+		dev_err(host->hw_dev, "error!\n");
+
+	if ((readl(&host->base->sr) & AT91_MCI_CMDRDY) == 0) {
+		dev_err(host->hw_dev, "mci not ready!\n");
+		return -EBUSY;
+	}
+
+	host->cmd = cmd;
+	cmdval = AT91_MCI_CMDNB & cmd->cmdidx;
+
+	switch (cmd->resp_type) {
+	case MMC_RSP_R1: /* short CRC, OPCODE */
+	case MMC_RSP_R1b:/* short CRC, OPCODE, BUSY */
+		flags |= AT91_MCI_RSPTYP_48;
+		break;
+	case MMC_RSP_R2: /* long 136 bit + CRC */
+		flags |= AT91_MCI_RSPTYP_136;
+		break;
+	case MMC_RSP_R3: /* short */
+		flags |= AT91_MCI_RSPTYP_48;
+		break;
+	case MMC_RSP_NONE:
+		flags |= AT91_MCI_RSPTYP_NONE;
+		break;
+	default:
+		dev_err(host->hw_dev, "unhandled response type 0x%x\n",
+				cmd->resp_type);
+		return -EINVAL;
+	}
+	cmdval |= AT91_MCI_RSPTYP & flags;
+	cmdval |= cmdat & ~(AT91_MCI_CMDNB | AT91_MCI_RSPTYP);
+
+	writel(cmd->cmdarg, &host->base->argr);
+	writel(cmdval, &host->base->cmdr);
+
+	return 0;
+}
+
+/** init the host interface */
+static int mci_reset(struct mci_host *mci, struct device_d *mci_dev)
+{
+	int ret;
+	struct atmel_mci_host *host = to_mci_host(mci);
+	struct atmel_mci_platform_data *pd = host->hw_dev->platform_data;
+
+	ret = gpio_get_value(pd->detect_pin);
+	dev_dbg(host->hw_dev, "card %sdetected\n", ret != 0 ? "not " : "");
+
+	if (pd->detect_pin && ret == 1)
+		return -ENODEV;
+
+	clk_enable(host->clk);
+	atmel_mci_reset(host);
+
+	return 0;
+}
+
+/** change host interface settings */
+static void mci_set_ios(struct mci_host *mci, struct device_d *mci_dev,
+			unsigned bus_width, unsigned clock)
+{
+	struct atmel_mci_host *host = to_mci_host(mci);
+
+	dev_dbg(host->hw_dev, "atmel_mci_set_ios: bus_width=%d clk=%d\n",
+		bus_width, clock);
+
+	if (bus_width == 8)
+		writel(AT91_MCI_SDCBUS_8BIT, &host->base->sdcr);
+	if (bus_width == 4)
+		writel(AT91_MCI_SDCBUS_4BIT, &host->base->sdcr);
+	else
+		writel(AT91_MCI_SDCBUS_1BIT, &host->base->sdcr);
+
+	if (clock) {
+		atmel_set_clk_rate(host, clock);
+		writel(AT91_MCI_MCIEN, &host->base->cr);
+	} else {
+		writel(AT91_MCI_MCIDIS, &host->base->cr);
+	}
+
+	return;
+}
+
+/** handle a command */
+static int mci_request(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
+{
+	struct atmel_mci_host *host = to_mci_host(mci);
+	u32 stat, cmdat = 0;
+	int ret;
+
+	if (cmd->resp_type != MMC_RSP_NONE)
+		cmdat |= AT91_MCI_MAXLAT;
+
+	if (data) {
+		atmel_setup_data(host, data);
+
+		cmdat |= AT91_MCI_TRCMD_START | AT91_MCI_TRTYP_MULTIPLE;
+
+		if (data->flags & MMC_DATA_READ)
+			cmdat |= AT91_MCI_TRDIR_RX;
+	}
+
+	ret = atmel_start_cmd(host, cmd, cmdat);
+	if (ret) {
+		atmel_finish_request(host);
+		return ret;
+	}
+
+	stat = atmel_poll_status(host, AT91_MCI_CMDRDY);
+	return atmel_cmd_done(host, stat);
+}
+
+#ifdef CONFIG_MCI_INFO
+static void mci_info(struct device_d *mci_dev)
+{
+	struct atmel_mci_host *host = mci_dev->priv;
+	struct atmel_mci_platform_data *pd = host->hw_dev->platform_data;
+
+	printf("  Bus data width: %d bit\n", host->mci.bus_width);
+
+	printf("  Bus frequency: %u Hz\n", host->mci.clock);
+	printf("  Frequency limits: ");
+	if (host->mci.f_min == 0)
+		printf("no lower limit ");
+	else
+		printf("%u Hz lower limit ", host->mci.f_min);
+	if (host->mci.f_max == 0)
+		printf("- no upper limit");
+	else
+		printf("- %u Hz upper limit", host->mci.f_max);
+
+	printf("\n  Card detection support: %s\n",
+		pd->detect_pin != 0 ? "yes" : "no");
+
+}
+#endif /* CONFIG_MCI_INFO */
+
+static int mci_probe(struct device_d *hw_dev)
+{
+	unsigned long clk_rate;
+	struct atmel_mci_host *host;
+	struct atmel_mci_platform_data *pd = hw_dev->platform_data;
+
+	if (pd == NULL) {
+		dev_err(hw_dev, "missing platform data\n");
+		return -EINVAL;
+	}
+
+	host = xzalloc(sizeof(*host));
+	host->mci.send_cmd = mci_request;
+	host->mci.set_ios = mci_set_ios;
+	host->mci.init = mci_reset;
+
+	host->mci.host_caps = pd->host_caps;
+	if (pd->bus_width == 4)
+		host->mci.host_caps |= MMC_MODE_4BIT;
+	else if (pd->bus_width == 8)
+		host->mci.host_caps |= MMC_MODE_8BIT;
+
+	host->base = (struct atmel_mci_regs *)hw_dev->map_base;
+	host->hw_dev = hw_dev;
+	hw_dev->priv = host;
+	host->clk = clk_get(hw_dev, "mci_clk");
+	if (host->clk == NULL) {
+		dev_err(hw_dev, "no mci_clk\n");
+		return -EINVAL;
+	}
+
+	clk_rate = clk_get_rate(host->clk);
+
+	host->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+	host->mci.f_min = clk_rate >> 9;
+	host->mci.f_max = clk_rate >> 1;
+
+	mci_register(&host->mci);
+
+	return 0;
+}
+
+static struct driver_d atmel_mci_driver = {
+	.name	= "atmel_mci",
+	.probe	= mci_probe,
+#ifdef CONFIG_MCI_INFO
+	.info	= mci_info,
+#endif
+};
+
+static int atmel_mci_init_driver(void)
+{
+	register_driver(&atmel_mci_driver);
+	return 0;
+}
+
+device_initcall(atmel_mci_init_driver);
-- 
1.7.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH v2] mci: add Atmel AT91 MCI driver
  2011-06-01 11:48   ` [PATCH v2] " Hubert Feurstein
@ 2011-06-02 16:04     ` Jean-Christophe PLAGNIOL-VILLARD
  2011-06-06  7:43       ` Hubert Feurstein
  2011-06-02 16:05     ` [PATCH] at91sam9263ek: add mci1 support Jean-Christophe PLAGNIOL-VILLARD
  1 sibling, 1 reply; 9+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2011-06-02 16:04 UTC (permalink / raw)
  To: Hubert Feurstein; +Cc: Patrice Vilchez, barebox, Nicolas Ferre

On 13:48 Wed 01 Jun     , Hubert Feurstein wrote:
> The driver supports push and pull transfers.
> Tested on at91sam9m10 SoC.
> 
> Signed-off-by: Hubert Feurstein <h.feurstein@gmail.com>
> Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> ---
>  Changes against v1:
>  - Code cleanup according to checkpatch.pl.
>  - Add support for devices: 9260, 9261 and 9263 (but not tested!)
>  - Use dev_* function instead of pr_*
>
I've test it on at91sam9263ek and work fine

please fix the following comments
Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>

> diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
> index 1ab05ad..89c1746 100644
> --- a/arch/arm/mach-at91/include/mach/board.h
> +++ b/arch/arm/mach-at91/include/mach/board.h
> @@ -63,4 +63,14 @@ void at91_add_device_sdram(u32 size);
>  #define ATMEL_UART_RI	0x20
>  
>  void at91_register_uart(unsigned id, unsigned pins);
> +
> +/* Multimedia Card Interface */
> +struct atmel_mci_platform_data {
> +	unsigned bus_width;
> +	unsigned host_caps; /* MCI_MODE_* from mci.h */
> +	unsigned detect_pin;
> +	unsigned wp_pin;
> +};
we can have 2 slot but you allow only one
> +
> +void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data);
>  #endif
> diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
> index 5d8adbd..7b71b99 100644
> --- a/drivers/mci/Kconfig
> +++ b/drivers/mci/Kconfig
> @@ -73,4 +73,11 @@ config MCI_OMAP_HSMMC
>  	  Enable this entry to add support to read and write SD cards on a
>  	  OMAP4 based system.
>  
> +config MCI_ATMEL
> +	bool "ATMEL (AT91)"
> +	depends on ARCH_AT91
> +	help
> +	  Enable this entry to add support to read and write SD cards on a
> +	  Atmel AT91.
> +
>  endif

> + * 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.
> + *
> + */
> +
> +/* #define DEBUG */
no need please remove
> +
> +#include <common.h>
> +#include <init.h>
> +#include <mci.h>
> +#include <errno.h>
> +#include <clock.h>
> +#include <gpio.h>
> +#include <asm/io.h>
> +#include <mach/board.h>
> +#include <linux/clk.h>
> +
> +#include "at91_mci.h"
> +
> +/*
> + * Structure for struct SoC access.
> + * Names starting with '_' are fillers.
> + */
> +struct atmel_mci_regs {
> +	/*	reg	Offset */
> +	u32	cr;	/* 0x00 */
> +	u32	mr;	/* 0x04 */
> +	u32	dtor;	/* 0x08 */
> +	u32	sdcr;	/* 0x0c */
> +	u32	argr;	/* 0x10 */
> +	u32	cmdr;	/* 0x14 */
> +	u32	blkr;	/* 0x18 */
> +	u32	_1c;	/* 0x1c */
> +	u32	rspr;	/* 0x20 */
> +	u32	rspr1;	/* 0x24 */
> +	u32	rspr2;	/* 0x28 */
> +	u32	rspr3;	/* 0x2c */
> +	u32	rdr;	/* 0x30 */
> +	u32	tdr;	/* 0x34 */
> +	u32	_38;	/* 0x38 */
> +	u32	_3c;	/* 0x3c */
> +	u32	sr;	/* 0x40 */
> +	u32	ier;	/* 0x44 */
> +	u32	idr;	/* 0x48 */
> +	u32	imr;	/* 0x4c */
> +};
please use the same as the kernel here
offset not a struct
> +
> +struct atmel_mci_host {
> +	struct mci_host mci;
> +	struct atmel_mci_regs volatile __iomem *base;
> +	struct device_d *hw_dev;
> +	struct clk *clk;
> +
> +	u32 datasize;
> +	struct mci_cmd *cmd;
> +	struct mci_data *data;
> +};
> +

> +static int atmel_pull(struct atmel_mci_host *host, void *_buf, int bytes)
> +{
> +	unsigned int stat;
> +	u32 *buf = _buf;
> +
> +	while (bytes > 3) {
> +		stat = atmel_poll_status(host, AT91_MCI_RXRDY);
> +		if (stat)
> +			return stat;
> +
> +		*buf++ = readl(&host->base->rdr);
> +		bytes -= 4;
> +	}
> +
> +	if (bytes) {
> +		u8 *b = (u8 *)buf;
> +		u32 tmp;
> +
> +		stat = atmel_poll_status(host, AT91_MCI_RXRDY);
> +		if (stat)
> +			return stat;
> +
> +		tmp = readl(&host->base->rdr);
> +		memcpy(b, &tmp, bytes);
please use __iowrite32 to speedup the copy
> +	}
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_MCI_WRITE
> +static int atmel_push(struct atmel_mci_host *host, const void *_buf, int bytes)
> +{
> +	unsigned int stat;
> +	const u32 *buf = _buf;
> +
> +	while (bytes > 3) {
> +		stat = atmel_poll_status(host, AT91_MCI_TXRDY);
> +		if (stat)
> +			return stat;
> +
> +		writel(*buf++, &host->base->tdr);
> +		bytes -= 4;
> +	}
> +
> +	if (bytes) {
> +		const u8 *b = (u8 *)buf;
> +		u32 tmp;
> +
> +		stat = atmel_poll_status(host, AT91_MCI_TXRDY);
> +		if (stat)
> +			return stat;
> +
> +		memcpy(&tmp, b, bytes);
ditto
> +		writel(tmp, &host->base->tdr);
> +	}
> +
> +	stat = atmel_poll_status(host, AT91_MCI_TXRDY);
> +	if (stat)
> +		return stat;
> +
> +	return 0;
> +}
> +#endif /* CONFIG_MCI_WRITE */
> +
> +static int atmel_transfer_data(struct atmel_mci_host *host)
> +{
> +	struct mci_data *data = host->data;
> +	int stat;
> +	unsigned long length;
> +
> +	length = data->blocks * data->blocksize;
> +	host->datasize = 0;
> +
> +	if (data->flags & MMC_DATA_READ) {
> +		stat = atmel_pull(host, data->dest, length);
> +		if (stat)
> +			return stat;
> +
> +/** change host interface settings */
> +static void mci_set_ios(struct mci_host *mci, struct device_d *mci_dev,
> +			unsigned bus_width, unsigned clock)
> +{
> +	struct atmel_mci_host *host = to_mci_host(mci);
> +
> +	dev_dbg(host->hw_dev, "atmel_mci_set_ios: bus_width=%d clk=%d\n",
> +		bus_width, clock);
> +
> +	if (bus_width == 8)
> +		writel(AT91_MCI_SDCBUS_8BIT, &host->base->sdcr);
> +	if (bus_width == 4)
> +		writel(AT91_MCI_SDCBUS_4BIT, &host->base->sdcr);
> +	else
> +		writel(AT91_MCI_SDCBUS_1BIT, &host->base->sdcr);
switch here
> +
> +	if (clock) {
> +		atmel_set_clk_rate(host, clock);
> +		writel(AT91_MCI_MCIEN, &host->base->cr);
> +	} else {
> +		writel(AT91_MCI_MCIDIS, &host->base->cr);
> +	}
> +
> +	return;
> +}
> +
> +/** handle a command */
> +static int mci_request(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
> +{
> +	struct atmel_mci_host *host = to_mci_host(mci);
> +	u32 stat, cmdat = 0;
> +	int ret;
> +
> +	if (cmd->resp_type != MMC_RSP_NONE)
> +		cmdat |= AT91_MCI_MAXLAT;
> +
> +	if (data) {
> +		atmel_setup_data(host, data);
> +
> +		cmdat |= AT91_MCI_TRCMD_START | AT91_MCI_TRTYP_MULTIPLE;
> +
> +		if (data->flags & MMC_DATA_READ)
> +			cmdat |= AT91_MCI_TRDIR_RX;
> +	}
> +
> +	ret = atmel_start_cmd(host, cmd, cmdat);
> +	if (ret) {
> +		atmel_finish_request(host);
> +		return ret;
> +	}
> +
> +	stat = atmel_poll_status(host, AT91_MCI_CMDRDY);
> +	return atmel_cmd_done(host, stat);
> +}
> +
> +#ifdef CONFIG_MCI_INFO
> +static void mci_info(struct device_d *mci_dev)
> +{
> +	struct atmel_mci_host *host = mci_dev->priv;
> +	struct atmel_mci_platform_data *pd = host->hw_dev->platform_data;
> +
> +	printf("  Bus data width: %d bit\n", host->mci.bus_width);
> +
> +	printf("  Bus frequency: %u Hz\n", host->mci.clock);
> +	printf("  Frequency limits: ");
> +	if (host->mci.f_min == 0)
> +		printf("no lower limit ");
> +	else
> +		printf("%u Hz lower limit ", host->mci.f_min);
> +	if (host->mci.f_max == 0)
> +		printf("- no upper limit");
> +	else
> +		printf("- %u Hz upper limit", host->mci.f_max);
> +
> +	printf("\n  Card detection support: %s\n",
> +		pd->detect_pin != 0 ? "yes" : "no");
> +
> +}
> +#endif /* CONFIG_MCI_INFO */
> +
> +static int mci_probe(struct device_d *hw_dev)
> +{
> +	unsigned long clk_rate;
> +	struct atmel_mci_host *host;
> +	struct atmel_mci_platform_data *pd = hw_dev->platform_data;
> +
> +	if (pd == NULL) {
if (!pd)
> +		dev_err(hw_dev, "missing platform data\n");
> +		return -EINVAL;
> +	}
> +
> +	host = xzalloc(sizeof(*host));
> +	host->mci.send_cmd = mci_request;
> +	host->mci.set_ios = mci_set_ios;
> +	host->mci.init = mci_reset;
> +

Best Regards,
J.

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH] at91sam9263ek: add mci1 support
  2011-06-01 11:48   ` [PATCH v2] " Hubert Feurstein
  2011-06-02 16:04     ` Jean-Christophe PLAGNIOL-VILLARD
@ 2011-06-02 16:05     ` Jean-Christophe PLAGNIOL-VILLARD
  1 sibling, 0 replies; 9+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2011-06-02 16:05 UTC (permalink / raw)
  To: barebox

enable fat support

Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
---
 arch/arm/boards/at91sam9263ek/init.c     |   16 ++++++++++++++++
 arch/arm/configs/at91sam9263ek_defconfig |    5 +++++
 2 files changed, 21 insertions(+), 0 deletions(-)

diff --git a/arch/arm/boards/at91sam9263ek/init.c b/arch/arm/boards/at91sam9263ek/init.c
index 8448866..9a763b3 100644
--- a/arch/arm/boards/at91sam9263ek/init.c
+++ b/arch/arm/boards/at91sam9263ek/init.c
@@ -99,6 +99,21 @@ static struct at91_ether_platform_data macb_pdata = {
 	.phy_addr = 0,
 };
 
+#if defined(CONFIG_MCI_ATMEL)
+static struct atmel_mci_platform_data __initdata ek_mci_data = {
+	.bus_width	= 4,
+	.detect_pin	= AT91_PIN_PE18,
+	.wp_pin		= AT91_PIN_PE19,
+};
+
+static void ek_add_device_mci(void)
+{
+	at91_add_device_mci(1, &ek_mci_data);
+}
+#else
+static void ek_add_device_mci(void) {}
+#endif
+
 static int at91sam9263ek_devices_init(void)
 {
 	/*
@@ -113,6 +128,7 @@ static int at91sam9263ek_devices_init(void)
 	ek_add_device_nand();
 	at91_add_device_eth(&macb_pdata);
 	register_device(&cfi_dev);
+	ek_add_device_mci();
 
 #if defined(CONFIG_DRIVER_CFI) || defined(CONFIG_DRIVER_CFI_OLD)
 	devfs_add_partition("nor0", 0x00000, 0x40000, PARTITION_FIXED, "self");
diff --git a/arch/arm/configs/at91sam9263ek_defconfig b/arch/arm/configs/at91sam9263ek_defconfig
index 271f7b2..308f0cd 100644
--- a/arch/arm/configs/at91sam9263ek_defconfig
+++ b/arch/arm/configs/at91sam9263ek_defconfig
@@ -40,3 +40,8 @@ CONFIG_MTD=y
 CONFIG_NAND=y
 CONFIG_NAND_ATMEL=y
 CONFIG_UBI=y
+CONFIG_MCI=y
+CONFIG_MCI_ATMEL=y
+CONFIG_FS_FAT=y
+CONFIG_FS_FAT_WRITE=y
+CONFIG_FS_FAT_LFN=y
-- 
1.7.4.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH v2] mci: add Atmel AT91 MCI driver
  2011-06-02 16:04     ` Jean-Christophe PLAGNIOL-VILLARD
@ 2011-06-06  7:43       ` Hubert Feurstein
  2011-06-06  9:04         ` Sascha Hauer
  2011-06-06 11:07         ` Jean-Christophe PLAGNIOL-VILLARD
  0 siblings, 2 replies; 9+ messages in thread
From: Hubert Feurstein @ 2011-06-06  7:43 UTC (permalink / raw)
  To: Jean-Christophe PLAGNIOL-VILLARD; +Cc: barebox

2011/6/2 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>:
> On 13:48 Wed 01 Jun     , Hubert Feurstein wrote:
[snip]
> I've test it on at91sam9263ek and work fine
Great, good to hear ;) I'll add also support for at91sam9m10g45ek
later in an extra commit.
>
> please fix the following comments
> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
>
[snip]
>> +/* Multimedia Card Interface */
>> +struct atmel_mci_platform_data {
>> +     unsigned bus_width;
>> +     unsigned host_caps; /* MCI_MODE_* from mci.h */
>> +     unsigned detect_pin;
>> +     unsigned wp_pin;
>> +};
>we can have 2 slot but you allow only one
Would it be ok to add the second slot in an extra commit?

[snip]
>> +/*
>> + * Structure for struct SoC access.
>> + * Names starting with '_' are fillers.
>> + */
>> +struct atmel_mci_regs {
>> +     /*      reg     Offset */
>> +     u32     cr;     /* 0x00 */
>> +     u32     mr;     /* 0x04 */
>> +     u32     dtor;   /* 0x08 */
>> +     u32     sdcr;   /* 0x0c */
>> +     u32     argr;   /* 0x10 */
>> +     u32     cmdr;   /* 0x14 */
>> +     u32     blkr;   /* 0x18 */
>> +     u32     _1c;    /* 0x1c */
>> +     u32     rspr;   /* 0x20 */
>> +     u32     rspr1;  /* 0x24 */
>> +     u32     rspr2;  /* 0x28 */
>> +     u32     rspr3;  /* 0x2c */
>> +     u32     rdr;    /* 0x30 */
>> +     u32     tdr;    /* 0x34 */
>> +     u32     _38;    /* 0x38 */
>> +     u32     _3c;    /* 0x3c */
>> +     u32     sr;     /* 0x40 */
>> +     u32     ier;    /* 0x44 */
>> +     u32     idr;    /* 0x48 */
>> +     u32     imr;    /* 0x4c */
>> +};
> please use the same as the kernel here
> offset not a struct
Of course I could change it to offset, but I thought this is the way
how it is done in barebox.
What is Sascha's opinion on that?

>> +
>> +struct atmel_mci_host {
>> +     struct mci_host mci;
>> +     struct atmel_mci_regs volatile __iomem *base;
>> +     struct device_d *hw_dev;
>> +     struct clk *clk;
>> +
>> +     u32 datasize;
>> +     struct mci_cmd *cmd;
>> +     struct mci_data *data;
>> +};
>> +
>
>> +static int atmel_pull(struct atmel_mci_host *host, void *_buf, int bytes)
>> +{
>> +     unsigned int stat;
>> +     u32 *buf = _buf;
>> +
>> +     while (bytes > 3) {
>> +             stat = atmel_poll_status(host, AT91_MCI_RXRDY);
>> +             if (stat)
>> +                     return stat;
>> +
>> +             *buf++ = readl(&host->base->rdr);
>> +             bytes -= 4;
>> +     }
>> +
>> +     if (bytes) {
>> +             u8 *b = (u8 *)buf;
>> +             u32 tmp;
>> +
>> +             stat = atmel_poll_status(host, AT91_MCI_RXRDY);
>> +             if (stat)
>> +                     return stat;
>> +
>> +             tmp = readl(&host->base->rdr);
>> +             memcpy(b, &tmp, bytes);
> please use __iowrite32 to speedup the copy
I think memcpy is alright here. Usually this code-path shouldn't be
executed anyway, because the mci-core always
requests multiples of 512 bytes, and here we copy only the last
remaining _three_ bytes.

>> +     }
>> +
>> +     return 0;
>> +}
>> +
[snip]
>> +
>> +     if (bus_width == 8)
>> +             writel(AT91_MCI_SDCBUS_8BIT, &host->base->sdcr);
>> +     if (bus_width == 4)
>> +             writel(AT91_MCI_SDCBUS_4BIT, &host->base->sdcr);
>> +     else
>> +             writel(AT91_MCI_SDCBUS_1BIT, &host->base->sdcr);
> switch here
OK

[snip]
>> +
>> +static int mci_probe(struct device_d *hw_dev)
>> +{
>> +     unsigned long clk_rate;
>> +     struct atmel_mci_host *host;
>> +     struct atmel_mci_platform_data *pd = hw_dev->platform_data;
>> +
>> +     if (pd == NULL) {
> if (!pd)
OK
>> +             dev_err(hw_dev, "missing platform data\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     host = xzalloc(sizeof(*host));
>> +     host->mci.send_cmd = mci_request;
>> +     host->mci.set_ios = mci_set_ios;
>> +     host->mci.init = mci_reset;
>> +
>
> Best Regards,
> J.
>
Best Regards and thank you for the support.
Hubert

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH v2] mci: add Atmel AT91 MCI driver
  2011-06-06  7:43       ` Hubert Feurstein
@ 2011-06-06  9:04         ` Sascha Hauer
  2011-06-06 11:07         ` Jean-Christophe PLAGNIOL-VILLARD
  1 sibling, 0 replies; 9+ messages in thread
From: Sascha Hauer @ 2011-06-06  9:04 UTC (permalink / raw)
  To: Hubert Feurstein; +Cc: barebox

On Mon, Jun 06, 2011 at 09:43:49AM +0200, Hubert Feurstein wrote:
> 2011/6/2 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>:
> > On 13:48 Wed 01 Jun     , Hubert Feurstein wrote:
> [snip]
> > I've test it on at91sam9263ek and work fine
> Great, good to hear ;) I'll add also support for at91sam9m10g45ek
> later in an extra commit.
> >
> > please fix the following comments
> > Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
> >
> [snip]
> >> +/* Multimedia Card Interface */
> >> +struct atmel_mci_platform_data {
> >> +     unsigned bus_width;
> >> +     unsigned host_caps; /* MCI_MODE_* from mci.h */
> >> +     unsigned detect_pin;
> >> +     unsigned wp_pin;
> >> +};
> >we can have 2 slot but you allow only one
> Would it be ok to add the second slot in an extra commit?
> 
> [snip]
> >> +/*
> >> + * Structure for struct SoC access.
> >> + * Names starting with '_' are fillers.
> >> + */
> >> +struct atmel_mci_regs {
> >> +     /*      reg     Offset */
> >> +     u32     cr;     /* 0x00 */
> >> +     u32     mr;     /* 0x04 */
> >> +     u32     dtor;   /* 0x08 */
> >> +     u32     sdcr;   /* 0x0c */
> >> +     u32     argr;   /* 0x10 */
> >> +     u32     cmdr;   /* 0x14 */
> >> +     u32     blkr;   /* 0x18 */
> >> +     u32     _1c;    /* 0x1c */
> >> +     u32     rspr;   /* 0x20 */
> >> +     u32     rspr1;  /* 0x24 */
> >> +     u32     rspr2;  /* 0x28 */
> >> +     u32     rspr3;  /* 0x2c */
> >> +     u32     rdr;    /* 0x30 */
> >> +     u32     tdr;    /* 0x34 */
> >> +     u32     _38;    /* 0x38 */
> >> +     u32     _3c;    /* 0x3c */
> >> +     u32     sr;     /* 0x40 */
> >> +     u32     ier;    /* 0x44 */
> >> +     u32     idr;    /* 0x48 */
> >> +     u32     imr;    /* 0x4c */
> >> +};
> > please use the same as the kernel here
> > offset not a struct
> Of course I could change it to offset, but I thought this is the way
> how it is done in barebox.

Nope, it's prefered in U-Boot but not barebox.

> What is Sascha's opinion on that?

Personally I prefer having defines instead of struct types for
variables. Reasons for this are:

- defines make the actual register offset clear without having to put
  comments after them. Also, you can't make mistakes while numbering
  the registers.
- Type safety is often an argument for using struct types, but 16 bit
  registers with 32bit alignment are just too ugly to describe in struct
  types. Also, if used in another SoC the registers might have a
  different alignment which is also quite ugly to describe in structs.

I know there are other opinions and I won't nack patches just because
of this. It's probably best to just use the way the drivers does you
copied this one from.


> >> +
> >> +struct atmel_mci_host {
> >> +     struct mci_host mci;
> >> +     struct atmel_mci_regs volatile __iomem *base;
> >> +     struct device_d *hw_dev;
> >> +     struct clk *clk;
> >> +
> >> +     u32 datasize;
> >> +     struct mci_cmd *cmd;
> >> +     struct mci_data *data;
> >> +};
> >> +
> >
> >> +static int atmel_pull(struct atmel_mci_host *host, void *_buf, int bytes)
> >> +{
> >> +     unsigned int stat;
> >> +     u32 *buf = _buf;
> >> +
> >> +     while (bytes > 3) {
> >> +             stat = atmel_poll_status(host, AT91_MCI_RXRDY);
> >> +             if (stat)
> >> +                     return stat;
> >> +
> >> +             *buf++ = readl(&host->base->rdr);
> >> +             bytes -= 4;
> >> +     }
> >> +
> >> +     if (bytes) {
> >> +             u8 *b = (u8 *)buf;
> >> +             u32 tmp;
> >> +
> >> +             stat = atmel_poll_status(host, AT91_MCI_RXRDY);
> >> +             if (stat)
> >> +                     return stat;
> >> +
> >> +             tmp = readl(&host->base->rdr);
> >> +             memcpy(b, &tmp, bytes);
> > please use __iowrite32 to speedup the copy

Ah, here is the potential __iowrite32 user ;)

> I think memcpy is alright here. Usually this code-path shouldn't be
> executed anyway, because the mci-core always
> requests multiples of 512 bytes, and here we copy only the last
> remaining _three_ bytes.

One thing to consider when using memcpy for io accesses is that it
is not specified which type of accesses are used. For example on arm,
when assembler optimized string functions are used, the code will
use 32 bit accesses when possible. If not, memcpy will be a simple
byte copy loop.
I really doubt this code works as expected. Does your hardware support
byte accesses? Might be better to just add a WARN_ON(bytes) instead
of introducing this kind of untested code.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH v2] mci: add Atmel AT91 MCI driver
  2011-06-06  7:43       ` Hubert Feurstein
  2011-06-06  9:04         ` Sascha Hauer
@ 2011-06-06 11:07         ` Jean-Christophe PLAGNIOL-VILLARD
  2011-06-06 17:40           ` [PATCH v3] " Hubert Feurstein
  1 sibling, 1 reply; 9+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2011-06-06 11:07 UTC (permalink / raw)
  To: Hubert Feurstein; +Cc: barebox

On 09:43 Mon 06 Jun     , Hubert Feurstein wrote:
> 2011/6/2 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>:
> > On 13:48 Wed 01 Jun     , Hubert Feurstein wrote:
> [snip]
> > I've test it on at91sam9263ek and work fine
> Great, good to hear ;) I'll add also support for at91sam9m10g45ek
> later in an extra commit.
> >
> > please fix the following comments
> > Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
> >
> [snip]
> >> +/* Multimedia Card Interface */
> >> +struct atmel_mci_platform_data {
> >> +     unsigned bus_width;
> >> +     unsigned host_caps; /* MCI_MODE_* from mci.h */
> >> +     unsigned detect_pin;
> >> +     unsigned wp_pin;
> >> +};
> >we can have 2 slot but you allow only one
> Would it be ok to add the second slot in an extra commit?
ok

Best Regards,
J.

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH v3] mci: add Atmel AT91 MCI driver
  2011-06-06 11:07         ` Jean-Christophe PLAGNIOL-VILLARD
@ 2011-06-06 17:40           ` Hubert Feurstein
  0 siblings, 0 replies; 9+ messages in thread
From: Hubert Feurstein @ 2011-06-06 17:40 UTC (permalink / raw)
  To: barebox; +Cc: Nicolas Ferre

The driver supports push and pull transfers.
Tested on at91sam9m10 SoC.

Signed-off-by: Hubert Feurstein <h.feurstein@gmail.com>
Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 Changes according to v2:
 - removed struct based register access
 - use WARN_ON when transfer size is not multiple of 4 bytes

 arch/arm/mach-at91/at91sam9260_devices.c |   50 +++
 arch/arm/mach-at91/at91sam9261_devices.c |   50 +++
 arch/arm/mach-at91/at91sam9263_devices.c |   78 +++++
 arch/arm/mach-at91/at91sam9g45_devices.c |   91 ++++++
 arch/arm/mach-at91/include/mach/board.h  |   10 +
 drivers/mci/Kconfig                      |    7 +
 drivers/mci/Makefile                     |    1 +
 drivers/mci/at91_mci.h                   |  121 ++++++++
 drivers/mci/atmel_mci.c                  |  494 ++++++++++++++++++++++++++++++
 9 files changed, 902 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mci/at91_mci.h
 create mode 100644 drivers/mci/atmel_mci.c

diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index fc8f828..d44e280 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -10,6 +10,7 @@
  *
  */
 #include <common.h>
+#include <sizes.h>
 #include <asm/armlinux.h>
 #include <asm/hardware.h>
 #include <mach/board.h>
@@ -280,3 +281,52 @@ void at91_register_uart(unsigned id, unsigned pins)
 			return;
 	}
 }
+
+#if defined(CONFIG_MCI_ATMEL)
+static struct device_d mci_device = {
+	.id		= -1,
+	.name		= "atmel_mci",
+	.map_base	= AT91SAM9260_BASE_MCI,
+	.size		= SZ_16K,
+};
+
+/* Consider only one slot : slot 0 */
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data)
+{
+	if (!data)
+		return;
+
+	/* need bus_width */
+	if (!data->bus_width)
+		return;
+
+	/* input/irq */
+	if (data->detect_pin) {
+		at91_set_gpio_input(data->detect_pin, 1);
+		at91_set_deglitch(data->detect_pin, 1);
+	}
+
+	if (data->wp_pin)
+		at91_set_gpio_input(data->wp_pin, 1);
+
+	/* CLK */
+	at91_set_A_periph(AT91_PIN_PA8, 0);
+
+	/* CMD */
+	at91_set_A_periph(AT91_PIN_PA7, 1);
+
+	/* DAT0, maybe DAT1..DAT3 */
+	at91_set_A_periph(AT91_PIN_PA6, 1);
+	if (data->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);
+	}
+
+	mci_device.platform_data = data;
+	at91_clock_associate("mci_clk", &mci_device, "mci_clk");
+	register_device(&mci_device);
+}
+#else
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data) {}
+#endif
diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c
index 66bf3a8..d8b70a3 100644
--- a/arch/arm/mach-at91/at91sam9261_devices.c
+++ b/arch/arm/mach-at91/at91sam9261_devices.c
@@ -10,6 +10,7 @@
  *
  */
 #include <common.h>
+#include <sizes.h>
 #include <asm/armlinux.h>
 #include <asm/hardware.h>
 #include <mach/at91_pmc.h>
@@ -173,3 +174,52 @@ void at91_register_uart(unsigned id, unsigned pins)
 			return;
 	}
 }
+
+#if defined(CONFIG_MCI_ATMEL)
+static struct device_d mci_device = {
+	.id		= -1,
+	.name		= "atmel_mci",
+	.map_base	= AT91SAM9261_BASE_MCI,
+	.size		= SZ_16K,
+};
+
+/* Consider only one slot : slot 0 */
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data)
+{
+	if (!data)
+		return;
+
+	/* need bus_width */
+	if (!data->bus_width)
+		return;
+
+	/* input/irq */
+	if (data->detect_pin) {
+		at91_set_gpio_input(data->detect_pin, 1);
+		at91_set_deglitch(data->detect_pin, 1);
+	}
+
+	if (data->wp_pin)
+		at91_set_gpio_input(data->wp_pin, 1);
+
+	/* CLK */
+	at91_set_B_periph(AT91_PIN_PA2, 0);
+
+	/* CMD */
+	at91_set_B_periph(AT91_PIN_PA1, 1);
+
+	/* DAT0, maybe DAT1..DAT3 */
+	at91_set_B_periph(AT91_PIN_PA0, 1);
+	if (data->bus_width == 4) {
+		at91_set_B_periph(AT91_PIN_PA4, 1);
+		at91_set_B_periph(AT91_PIN_PA5, 1);
+		at91_set_B_periph(AT91_PIN_PA6, 1);
+	}
+
+	mci_device.platform_data = data;
+	at91_clock_associate("mci_clk", &mci_device, "mci_clk");
+	register_device(&mci_device);
+}
+#else
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data) {}
+#endif
diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c
index 346426c..04fb79e 100644
--- a/arch/arm/mach-at91/at91sam9263_devices.c
+++ b/arch/arm/mach-at91/at91sam9263_devices.c
@@ -10,6 +10,7 @@
  *
  */
 #include <common.h>
+#include <sizes.h>
 #include <asm/armlinux.h>
 #include <asm/hardware.h>
 #include <mach/at91_pmc.h>
@@ -213,3 +214,80 @@ void at91_register_uart(unsigned id, unsigned pins)
 	}

 }
+
+#if defined(CONFIG_MCI_ATMEL)
+static struct device_d mci0_device = {
+	.id		= 0,
+	.name		= "atmel_mci",
+	.map_base	= AT91SAM9263_BASE_MCI0,
+	.size		= SZ_16K,
+};
+
+static struct device_d mci1_device = {
+	.id		= 1,
+	.name		= "atmel_mci",
+	.map_base	= AT91SAM9263_BASE_MCI1,
+	.size		= SZ_16K,
+};
+
+/* Consider only one slot : slot 0 */
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data)
+{
+	if (!data)
+		return;
+
+	/* need bus_width */
+	if (!data->bus_width)
+		return;
+
+	/* input/irq */
+	if (data->detect_pin) {
+		at91_set_gpio_input(data->detect_pin, 1);
+		at91_set_deglitch(data->detect_pin, 1);
+	}
+
+	if (data->wp_pin)
+		at91_set_gpio_input(data->wp_pin, 1);
+
+	if (mmc_id == 0) {		/* MCI0 */
+		/* CLK */
+		at91_set_A_periph(AT91_PIN_PA12, 0);
+
+		/* CMD */
+		at91_set_A_periph(AT91_PIN_PA1, 1);
+
+		/* DAT0, maybe DAT1..DAT3 and maybe DAT4..DAT7 */
+		at91_set_A_periph(AT91_PIN_PA0, 1);
+		if (data->bus_width == 4) {
+			at91_set_A_periph(AT91_PIN_PA3, 1);
+			at91_set_A_periph(AT91_PIN_PA4, 1);
+			at91_set_A_periph(AT91_PIN_PA5, 1);
+		}
+
+		mci0_device.platform_data = data;
+		at91_clock_associate("mci0_clk", &mci0_device, "mci_clk");
+		register_device(&mci0_device);
+
+	} else {			/* MCI1 */
+		/* CLK */
+		at91_set_A_periph(AT91_PIN_PA6, 0);
+
+		/* CMD */
+		at91_set_A_periph(AT91_PIN_PA7, 1);
+
+		/* DAT0, maybe DAT1..DAT3 */
+		at91_set_A_periph(AT91_PIN_PA8, 1);
+		if (data->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);
+		}
+
+		mci1_device.platform_data = data;
+		at91_clock_associate("mci1_clk", &mci1_device, "mci_clk");
+		register_device(&mci1_device);
+	}
+}
+#else
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data) {}
+#endif
diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
index ddb005a..dc01705 100644
--- a/arch/arm/mach-at91/at91sam9g45_devices.c
+++ b/arch/arm/mach-at91/at91sam9g45_devices.c
@@ -10,6 +10,7 @@
  *
  */
 #include <common.h>
+#include <sizes.h>
 #include <asm/armlinux.h>
 #include <asm/hardware.h>
 #include <mach/at91_pmc.h>
@@ -240,3 +241,93 @@ void at91_register_uart(unsigned id, unsigned pins)
 	}

 }
+
+#if defined(CONFIG_MCI_ATMEL)
+static struct device_d mci0_device = {
+	.id		= 0,
+	.name		= "atmel_mci",
+	.map_base	= AT91SAM9G45_BASE_MCI0,
+	.size		= SZ_16K,
+};
+
+static struct device_d mci1_device = {
+	.id		= 1,
+	.name		= "atmel_mci",
+	.map_base	= AT91SAM9G45_BASE_MCI1,
+	.size		= SZ_16K,
+};
+
+/* Consider only one slot : slot 0 */
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data)
+{
+	if (!data)
+		return;
+
+	/* need bus_width */
+	if (!data->bus_width)
+		return;
+
+	/* input/irq */
+	if (data->detect_pin) {
+		at91_set_gpio_input(data->detect_pin, 1);
+		at91_set_deglitch(data->detect_pin, 1);
+	}
+
+	if (data->wp_pin)
+		at91_set_gpio_input(data->wp_pin, 1);
+
+	if (mmc_id == 0) {		/* MCI0 */
+		/* CLK */
+		at91_set_A_periph(AT91_PIN_PA0, 0);
+
+		/* CMD */
+		at91_set_A_periph(AT91_PIN_PA1, 1);
+
+		/* DAT0, maybe DAT1..DAT3 and maybe DAT4..DAT7 */
+		at91_set_A_periph(AT91_PIN_PA2, 1);
+		if (data->bus_width == 4) {
+			at91_set_A_periph(AT91_PIN_PA3, 1);
+			at91_set_A_periph(AT91_PIN_PA4, 1);
+			at91_set_A_periph(AT91_PIN_PA5, 1);
+			if (data->bus_width == 8) {
+				at91_set_A_periph(AT91_PIN_PA6, 1);
+				at91_set_A_periph(AT91_PIN_PA7, 1);
+				at91_set_A_periph(AT91_PIN_PA8, 1);
+				at91_set_A_periph(AT91_PIN_PA9, 1);
+			}
+		}
+
+		mci0_device.platform_data = data;
+		at91_clock_associate("mci0_clk", &mci0_device, "mci_clk");
+		register_device(&mci0_device);
+
+	} else {			/* MCI1 */
+		/* CLK */
+		at91_set_A_periph(AT91_PIN_PA31, 0);
+
+		/* CMD */
+		at91_set_A_periph(AT91_PIN_PA22, 1);
+
+		/* DAT0, maybe DAT1..DAT3 and maybe DAT4..DAT7 */
+		at91_set_A_periph(AT91_PIN_PA23, 1);
+		if (data->bus_width == 4) {
+			at91_set_A_periph(AT91_PIN_PA24, 1);
+			at91_set_A_periph(AT91_PIN_PA25, 1);
+			at91_set_A_periph(AT91_PIN_PA26, 1);
+			if (data->bus_width == 8) {
+				at91_set_A_periph(AT91_PIN_PA27, 1);
+				at91_set_A_periph(AT91_PIN_PA28, 1);
+				at91_set_A_periph(AT91_PIN_PA29, 1);
+				at91_set_A_periph(AT91_PIN_PA30, 1);
+			}
+		}
+
+		mci1_device.platform_data = data;
+		at91_clock_associate("mci1_clk", &mci1_device, "mci_clk");
+		register_device(&mci1_device);
+	}
+}
+#else
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data) {}
+#endif
+
diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index 1ab05ad..89c1746 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -63,4 +63,14 @@ void at91_add_device_sdram(u32 size);
 #define ATMEL_UART_RI	0x20

 void at91_register_uart(unsigned id, unsigned pins);
+
+/* Multimedia Card Interface */
+struct atmel_mci_platform_data {
+	unsigned bus_width;
+	unsigned host_caps; /* MCI_MODE_* from mci.h */
+	unsigned detect_pin;
+	unsigned wp_pin;
+};
+
+void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data);
 #endif
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index 5d8adbd..7b71b99 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -73,4 +73,11 @@ config MCI_OMAP_HSMMC
 	  Enable this entry to add support to read and write SD cards on a
 	  OMAP4 based system.

+config MCI_ATMEL
+	bool "ATMEL (AT91)"
+	depends on ARCH_AT91
+	help
+	  Enable this entry to add support to read and write SD cards on a
+	  Atmel AT91.
+
 endif
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index 2bb9a93..4fc0046 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_MCI_S3C) += s3c.o
 obj-$(CONFIG_MCI_IMX) += imx.o
 obj-$(CONFIG_MCI_IMX_ESDHC) += imx-esdhc.o
 obj-$(CONFIG_MCI_OMAP_HSMMC) += omap_hsmmc.o
+obj-$(CONFIG_MCI_ATMEL) += atmel_mci.o
diff --git a/drivers/mci/at91_mci.h b/drivers/mci/at91_mci.h
new file mode 100644
index 0000000..4025aeb
--- /dev/null
+++ b/drivers/mci/at91_mci.h
@@ -0,0 +1,121 @@
+/*
+ * [origin: Linux kernel arch/arm/mach-at91/include/mach/at91_mci.h]
+ *
+ * Copyright (C) 2005 Ivan Kokshaysky
+ * Copyright (C) SAN People
+ *
+ * MultiMedia Card Interface (MCI) registers.
+ * Based on AT91RM9200 datasheet revision F.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef AT91_MCI_H
+#define AT91_MCI_H
+
+#define AT91_MCI_CR		0x00		/* Control Register */
+#define		AT91_MCI_MCIEN		(1 <<  0)	/* Multi-Media Interface Enable */
+#define		AT91_MCI_MCIDIS		(1 <<  1)	/* Multi-Media Interface Disable */
+#define		AT91_MCI_PWSEN		(1 <<  2)	/* Power Save Mode Enable */
+#define		AT91_MCI_PWSDIS		(1 <<  3)	/* Power Save Mode Disable */
+#define		AT91_MCI_SWRST		(1 <<  7)	/* Software Reset */
+
+#define AT91_MCI_MR		0x04		/* Mode Register */
+#define		AT91_MCI_CLKDIV		(0xff  <<  0)	/* Clock Divider */
+#define		AT91_MCI_PWSDIV		(7     <<  8)	/* Power Saving Divider */
+#define		AT91_MCI_RDPROOF	(1     << 11)	/* Read Proof Enable [SAM926[03] only] */
+#define		AT91_MCI_WRPROOF	(1     << 12)	/* Write Proof Enable [SAM926[03] only] */
+#define		AT91_MCI_PDCFBYTE	(1     << 13)	/* PDC Force Byte Transfer [SAM926[03] only] */
+#define		AT91_MCI_PDCPADV	(1     << 14)	/* PDC Padding Value */
+#define		AT91_MCI_PDCMODE	(1     << 15)	/* PDC-orientated Mode */
+#define		AT91_MCI_BLKLEN		(0xfff << 18)	/* Data Block Length */
+
+#define AT91_MCI_DTOR		0x08		/* Data Timeout Register */
+#define		AT91_MCI_DTOCYC		(0xf << 0)	/* Data Timeout Cycle Number */
+#define		AT91_MCI_DTOMUL		(7   << 4)	/* Data Timeout Multiplier */
+#define		AT91_MCI_DTOMUL_1		(0 <<  4)
+#define		AT91_MCI_DTOMUL_16		(1 <<  4)
+#define		AT91_MCI_DTOMUL_128		(2 <<  4)
+#define		AT91_MCI_DTOMUL_256		(3 <<  4)
+#define		AT91_MCI_DTOMUL_1K		(4 <<  4)
+#define		AT91_MCI_DTOMUL_4K		(5 <<  4)
+#define		AT91_MCI_DTOMUL_64K		(6 <<  4)
+#define		AT91_MCI_DTOMUL_1M		(7 <<  4)
+
+#define AT91_MCI_SDCR		0x0c		/* SD Card Register */
+#define		AT91_MCI_SDCSEL		(3 << 0)	/* SD Card Selector */
+#define		AT91_MCI_SDCBUS		(3 << 6)	/* 1-bit, 4-bit, or 8-bit bus */
+#define			AT91_MCI_SDCBUS_1BIT	(0 << 6)	/* 1-bit bus */
+#define			AT91_MCI_SDCBUS_4BIT	(2 << 6)	/* 4-bit bus */
+#define			AT91_MCI_SDCBUS_8BIT	(3 << 6)	/* 8-bit bus */
+
+#define AT91_MCI_ARGR		0x10		/* Argument Register */
+
+#define AT91_MCI_CMDR		0x14		/* Command Register */
+#define		AT91_MCI_CMDNB		(0x3f << 0)	/* Command Number */
+#define		AT91_MCI_RSPTYP		(3    << 6)	/* Response Type */
+#define			AT91_MCI_RSPTYP_NONE	(0 <<  6)
+#define			AT91_MCI_RSPTYP_48	(1 <<  6)
+#define			AT91_MCI_RSPTYP_136	(2 <<  6)
+#define			AT91_MCI_RSPTYP_R1B	(3 <<  6)
+#define		AT91_MCI_SPCMD		(7    << 8)	/* Special Command */
+#define			AT91_MCI_SPCMD_NONE	(0 <<  8)
+#define			AT91_MCI_SPCMD_INIT	(1 <<  8)
+#define			AT91_MCI_SPCMD_SYNC	(2 <<  8)
+#define			AT91_MCI_SPCMD_ICMD	(4 <<  8)
+#define			AT91_MCI_SPCMD_IRESP	(5 <<  8)
+#define		AT91_MCI_OPDCMD		(1 << 11)	/* Open Drain Command */
+#define		AT91_MCI_MAXLAT		(1 << 12)	/* Max Latency for Command to Response */
+#define		AT91_MCI_TRCMD		(3 << 16)	/* Transfer Command */
+#define			AT91_MCI_TRCMD_NONE	(0 << 16)
+#define			AT91_MCI_TRCMD_START	(1 << 16)
+#define			AT91_MCI_TRCMD_STOP	(2 << 16)
+#define		AT91_MCI_TRDIR		(1 << 18)	/* Transfer Direction */
+#define			AT91_MCI_TRDIR_RX	(1 << 18)	/* Read Transfer Direction */
+#define			AT91_MCI_TRDIR_TX	(0 << 18)	/* Write Transfer Direction */
+#define		AT91_MCI_TRTYP		(3 << 19)	/* Transfer Type */
+#define			AT91_MCI_TRTYP_BLOCK	(0 << 19)
+#define			AT91_MCI_TRTYP_MULTIPLE	(1 << 19)
+#define			AT91_MCI_TRTYP_STREAM	(2 << 19)
+#define			AT91_MCI_TRTYP_SDIO_BYTE	(4 << 19)
+#define			AT91_MCI_TRTYP_SDIO_BLOCK	(5 << 19)
+
+#define AT91_MCI_BLKR		0x18		/* Block Register */
+#define		AT91_MCI_BLKR_BCNT(n)	((0xffff & (n)) << 0)	/* Block count */
+#define		AT91_MCI_BLKR_BLKLEN(n)	((0xffff & (n)) << 16)	/* Block length */
+
+#define AT91_MCI_RSPR(n)	(0x20 + ((n) * 4))	/* Response Registers 0-3 */
+#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 */
+#define		AT91_MCI_RXRDY		(1 <<  1)	/* Receiver Ready */
+#define		AT91_MCI_TXRDY		(1 <<  2)	/* Transmit Ready */
+#define		AT91_MCI_BLKE		(1 <<  3)	/* Data Block Ended */
+#define		AT91_MCI_DTIP		(1 <<  4)	/* Data Transfer in Progress */
+#define		AT91_MCI_NOTBUSY	(1 <<  5)	/* Data Not Busy */
+#define		AT91_MCI_ENDRX		(1 <<  6)	/* End of RX Buffer */
+#define		AT91_MCI_ENDTX		(1 <<  7)	/* End fo TX Buffer */
+#define		AT91_MCI_SDIOIRQA	(1 <<  8)	/* SDIO Interrupt for Slot A */
+#define		AT91_MCI_SDIOIRQB	(1 <<  9)	/* SDIO Interrupt for Slot B */
+#define		AT91_MCI_RXBUFF		(1 << 14)	/* RX Buffer Full */
+#define		AT91_MCI_TXBUFE		(1 << 15)	/* TX Buffer Empty */
+#define		AT91_MCI_RINDE		(1 << 16)	/* Response Index Error */
+#define		AT91_MCI_RDIRE		(1 << 17)	/* Response Direction Error */
+#define		AT91_MCI_RCRCE		(1 << 18)	/* Response CRC Error */
+#define		AT91_MCI_RENDE		(1 << 19)	/* Response End Bit Error */
+#define		AT91_MCI_RTOE		(1 << 20)	/* Response Time-out Error */
+#define		AT91_MCI_DCRCE		(1 << 21)	/* Data CRC Error */
+#define		AT91_MCI_DTOE		(1 << 22)	/* Data Time-out Error */
+#define		AT91_MCI_OVRE		(1 << 30)	/* Overrun */
+#define		AT91_MCI_UNRE		(1 << 31)	/* Underrun */
+
+#define AT91_MCI_IER		0x44		/* Interrupt Enable Register */
+#define AT91_MCI_IDR		0x48		/* Interrupt Disable Register */
+#define AT91_MCI_IMR		0x4c		/* Interrupt Mask Register */
+
+#endif
diff --git a/drivers/mci/atmel_mci.c b/drivers/mci/atmel_mci.c
new file mode 100644
index 0000000..980aa24
--- /dev/null
+++ b/drivers/mci/atmel_mci.c
@@ -0,0 +1,494 @@
+/*
+ * Atmel AT91 MCI driver
+ *
+ * Copyright (C) 2011 Hubert Feurstein <h.feurstein@gmail.com>
+ *
+ * based on imx.c by:
+ * Copyright (C) 2009 Ilya Yanok, <yanok@emcraft.com>
+ * Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com>
+ *
+ * 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 <common.h>
+#include <init.h>
+#include <mci.h>
+#include <errno.h>
+#include <clock.h>
+#include <gpio.h>
+#include <asm/io.h>
+#include <mach/board.h>
+#include <linux/clk.h>
+
+#include "at91_mci.h"
+
+struct atmel_mci_host {
+	struct mci_host		mci;
+	void  __iomem		*base;
+	struct device_d		*hw_dev;
+	struct clk		*clk;
+
+	u32			datasize;
+	struct mci_cmd		*cmd;
+	struct mci_data		*data;
+};
+
+#define to_mci_host(mci)	container_of(mci, struct atmel_mci_host, mci)
+
+#define STATUS_ERROR_MASK	(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)
+
+static inline u32 atmel_mci_readl(struct atmel_mci_host *host, u32 offset)
+{
+	return readl(host->base + offset);
+}
+
+static inline void atmel_mci_writel(struct atmel_mci_host *host, u32 offset,
+				    u32 value)
+{
+	writel(value, host->base + offset);
+}
+
+static void atmel_mci_reset(struct atmel_mci_host *host)
+{
+	atmel_mci_writel(host, AT91_MCI_CR, AT91_MCI_SWRST | AT91_MCI_MCIDIS);
+	atmel_mci_writel(host, AT91_MCI_DTOR, 0x7f);
+	atmel_mci_writel(host, AT91_MCI_IDR, ~0UL);
+}
+
+static void atmel_set_clk_rate(struct atmel_mci_host *host,
+			       unsigned int clk_ios)
+{
+	unsigned int divider;
+	unsigned int clk_in = clk_get_rate(host->clk);
+
+	if (clk_ios > 0) {
+		divider = (clk_in / clk_ios) / 2;
+		if (divider > 0)
+			divider -= 1;
+	}
+
+	if (clk_ios == 0 || divider > 255)
+		divider = 255;
+
+	dev_dbg(host->hw_dev, "atmel_set_clk_rate: clkIn=%d clkIos=%d divider=%d\n",
+		clk_in, clk_ios, divider);
+
+	atmel_mci_writel(host, AT91_MCI_MR, (AT91_MCI_CLKDIV & divider)
+		| AT91_MCI_RDPROOF | AT91_MCI_WRPROOF);
+}
+
+static int atmel_poll_status(struct atmel_mci_host *host, u32 mask)
+{
+	u32 stat;
+	uint64_t start = get_time_ns();
+
+	do {
+		stat = atmel_mci_readl(host, AT91_MCI_SR);
+		if (stat & STATUS_ERROR_MASK)
+			return stat;
+		if (is_timeout(start, SECOND)) {
+			dev_err(host->hw_dev, "timeout\n");
+			return AT91_MCI_RTOE | stat;
+		}
+		if (stat & mask)
+			return 0;
+	} while (1);
+}
+
+static int atmel_pull(struct atmel_mci_host *host, void *_buf, int bytes)
+{
+	unsigned int stat;
+	u32 *buf = _buf;
+
+	while (bytes > 3) {
+		stat = atmel_poll_status(host, AT91_MCI_RXRDY);
+		if (stat)
+			return stat;
+
+		*buf++ = atmel_mci_readl(host, AT91_MCI_RDR);
+		bytes -= 4;
+	}
+
+	if (WARN_ON(bytes))
+		return -EIO;
+
+	return 0;
+}
+
+#ifdef CONFIG_MCI_WRITE
+static int atmel_push(struct atmel_mci_host *host, const void *_buf, int bytes)
+{
+	unsigned int stat;
+	const u32 *buf = _buf;
+
+	while (bytes > 3) {
+		stat = atmel_poll_status(host, AT91_MCI_TXRDY);
+		if (stat)
+			return stat;
+
+		atmel_mci_writel(host, AT91_MCI_TDR, *buf++);
+		bytes -= 4;
+	}
+
+	stat = atmel_poll_status(host, AT91_MCI_TXRDY);
+	if (stat)
+		return stat;
+
+	if (WARN_ON(bytes))
+		return -EIO;
+
+	return 0;
+}
+#endif /* CONFIG_MCI_WRITE */
+
+static int atmel_transfer_data(struct atmel_mci_host *host)
+{
+	struct mci_data *data = host->data;
+	int stat;
+	unsigned long length;
+
+	length = data->blocks * data->blocksize;
+	host->datasize = 0;
+
+	if (data->flags & MMC_DATA_READ) {
+		stat = atmel_pull(host, data->dest, length);
+		if (stat)
+			return stat;
+
+		stat = atmel_poll_status(host, AT91_MCI_NOTBUSY);
+		if (stat)
+			return stat;
+
+		host->datasize += length;
+	} else {
+#ifdef CONFIG_MCI_WRITE
+		stat = atmel_push(host, (const void *)(data->src), length);
+		if (stat)
+			return stat;
+
+		host->datasize += length;
+		stat = atmel_poll_status(host, AT91_MCI_NOTBUSY);
+		if (stat)
+			return stat;
+#endif /* CONFIG_MCI_WRITE */
+	}
+	return 0;
+}
+
+static void atmel_finish_request(struct atmel_mci_host *host)
+{
+	host->cmd = NULL;
+	host->data = NULL;
+}
+
+static int atmel_finish_data(struct atmel_mci_host *host, unsigned int stat)
+{
+	int data_error = 0;
+
+	if (stat & STATUS_ERROR_MASK) {
+		dev_err(host->hw_dev, "request failed (status=0x%08x)\n", stat);
+		if (stat & AT91_MCI_DCRCE)
+			data_error = -EILSEQ;
+		else if (stat & (AT91_MCI_RTOE | AT91_MCI_DTOE))
+			data_error = -ETIMEDOUT;
+		else
+			data_error = -EIO;
+	}
+
+	host->data = NULL;
+
+	return data_error;
+}
+
+static void atmel_setup_data(struct atmel_mci_host *host, struct mci_data *data)
+{
+	unsigned int nob = data->blocks;
+	unsigned int blksz = data->blocksize;
+	unsigned int datasize = nob * blksz;
+
+	BUG_ON(data->blocksize & 3);
+	BUG_ON(nob == 0);
+
+	host->data = data;
+
+	dev_dbg(host->hw_dev, "atmel_setup_data: nob=%d blksz=%d\n",
+		nob, blksz);
+
+	atmel_mci_writel(host, AT91_MCI_BLKR, AT91_MCI_BLKR_BCNT(nob)
+		| AT91_MCI_BLKR_BLKLEN(blksz));
+
+	host->datasize = datasize;
+}
+
+static int atmel_read_response(struct atmel_mci_host *host, unsigned int stat)
+{
+	struct mci_cmd *cmd = host->cmd;
+	int i;
+	u32 *resp = (u32 *)cmd->response;
+
+	if (!cmd)
+		return 0;
+
+	if (stat & (AT91_MCI_RTOE | AT91_MCI_DTOE)) {
+		dev_err(host->hw_dev, "command/data timeout\n");
+		return -ETIMEDOUT;
+	} else if ((stat & AT91_MCI_RCRCE) && (cmd->resp_type & MMC_RSP_CRC)) {
+		dev_err(host->hw_dev, "cmd crc error\n");
+		return -EILSEQ;
+	}
+
+	if (cmd->resp_type & MMC_RSP_PRESENT) {
+		if (cmd->resp_type & MMC_RSP_136) {
+			for (i = 0; i < 4; i++)
+				resp[i] = atmel_mci_readl(host, AT91_MCI_RSPR(0));
+		} else {
+			resp[0] = atmel_mci_readl(host, AT91_MCI_RSPR(0));
+		}
+	}
+
+	return 0;
+}
+
+static int atmel_cmd_done(struct atmel_mci_host *host, unsigned int stat)
+{
+	int datastat;
+	int ret;
+
+	ret = atmel_read_response(host, stat);
+
+	if (ret) {
+		atmel_finish_request(host);
+		return ret;
+	}
+
+	if (!host->data) {
+		atmel_finish_request(host);
+		return 0;
+	}
+
+	datastat = atmel_transfer_data(host);
+	ret = atmel_finish_data(host, datastat);
+	atmel_finish_request(host);
+	return ret;
+}
+
+static int atmel_start_cmd(struct atmel_mci_host *host, struct mci_cmd *cmd,
+			   unsigned int cmdat)
+{
+	unsigned flags = 0;
+	unsigned cmdval = 0;
+
+	if (host->cmd != NULL)
+		dev_err(host->hw_dev, "error!\n");
+
+	if ((atmel_mci_readl(host, AT91_MCI_SR) & AT91_MCI_CMDRDY) == 0) {
+		dev_err(host->hw_dev, "mci not ready!\n");
+		return -EBUSY;
+	}
+
+	host->cmd = cmd;
+	cmdval = AT91_MCI_CMDNB & cmd->cmdidx;
+
+	switch (cmd->resp_type) {
+	case MMC_RSP_R1: /* short CRC, OPCODE */
+	case MMC_RSP_R1b:/* short CRC, OPCODE, BUSY */
+		flags |= AT91_MCI_RSPTYP_48;
+		break;
+	case MMC_RSP_R2: /* long 136 bit + CRC */
+		flags |= AT91_MCI_RSPTYP_136;
+		break;
+	case MMC_RSP_R3: /* short */
+		flags |= AT91_MCI_RSPTYP_48;
+		break;
+	case MMC_RSP_NONE:
+		flags |= AT91_MCI_RSPTYP_NONE;
+		break;
+	default:
+		dev_err(host->hw_dev, "unhandled response type 0x%x\n",
+				cmd->resp_type);
+		return -EINVAL;
+	}
+	cmdval |= AT91_MCI_RSPTYP & flags;
+	cmdval |= cmdat & ~(AT91_MCI_CMDNB | AT91_MCI_RSPTYP);
+
+	atmel_mci_writel(host, AT91_MCI_ARGR, cmd->cmdarg);
+	atmel_mci_writel(host, AT91_MCI_CMDR, cmdval);
+
+	return 0;
+}
+
+/** init the host interface */
+static int mci_reset(struct mci_host *mci, struct device_d *mci_dev)
+{
+	int ret;
+	struct atmel_mci_host *host = to_mci_host(mci);
+	struct atmel_mci_platform_data *pd = host->hw_dev->platform_data;
+
+	ret = gpio_get_value(pd->detect_pin);
+	dev_dbg(host->hw_dev, "card %sdetected\n", ret != 0 ? "not " : "");
+
+	if (pd->detect_pin && ret == 1)
+		return -ENODEV;
+
+	clk_enable(host->clk);
+	atmel_mci_reset(host);
+
+	return 0;
+}
+
+/** change host interface settings */
+static void mci_set_ios(struct mci_host *mci, struct device_d *mci_dev,
+			unsigned bus_width, unsigned clock)
+{
+	struct atmel_mci_host *host = to_mci_host(mci);
+
+	dev_dbg(host->hw_dev, "atmel_mci_set_ios: bus_width=%d clk=%d\n",
+		bus_width, clock);
+
+	switch (bus_width) {
+	case 4:
+		atmel_mci_writel(host, AT91_MCI_SDCR, AT91_MCI_SDCBUS_4BIT);
+		break;
+	case 8:
+		atmel_mci_writel(host, AT91_MCI_SDCR, AT91_MCI_SDCBUS_8BIT);
+		break;
+	default:
+		atmel_mci_writel(host, AT91_MCI_SDCR, AT91_MCI_SDCBUS_1BIT);
+		break;
+	}
+
+	if (clock) {
+		atmel_set_clk_rate(host, clock);
+		atmel_mci_writel(host, AT91_MCI_CR, AT91_MCI_MCIEN
+		);
+	} else {
+		atmel_mci_writel(host, AT91_MCI_CR, AT91_MCI_MCIDIS);
+	}
+
+	return;
+}
+
+/** handle a command */
+static int mci_request(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
+{
+	struct atmel_mci_host *host = to_mci_host(mci);
+	u32 stat, cmdat = 0;
+	int ret;
+
+	if (cmd->resp_type != MMC_RSP_NONE)
+		cmdat |= AT91_MCI_MAXLAT;
+
+	if (data) {
+		atmel_setup_data(host, data);
+
+		cmdat |= AT91_MCI_TRCMD_START | AT91_MCI_TRTYP_MULTIPLE;
+
+		if (data->flags & MMC_DATA_READ)
+			cmdat |= AT91_MCI_TRDIR_RX;
+	}
+
+	ret = atmel_start_cmd(host, cmd, cmdat);
+	if (ret) {
+		atmel_finish_request(host);
+		return ret;
+	}
+
+	stat = atmel_poll_status(host, AT91_MCI_CMDRDY);
+	return atmel_cmd_done(host, stat);
+}
+
+#ifdef CONFIG_MCI_INFO
+static void mci_info(struct device_d *mci_dev)
+{
+	struct atmel_mci_host *host = mci_dev->priv;
+	struct atmel_mci_platform_data *pd = host->hw_dev->platform_data;
+
+	printf("  Bus data width: %d bit\n", host->mci.bus_width);
+
+	printf("  Bus frequency: %u Hz\n", host->mci.clock);
+	printf("  Frequency limits: ");
+	if (host->mci.f_min == 0)
+		printf("no lower limit ");
+	else
+		printf("%u Hz lower limit ", host->mci.f_min);
+	if (host->mci.f_max == 0)
+		printf("- no upper limit");
+	else
+		printf("- %u Hz upper limit", host->mci.f_max);
+
+	printf("\n  Card detection support: %s\n",
+		pd->detect_pin != 0 ? "yes" : "no");
+
+}
+#endif /* CONFIG_MCI_INFO */
+
+static int mci_probe(struct device_d *hw_dev)
+{
+	unsigned long clk_rate;
+	struct atmel_mci_host *host;
+	struct atmel_mci_platform_data *pd = hw_dev->platform_data;
+
+	if (!pd) {
+		dev_err(hw_dev, "missing platform data\n");
+		return -EINVAL;
+	}
+
+	host = xzalloc(sizeof(*host));
+	host->mci.send_cmd = mci_request;
+	host->mci.set_ios = mci_set_ios;
+	host->mci.init = mci_reset;
+
+	host->mci.host_caps = pd->host_caps;
+	if (pd->bus_width == 4)
+		host->mci.host_caps |= MMC_MODE_4BIT;
+	else if (pd->bus_width == 8)
+		host->mci.host_caps |= MMC_MODE_8BIT;
+
+	host->base = (struct atmel_mci_regs *)hw_dev->map_base;
+	host->hw_dev = hw_dev;
+	hw_dev->priv = host;
+	host->clk = clk_get(hw_dev, "mci_clk");
+	if (host->clk == NULL) {
+		dev_err(hw_dev, "no mci_clk\n");
+		return -EINVAL;
+	}
+
+	clk_rate = clk_get_rate(host->clk);
+
+	host->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+	host->mci.f_min = clk_rate >> 9;
+	host->mci.f_max = clk_rate >> 1;
+
+	mci_register(&host->mci);
+
+	return 0;
+}
+
+static struct driver_d atmel_mci_driver = {
+	.name	= "atmel_mci",
+	.probe	= mci_probe,
+#ifdef CONFIG_MCI_INFO
+	.info	= mci_info,
+#endif
+};
+
+static int atmel_mci_init_driver(void)
+{
+	register_driver(&atmel_mci_driver);
+	return 0;
+}
+
+device_initcall(atmel_mci_init_driver);
--
1.7.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

end of thread, other threads:[~2011-06-06 17:40 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-05-31 15:02 [PATCH] mci: add Atmel AT91 MCI driver Hubert Feurstein
2011-06-01  5:45 ` Jean-Christophe PLAGNIOL-VILLARD
2011-06-01 11:48   ` [PATCH v2] " Hubert Feurstein
2011-06-02 16:04     ` Jean-Christophe PLAGNIOL-VILLARD
2011-06-06  7:43       ` Hubert Feurstein
2011-06-06  9:04         ` Sascha Hauer
2011-06-06 11:07         ` Jean-Christophe PLAGNIOL-VILLARD
2011-06-06 17:40           ` [PATCH v3] " Hubert Feurstein
2011-06-02 16:05     ` [PATCH] at91sam9263ek: add mci1 support Jean-Christophe PLAGNIOL-VILLARD

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox