Commit ef7a4567 authored by Linus Torvalds's avatar Linus Torvalds

Merge master.kernel.org:/home/rmk/linux-2.6-mmc

* master.kernel.org:/home/rmk/linux-2.6-mmc:
  [ARM] 3457/1: i.MX: SD/MMC support for i.MX/MX1
  [ARM] 3456/1: AT91RM9200 support for 2.6 (MMC/SD driver)
parents a2308b7f 56ca9040
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <asm/arch/imx-regs.h> #include <asm/arch/imx-regs.h>
#include <asm/mach/map.h> #include <asm/mach/map.h>
#include <asm/arch/mmc.h>
void imx_gpio_mode(int gpio_mode) void imx_gpio_mode(int gpio_mode)
{ {
...@@ -175,13 +176,25 @@ static struct resource imx_mmc_resources[] = { ...@@ -175,13 +176,25 @@ static struct resource imx_mmc_resources[] = {
}, },
}; };
static u64 imxmmmc_dmamask = 0xffffffffUL;
static struct platform_device imx_mmc_device = { static struct platform_device imx_mmc_device = {
.name = "imx-mmc", .name = "imx-mmc",
.id = 0, .id = 0,
.dev = {
.dma_mask = &imxmmmc_dmamask,
.coherent_dma_mask = 0xffffffff,
},
.num_resources = ARRAY_SIZE(imx_mmc_resources), .num_resources = ARRAY_SIZE(imx_mmc_resources),
.resource = imx_mmc_resources, .resource = imx_mmc_resources,
}; };
void __init imx_set_mmc_info(struct imxmmc_platform_data *info)
{
imx_mmc_device.dev.platform_data = info;
}
EXPORT_SYMBOL(imx_set_mmc_info);
static struct resource imx_uart1_resources[] = { static struct resource imx_uart1_resources[] = {
[0] = { [0] = {
.start = 0x00206000, .start = 0x00206000,
......
...@@ -91,4 +91,22 @@ config MMC_AU1X ...@@ -91,4 +91,22 @@ config MMC_AU1X
If unsure, say N. If unsure, say N.
config MMC_AT91RM9200
tristate "AT91RM9200 SD/MMC Card Interface support"
depends on ARCH_AT91RM9200 && MMC
help
This selects the AT91RM9200 MCI controller.
If unsure, say N.
config MMC_IMX
tristate "Motorola i.MX Multimedia Card Interface support"
depends on ARCH_IMX && MMC
help
This selects the Motorola i.MX Multimedia card Interface.
If you have a i.MX platform with a Multimedia Card slot,
say Y or M here.
If unsure, say N.
endmenu endmenu
...@@ -17,10 +17,12 @@ obj-$(CONFIG_MMC_BLOCK) += mmc_block.o ...@@ -17,10 +17,12 @@ obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
# #
obj-$(CONFIG_MMC_ARMMMCI) += mmci.o obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
obj-$(CONFIG_MMC_PXA) += pxamci.o obj-$(CONFIG_MMC_PXA) += pxamci.o
obj-$(CONFIG_MMC_IMX) += imxmmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o obj-$(CONFIG_MMC_WBSD) += wbsd.o
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
obj-$(CONFIG_MMC_OMAP) += omap.o obj-$(CONFIG_MMC_OMAP) += omap.o
obj-$(CONFIG_MMC_AT91RM9200) += at91_mci.o
mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o
......
/*
* linux/drivers/mmc/at91_mci.c - ATMEL AT91RM9200 MCI Driver
*
* Copyright (C) 2005 Cougar Creek Computing Devices Ltd, All Rights Reserved
*
* Copyright (C) 2006 Malcolm Noyes
*
* 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.
*/
/*
This is the AT91RM9200 MCI driver that has been tested with both MMC cards
and SD-cards. Boards that support write protect are now supported.
The CCAT91SBC001 board does not support SD cards.
The three entry points are at91_mci_request, at91_mci_set_ios
and at91_mci_get_ro.
SET IOS
This configures the device to put it into the correct mode and clock speed
required.
MCI REQUEST
MCI request processes the commands sent in the mmc_request structure. This
can consist of a processing command and a stop command in the case of
multiple block transfers.
There are three main types of request, commands, reads and writes.
Commands are straight forward. The command is submitted to the controller and
the request function returns. When the controller generates an interrupt to indicate
the command is finished, the response to the command are read and the mmc_request_done
function called to end the request.
Reads and writes work in a similar manner to normal commands but involve the PDC (DMA)
controller to manage the transfers.
A read is done from the controller directly to the scatterlist passed in from the request.
Due to a bug in the controller, when a read is completed, all the words are byte
swapped in the scatterlist buffers.
The sequence of read interrupts is: ENDRX, RXBUFF, CMDRDY
A write is slightly different in that the bytes to write are read from the scatterlist
into a dma memory buffer (this is in case the source buffer should be read only). The
entire write buffer is then done from this single dma memory buffer.
The sequence of write interrupts is: ENDTX, TXBUFE, NOTBUSY, CMDRDY
GET RO
Gets the status of the write protect pin, if available.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/mach/mmc.h>
#include <asm/arch/board.h>
#include <asm/arch/gpio.h>
#include <asm/arch/at91rm9200_mci.h>
#include <asm/arch/at91rm9200_pdc.h>
#define DRIVER_NAME "at91_mci"
#undef SUPPORT_4WIRE
#ifdef CONFIG_MMC_DEBUG
#define DBG(fmt...) \
printk(fmt)
#else
#define DBG(fmt...) do { } while (0)
#endif
static struct clk *mci_clk;
#define FL_SENT_COMMAND (1 << 0)
#define FL_SENT_STOP (1 << 1)
/*
* Read from a MCI register.
*/
static inline unsigned long at91_mci_read(unsigned int reg)
{
void __iomem *mci_base = (void __iomem *)AT91_VA_BASE_MCI;
return __raw_readl(mci_base + reg);
}
/*
* Write to a MCI register.
*/
static inline void at91_mci_write(unsigned int reg, unsigned long value)
{
void __iomem *mci_base = (void __iomem *)AT91_VA_BASE_MCI;
__raw_writel(value, mci_base + reg);
}
/*
* Low level type for this driver
*/
struct at91mci_host
{
struct mmc_host *mmc;
struct mmc_command *cmd;
struct mmc_request *request;
struct at91_mmc_data *board;
int present;
/*
* Flag indicating when the command has been sent. This is used to
* work out whether or not to send the stop
*/
unsigned int flags;
/* flag for current bus settings */
u32 bus_mode;
/* DMA buffer used for transmitting */
unsigned int* buffer;
dma_addr_t physical_address;
unsigned int total_length;
/* Latest in the scatterlist that has been enabled for transfer, but not freed */
int in_use_index;
/* Latest in the scatterlist that has been enabled for transfer */
int transfer_index;
};
/*
* Copy from sg to a dma block - used for transfers
*/
static inline void at91mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data)
{
unsigned int len, i, size;
unsigned *dmabuf = host->buffer;
size = host->total_length;
len = data->sg_len;
/*
* Just loop through all entries. Size might not
* be the entire list though so make sure that
* we do not transfer too much.
*/
for (i = 0; i < len; i++) {
struct scatterlist *sg;
int amount;
int index;
unsigned int *sgbuffer;
sg = &data->sg[i];
sgbuffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset;
amount = min(size, sg->length);
size -= amount;
amount /= 4;
for (index = 0; index < amount; index++)
*dmabuf++ = swab32(sgbuffer[index]);
kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ);
if (size == 0)
break;
}
/*
* Check that we didn't get a request to transfer
* more data than can fit into the SG list.
*/
BUG_ON(size != 0);
}
/*
* Prepare a dma read
*/
static void at91mci_pre_dma_read(struct at91mci_host *host)
{
int i;
struct scatterlist *sg;
struct mmc_command *cmd;
struct mmc_data *data;
DBG("pre dma read\n");
cmd = host->cmd;
if (!cmd) {
DBG("no command\n");
return;
}
data = cmd->data;
if (!data) {
DBG("no data\n");
return;
}
for (i = 0; i < 2; i++) {
/* nothing left to transfer */
if (host->transfer_index >= data->sg_len) {
DBG("Nothing left to transfer (index = %d)\n", host->transfer_index);
break;
}
/* Check to see if this needs filling */
if (i == 0) {
if (at91_mci_read(AT91_PDC_RCR) != 0) {
DBG("Transfer active in current\n");
continue;
}
}
else {
if (at91_mci_read(AT91_PDC_RNCR) != 0) {
DBG("Transfer active in next\n");
continue;
}
}
/* Setup the next transfer */
DBG("Using transfer index %d\n", host->transfer_index);
sg = &data->sg[host->transfer_index++];
DBG("sg = %p\n", sg);
sg->dma_address = dma_map_page(NULL, sg->page, sg->offset, sg->length, DMA_FROM_DEVICE);
DBG("dma address = %08X, length = %d\n", sg->dma_address, sg->length);
if (i == 0) {
at91_mci_write(AT91_PDC_RPR, sg->dma_address);
at91_mci_write(AT91_PDC_RCR, sg->length / 4);
}
else {
at91_mci_write(AT91_PDC_RNPR, sg->dma_address);
at91_mci_write(AT91_PDC_RNCR, sg->length / 4);
}
}
DBG("pre dma read done\n");
}
/*
* Handle after a dma read
*/
static void at91mci_post_dma_read(struct at91mci_host *host)
{
struct mmc_command *cmd;
struct mmc_data *data;
DBG("post dma read\n");
cmd = host->cmd;
if (!cmd) {
DBG("no command\n");
return;
}
data = cmd->data;
if (!data) {
DBG("no data\n");
return;
}
while (host->in_use_index < host->transfer_index) {
unsigned int *buffer;
int index;
int len;
struct scatterlist *sg;
DBG("finishing index %d\n", host->in_use_index);
sg = &data->sg[host->in_use_index++];
DBG("Unmapping page %08X\n", sg->dma_address);
dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE);
/* Swap the contents of the buffer */
buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset;
DBG("buffer = %p, length = %d\n", buffer, sg->length);
data->bytes_xfered += sg->length;
len = sg->length / 4;
for (index = 0; index < len; index++) {
buffer[index] = swab32(buffer[index]);
}
kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
flush_dcache_page(sg->page);
}
/* Is there another transfer to trigger? */
if (host->transfer_index < data->sg_len)
at91mci_pre_dma_read(host);
else {
at91_mci_write(AT91_MCI_IER, AT91_MCI_RXBUFF);
at91_mci_write(AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS);
}
DBG("post dma read done\n");
}
/*
* Handle transmitted data
*/
static void at91_mci_handle_transmitted(struct at91mci_host *host)
{
struct mmc_command *cmd;
struct mmc_data *data;
DBG("Handling the transmit\n");
/* Disable the transfer */
at91_mci_write(AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS);
/* Now wait for cmd ready */
at91_mci_write(AT91_MCI_IDR, AT91_MCI_TXBUFE);
at91_mci_write(AT91_MCI_IER, AT91_MCI_NOTBUSY);
cmd = host->cmd;
if (!cmd) return;
data = cmd->data;
if (!data) return;
data->bytes_xfered = host->total_length;
}
/*
* Enable the controller
*/
static void at91_mci_enable(void)
{
at91_mci_write(AT91_MCI_CR, AT91_MCI_MCIEN);
at91_mci_write(AT91_MCI_IDR, 0xFFFFFFFF);
at91_mci_write(AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC);
at91_mci_write(AT91_MCI_MR, 0x834A);
at91_mci_write(AT91_MCI_SDCR, 0x0);
}
/*
* Disable the controller
*/
static void at91_mci_disable(void)
{
at91_mci_write(AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST);
}
/*
* Send a command
* return the interrupts to enable
*/
static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd)
{
unsigned int cmdr, mr;
unsigned int block_length;
struct mmc_data *data = cmd->data;
unsigned int blocks;
unsigned int ier = 0;
host->cmd = cmd;
/* Not sure if this is needed */
#if 0
if ((at91_mci_read(AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) {
DBG("Clearing timeout\n");
at91_mci_write(AT91_MCI_ARGR, 0);
at91_mci_write(AT91_MCI_CMDR, AT91_MCI_OPDCMD);
while (!(at91_mci_read(AT91_MCI_SR) & AT91_MCI_CMDRDY)) {
/* spin */
DBG("Clearing: SR = %08X\n", at91_mci_read(AT91_MCI_SR));
}
}
#endif
cmdr = cmd->opcode;
if (mmc_resp_type(cmd) == MMC_RSP_NONE)
cmdr |= AT91_MCI_RSPTYP_NONE;
else {
/* if a response is expected then allow maximum response latancy */
cmdr |= AT91_MCI_MAXLAT;
/* set 136 bit response for R2, 48 bit response otherwise */
if (mmc_resp_type(cmd) == MMC_RSP_R2)
cmdr |= AT91_MCI_RSPTYP_136;
else
cmdr |= AT91_MCI_RSPTYP_48;
}
if (data) {
block_length = 1 << data->blksz_bits;
blocks = data->blocks;
/* always set data start - also set direction flag for read */
if (data->flags & MMC_DATA_READ)
cmdr |= (AT91_MCI_TRDIR | AT91_MCI_TRCMD_START);
else if (data->flags & MMC_DATA_WRITE)
cmdr |= AT91_MCI_TRCMD_START;
if (data->flags & MMC_DATA_STREAM)
cmdr |= AT91_MCI_TRTYP_STREAM;
if (data->flags & MMC_DATA_MULTI)
cmdr |= AT91_MCI_TRTYP_MULTIPLE;
}
else {
block_length = 0;
blocks = 0;
}
if (cmd->opcode == MMC_STOP_TRANSMISSION)
cmdr |= AT91_MCI_TRCMD_STOP;
if (host->bus_mode == MMC_BUSMODE_OPENDRAIN)
cmdr |= AT91_MCI_OPDCMD;
/*
* Set the arguments and send the command
*/
DBG("Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08lX)\n",
cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(AT91_MCI_MR));
if (!data) {
at91_mci_write(AT91_PDC_PTCR, AT91_PDC_TXTDIS | AT91_PDC_RXTDIS);
at91_mci_write(AT91_PDC_RPR, 0);
at91_mci_write(AT91_PDC_RCR, 0);
at91_mci_write(AT91_PDC_RNPR, 0);
at91_mci_write(AT91_PDC_RNCR, 0);
at91_mci_write(AT91_PDC_TPR, 0);
at91_mci_write(AT91_PDC_TCR, 0);
at91_mci_write(AT91_PDC_TNPR, 0);
at91_mci_write(AT91_PDC_TNCR, 0);
at91_mci_write(AT91_MCI_ARGR, cmd->arg);
at91_mci_write(AT91_MCI_CMDR, cmdr);
return AT91_MCI_CMDRDY;
}
mr = at91_mci_read(AT91_MCI_MR) & 0x7fff; /* zero block length and PDC mode */
at91_mci_write(AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE);
/*
* Disable the PDC controller
*/
at91_mci_write(AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS);
if (cmdr & AT91_MCI_TRCMD_START) {
data->bytes_xfered = 0;
host->transfer_index = 0;
host->in_use_index = 0;
if (cmdr & AT91_MCI_TRDIR) {
/*
* Handle a read
*/
host->buffer = NULL;
host->total_length = 0;
at91mci_pre_dma_read(host);
ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */;
}
else {
/*
* Handle a write
*/
host->total_length = block_length * blocks;
host->buffer = dma_alloc_coherent(NULL,
host->total_length,
&host->physical_address, GFP_KERNEL);
at91mci_sg_to_dma(host, data);
DBG("Transmitting %d bytes\n", host->total_length);
at91_mci_write(AT91_PDC_TPR, host->physical_address);
at91_mci_write(AT91_PDC_TCR, host->total_length / 4);
ier = AT91_MCI_TXBUFE;
}
}
/*
* Send the command and then enable the PDC - not the other way round as
* the data sheet says
*/
at91_mci_write(AT91_MCI_ARGR, cmd->arg);
at91_mci_write(AT91_MCI_CMDR, cmdr);
if (cmdr & AT91_MCI_TRCMD_START) {
if (cmdr & AT91_MCI_TRDIR)
at91_mci_write(AT91_PDC_PTCR, AT91_PDC_RXTEN);
else
at91_mci_write(AT91_PDC_PTCR, AT91_PDC_TXTEN);
}
return ier;
}
/*
* Wait for a command to complete
*/
static void at91mci_process_command(struct at91mci_host *host, struct mmc_command *cmd)
{
unsigned int ier;
ier = at91_mci_send_command(host, cmd);
DBG("setting ier to %08X\n", ier);
/* Stop on errors or the required value */
at91_mci_write(AT91_MCI_IER, 0xffff0000 | ier);
}
/*
* Process the next step in the request
*/
static void at91mci_process_next(struct at91mci_host *host)
{
if (!(host->flags & FL_SENT_COMMAND)) {
host->flags |= FL_SENT_COMMAND;
at91mci_process_command(host, host->request->cmd);
}
else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) {
host->flags |= FL_SENT_STOP;
at91mci_process_command(host, host->request->stop);
}
else
mmc_request_done(host->mmc, host->request);
}
/*
* Handle a command that has been completed
*/
static void at91mci_completed_command(struct at91mci_host *host)
{
struct mmc_command *cmd = host->cmd;
unsigned int status;
at91_mci_write(AT91_MCI_IDR, 0xffffffff);
cmd->resp[0] = at91_mci_read(AT91_MCI_RSPR(0));
cmd->resp[1] = at91_mci_read(AT91_MCI_RSPR(1));
cmd->resp[2] = at91_mci_read(AT91_MCI_RSPR(2));
cmd->resp[3] = at91_mci_read(AT91_MCI_RSPR(3));
if (host->buffer) {
dma_free_coherent(NULL, host->total_length, host->buffer, host->physical_address);
host->buffer = NULL;
}
status = at91_mci_read(AT91_MCI_SR);
DBG("Status = %08X [%08X %08X %08X %08X]\n",
status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
if (status & (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)) {
if ((status & AT91_MCI_RCRCE) &&
((cmd->opcode == MMC_SEND_OP_COND) || (cmd->opcode == SD_APP_OP_COND))) {
cmd->error = MMC_ERR_NONE;
}
else {
if (status & (AT91_MCI_RTOE | AT91_MCI_DTOE))
cmd->error = MMC_ERR_TIMEOUT;
else if (status & (AT91_MCI_RCRCE | AT91_MCI_DCRCE))
cmd->error = MMC_ERR_BADCRC;
else if (status & (AT91_MCI_OVRE | AT91_MCI_UNRE))
cmd->error = MMC_ERR_FIFO;
else
cmd->error = MMC_ERR_FAILED;
DBG("Error detected and set to %d (cmd = %d, retries = %d)\n",
cmd->error, cmd->opcode, cmd->retries);
}
}
else
cmd->error = MMC_ERR_NONE;
at91mci_process_next(host);
}
/*
* Handle an MMC request
*/
static void at91_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct at91mci_host *host = mmc_priv(mmc);
host->request = mrq;
host->flags = 0;
at91mci_process_next(host);
}
/*
* Set the IOS
*/
static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
int clkdiv;
struct at91mci_host *host = mmc_priv(mmc);
unsigned long at91_master_clock = clk_get_rate(mci_clk);
DBG("Clock %uHz, busmode %u, powermode %u, Vdd %u\n",
ios->clock, ios->bus_mode, ios->power_mode, ios->vdd);
if (host)
host->bus_mode = ios->bus_mode;
else
printk("MMC: No host for bus_mode\n");
if (ios->clock == 0) {
/* Disable the MCI controller */
at91_mci_write(AT91_MCI_CR, AT91_MCI_MCIDIS);
clkdiv = 0;
}
else {
/* Enable the MCI controller */
at91_mci_write(AT91_MCI_CR, AT91_MCI_MCIEN);
if ((at91_master_clock % (ios->clock * 2)) == 0)
clkdiv = ((at91_master_clock / ios->clock) / 2) - 1;
else
clkdiv = (at91_master_clock / ios->clock) / 2;
DBG("clkdiv = %d. mcck = %ld\n", clkdiv,
at91_master_clock / (2 * (clkdiv + 1)));
}
if (ios->bus_width == MMC_BUS_WIDTH_4 && host->board->wire4) {
DBG("MMC: Setting controller bus width to 4\n");
at91_mci_write(AT91_MCI_SDCR, at91_mci_read(AT91_MCI_SDCR) | AT91_MCI_SDCBUS);
}
else {
DBG("MMC: Setting controller bus width to 1\n");
at91_mci_write(AT91_MCI_SDCR, at91_mci_read(AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS);
}
/* Set the clock divider */
at91_mci_write(AT91_MCI_MR, (at91_mci_read(AT91_MCI_MR) & ~AT91_MCI_CLKDIV) | clkdiv);
/* maybe switch power to the card */
if (host && host->board->vcc_pin) {
switch (ios->power_mode) {
case MMC_POWER_OFF:
at91_set_gpio_output(host->board->vcc_pin, 0);
break;
case MMC_POWER_UP:
case MMC_POWER_ON:
at91_set_gpio_output(host->board->vcc_pin, 1);
break;
}
}
}
/*
* Handle an interrupt
*/
static irqreturn_t at91_mci_irq(int irq, void *devid, struct pt_regs *regs)
{
struct at91mci_host *host = devid;
int completed = 0;
unsigned int int_status;
if (host == NULL)
return IRQ_HANDLED;
int_status = at91_mci_read(AT91_MCI_SR);
DBG("MCI irq: status = %08X, %08lX, %08lX\n", int_status, at91_mci_read(AT91_MCI_IMR),
int_status & at91_mci_read(AT91_MCI_IMR));
if ((int_status & at91_mci_read(AT91_MCI_IMR)) & 0xffff0000)
completed = 1;
int_status &= at91_mci_read(AT91_MCI_IMR);
if (int_status & AT91_MCI_UNRE)
DBG("MMC: Underrun error\n");
if (int_status & AT91_MCI_OVRE)
DBG("MMC: Overrun error\n");
if (int_status & AT91_MCI_DTOE)
DBG("MMC: Data timeout\n");
if (int_status & AT91_MCI_DCRCE)
DBG("MMC: CRC error in data\n");
if (int_status & AT91_MCI_RTOE)
DBG("MMC: Response timeout\n");
if (int_status & AT91_MCI_RENDE)
DBG("MMC: Response end bit error\n");
if (int_status & AT91_MCI_RCRCE)
DBG("MMC: Response CRC error\n");
if (int_status & AT91_MCI_RDIRE)
DBG("MMC: Response direction error\n");
if (int_status & AT91_MCI_RINDE)
DBG("MMC: Response index error\n");
/* Only continue processing if no errors */
if (!completed) {
if (int_status & AT91_MCI_TXBUFE) {
DBG("TX buffer empty\n");
at91_mci_handle_transmitted(host);
}
if (int_status & AT91_MCI_RXBUFF) {
DBG("RX buffer full\n");
at91_mci_write(AT91_MCI_IER, AT91_MCI_CMDRDY);
}
if (int_status & AT91_MCI_ENDTX) {
DBG("Transmit has ended\n");
}
if (int_status & AT91_MCI_ENDRX) {
DBG("Receive has ended\n");
at91mci_post_dma_read(host);
}
if (int_status & AT91_MCI_NOTBUSY) {
DBG("Card is ready\n");
at91_mci_write(AT91_MCI_IER, AT91_MCI_CMDRDY);
}
if (int_status & AT91_MCI_DTIP) {
DBG("Data transfer in progress\n");
}
if (int_status & AT91_MCI_BLKE) {
DBG("Block transfer has ended\n");
}
if (int_status & AT91_MCI_TXRDY) {
DBG("Ready to transmit\n");
}
if (int_status & AT91_MCI_RXRDY) {
DBG("Ready to receive\n");
}
if (int_status & AT91_MCI_CMDRDY) {
DBG("Command ready\n");
completed = 1;
}
}
at91_mci_write(AT91_MCI_IDR, int_status);
if (completed) {
DBG("Completed command\n");
at91_mci_write(AT91_MCI_IDR, 0xffffffff);
at91mci_completed_command(host);
}
return IRQ_HANDLED;
}
static irqreturn_t at91_mmc_det_irq(int irq, void *_host, struct pt_regs *regs)
{
struct at91mci_host *host = _host;
int present = !at91_get_gpio_value(irq);
/*
* we expect this irq on both insert and remove,
* and use a short delay to debounce.
*/
if (present != host->present) {
host->present = present;
DBG("%s: card %s\n", mmc_hostname(host->mmc),
present ? "insert" : "remove");
if (!present) {
DBG("****** Resetting SD-card bus width ******\n");
at91_mci_write(AT91_MCI_SDCR, 0);
}
mmc_detect_change(host->mmc, msecs_to_jiffies(100));
}
return IRQ_HANDLED;
}
int at91_mci_get_ro(struct mmc_host *mmc)
{
int read_only = 0;
struct at91mci_host *host = mmc_priv(mmc);
if (host->board->wp_pin) {
read_only = at91_get_gpio_value(host->board->wp_pin);
printk(KERN_WARNING "%s: card is %s\n", mmc_hostname(mmc),
(read_only ? "read-only" : "read-write") );
}
else {
printk(KERN_WARNING "%s: host does not support reading read-only "
"switch. Assuming write-enable.\n", mmc_hostname(mmc));
}
return read_only;
}
static struct mmc_host_ops at91_mci_ops = {
.request = at91_mci_request,
.set_ios = at91_mci_set_ios,
.get_ro = at91_mci_get_ro,
};
/*
* Probe for the device
*/
static int at91_mci_probe(struct platform_device *pdev)
{
struct mmc_host *mmc;
struct at91mci_host *host;
int ret;
DBG("Probe MCI devices\n");
at91_mci_disable();
at91_mci_enable();
mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev);
if (!mmc) {
DBG("Failed to allocate mmc host\n");
return -ENOMEM;
}
mmc->ops = &at91_mci_ops;
mmc->f_min = 375000;
mmc->f_max = 25000000;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
host = mmc_priv(mmc);
host->mmc = mmc;
host->buffer = NULL;
host->bus_mode = 0;
host->board = pdev->dev.platform_data;
if (host->board->wire4) {
#ifdef SUPPORT_4WIRE
mmc->caps |= MMC_CAP_4_BIT_DATA;
#else
printk("MMC: 4 wire bus mode not supported by this driver - using 1 wire\n");
#endif
}
/*
* Get Clock
*/
mci_clk = clk_get(&pdev->dev, "mci_clk");
if (!mci_clk) {
printk(KERN_ERR "AT91 MMC: no clock defined.\n");
return -ENODEV;
}
clk_enable(mci_clk); /* Enable the peripheral clock */
/*
* Allocate the MCI interrupt
*/
ret = request_irq(AT91_ID_MCI, at91_mci_irq, SA_SHIRQ, DRIVER_NAME, host);
if (ret) {
DBG("Failed to request MCI interrupt\n");
return ret;
}
platform_set_drvdata(pdev, mmc);
/*
* Add host to MMC layer
*/
if (host->board->det_pin)
host->present = !at91_get_gpio_value(host->board->det_pin);
else
host->present = -1;
mmc_add_host(mmc);
/*
* monitor card insertion/removal if we can
*/
if (host->board->det_pin) {
ret = request_irq(host->board->det_pin, at91_mmc_det_irq,
SA_SAMPLE_RANDOM, DRIVER_NAME, host);
if (ret)
DBG("couldn't allocate MMC detect irq\n");
}
DBG(KERN_INFO "Added MCI driver\n");
return 0;
}
/*
* Remove a device
*/
static int at91_mci_remove(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
struct at91mci_host *host;
if (!mmc)
return -1;
host = mmc_priv(mmc);
if (host->present != -1) {
free_irq(host->board->det_pin, host);
cancel_delayed_work(&host->mmc->detect);
}
mmc_remove_host(mmc);
at91_mci_disable();
free_irq(AT91_ID_MCI, host);
mmc_free_host(mmc);
clk_disable(mci_clk); /* Disable the peripheral clock */
clk_put(mci_clk);
platform_set_drvdata(pdev, NULL);
DBG("Removed\n");
return 0;
}
#ifdef CONFIG_PM
static int at91_mci_suspend(struct platform_device *pdev, pm_message_t state)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
int ret = 0;
if (mmc)
ret = mmc_suspend_host(mmc, state);
return ret;
}
static int at91_mci_resume(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
int ret = 0;
if (mmc)
ret = mmc_resume_host(mmc);
return ret;
}
#else
#define at91_mci_suspend NULL
#define at91_mci_resume NULL
#endif
static struct platform_driver at91_mci_driver = {
.probe = at91_mci_probe,
.remove = at91_mci_remove,
.suspend = at91_mci_suspend,
.resume = at91_mci_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
};
static int __init at91_mci_init(void)
{
return platform_driver_register(&at91_mci_driver);
}
static void __exit at91_mci_exit(void)
{
platform_driver_unregister(&at91_mci_driver);
}
module_init(at91_mci_init);
module_exit(at91_mci_exit);
MODULE_DESCRIPTION("AT91 Multimedia Card Interface driver");
MODULE_AUTHOR("Nick Randell");
MODULE_LICENSE("GPL");
/*
* linux/drivers/mmc/imxmmc.c - Motorola i.MX MMCI driver
*
* Copyright (C) 2004 Sascha Hauer, Pengutronix <sascha@saschahauer.de>
* Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com>
*
* derived from pxamci.c by Russell King
*
* 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.
*
* 2005-04-17 Pavel Pisa <pisa@cmp.felk.cvut.cz>
* Changed to conform redesigned i.MX scatter gather DMA interface
*
* 2005-11-04 Pavel Pisa <pisa@cmp.felk.cvut.cz>
* Updated for 2.6.14 kernel
*
* 2005-12-13 Jay Monkman <jtm@smoothsmoothie.com>
* Found and corrected problems in the write path
*
* 2005-12-30 Pavel Pisa <pisa@cmp.felk.cvut.cz>
* The event handling rewritten right way in softirq.
* Added many ugly hacks and delays to overcome SDHC
* deficiencies
*
*/
#include <linux/config.h>
#ifdef CONFIG_MMC_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/blkdev.h>
#include <linux/dma-mapping.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/protocol.h>
#include <linux/delay.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/sizes.h>
#include <asm/arch/mmc.h>
#include <asm/arch/imx-dma.h>
#include "imxmmc.h"
#define DRIVER_NAME "imx-mmc"
#define IMXMCI_INT_MASK_DEFAULT (INT_MASK_BUF_READY | INT_MASK_DATA_TRAN | \
INT_MASK_WRITE_OP_DONE | INT_MASK_END_CMD_RES | \
INT_MASK_AUTO_CARD_DETECT | INT_MASK_DAT0_EN | INT_MASK_SDIO)
struct imxmci_host {
struct mmc_host *mmc;
spinlock_t lock;
struct resource *res;
int irq;
imx_dmach_t dma;
unsigned int clkrt;
unsigned int cmdat;
volatile unsigned int imask;
unsigned int power_mode;
unsigned int present;
struct imxmmc_platform_data *pdata;
struct mmc_request *req;
struct mmc_command *cmd;
struct mmc_data *data;
struct timer_list timer;
struct tasklet_struct tasklet;
unsigned int status_reg;
unsigned long pending_events;
/* Next to fields are there for CPU driven transfers to overcome SDHC deficiencies */
u16 *data_ptr;
unsigned int data_cnt;
atomic_t stuck_timeout;
unsigned int dma_nents;
unsigned int dma_size;
unsigned int dma_dir;
int dma_allocated;
unsigned char actual_bus_width;
};
#define IMXMCI_PEND_IRQ_b 0
#define IMXMCI_PEND_DMA_END_b 1
#define IMXMCI_PEND_DMA_ERR_b 2
#define IMXMCI_PEND_WAIT_RESP_b 3
#define IMXMCI_PEND_DMA_DATA_b 4
#define IMXMCI_PEND_CPU_DATA_b 5
#define IMXMCI_PEND_CARD_XCHG_b 6
#define IMXMCI_PEND_SET_INIT_b 7
#define IMXMCI_PEND_IRQ_m (1 << IMXMCI_PEND_IRQ_b)
#define IMXMCI_PEND_DMA_END_m (1 << IMXMCI_PEND_DMA_END_b)
#define IMXMCI_PEND_DMA_ERR_m (1 << IMXMCI_PEND_DMA_ERR_b)
#define IMXMCI_PEND_WAIT_RESP_m (1 << IMXMCI_PEND_WAIT_RESP_b)
#define IMXMCI_PEND_DMA_DATA_m (1 << IMXMCI_PEND_DMA_DATA_b)
#define IMXMCI_PEND_CPU_DATA_m (1 << IMXMCI_PEND_CPU_DATA_b)
#define IMXMCI_PEND_CARD_XCHG_m (1 << IMXMCI_PEND_CARD_XCHG_b)
#define IMXMCI_PEND_SET_INIT_m (1 << IMXMCI_PEND_SET_INIT_b)
static void imxmci_stop_clock(struct imxmci_host *host)
{
int i = 0;
MMC_STR_STP_CLK &= ~STR_STP_CLK_START_CLK;
while(i < 0x1000) {
if(!(i & 0x7f))
MMC_STR_STP_CLK |= STR_STP_CLK_STOP_CLK;
if(!(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)) {
/* Check twice before cut */
if(!(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN))
return;
}
i++;
}
dev_dbg(mmc_dev(host->mmc), "imxmci_stop_clock blocked, no luck\n");
}
static void imxmci_start_clock(struct imxmci_host *host)
{
int i = 0;
MMC_STR_STP_CLK &= ~STR_STP_CLK_STOP_CLK;
while(i < 0x1000) {
if(!(i & 0x7f))
MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK;
if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN) {
/* Check twice before cut */
if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)
return;
}
i++;
}
dev_dbg(mmc_dev(host->mmc), "imxmci_start_clock blocked, no luck\n");
}
static void imxmci_softreset(void)
{
/* reset sequence */
MMC_STR_STP_CLK = 0x8;
MMC_STR_STP_CLK = 0xD;
MMC_STR_STP_CLK = 0x5;
MMC_STR_STP_CLK = 0x5;
MMC_STR_STP_CLK = 0x5;
MMC_STR_STP_CLK = 0x5;
MMC_STR_STP_CLK = 0x5;
MMC_STR_STP_CLK = 0x5;
MMC_STR_STP_CLK = 0x5;
MMC_STR_STP_CLK = 0x5;
MMC_RES_TO = 0xff;
MMC_BLK_LEN = 512;
MMC_NOB = 1;
}
static int imxmci_busy_wait_for_status(struct imxmci_host *host,
unsigned int *pstat, unsigned int stat_mask,
int timeout, const char *where)
{
int loops=0;
while(!(*pstat & stat_mask)) {
loops+=2;
if(loops >= timeout) {
dev_dbg(mmc_dev(host->mmc), "busy wait timeout in %s, STATUS = 0x%x (0x%x)\n",
where, *pstat, stat_mask);
return -1;
}
udelay(2);
*pstat |= MMC_STATUS;
}
if(!loops)
return 0;
dev_info(mmc_dev(host->mmc), "busy wait for %d usec in %s, STATUS = 0x%x (0x%x)\n",
loops, where, *pstat, stat_mask);
return loops;
}
static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
{
unsigned int nob = data->blocks;
unsigned int blksz = 1 << data->blksz_bits;
unsigned int datasz = nob * blksz;
int i;
if (data->flags & MMC_DATA_STREAM)
nob = 0xffff;
host->data = data;
data->bytes_xfered = 0;
MMC_NOB = nob;
MMC_BLK_LEN = blksz;
/*
* DMA cannot be used for small block sizes, we have to use CPU driven transfers otherwise.
* We are in big troubles for non-512 byte transfers according to note in the paragraph
* 20.6.7 of User Manual anyway, but we need to be able to transfer SCR at least.
* The situation is even more complex in reality. The SDHC in not able to handle wll
* partial FIFO fills and reads. The length has to be rounded up to burst size multiple.
* This is required for SCR read at least.
*/
if (datasz < 64) {
host->dma_size = datasz;
if (data->flags & MMC_DATA_READ) {
host->dma_dir = DMA_FROM_DEVICE;
/* Hack to enable read SCR */
if(datasz < 16) {
MMC_NOB = 1;
MMC_BLK_LEN = 16;
}
} else {
host->dma_dir = DMA_TO_DEVICE;
}
/* Convert back to virtual address */
host->data_ptr = (u16*)(page_address(data->sg->page) + data->sg->offset);
host->data_cnt = 0;
clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events);
set_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events);
return;
}
if (data->flags & MMC_DATA_READ) {
host->dma_dir = DMA_FROM_DEVICE;
host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
data->sg_len, host->dma_dir);
imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz,
host->res->start + MMC_BUFFER_ACCESS_OFS, DMA_MODE_READ);
/*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_READ, IMX_DMA_WIDTH_16, CCR_REN);*/
CCR(host->dma) = CCR_DMOD_LINEAR | CCR_DSIZ_32 | CCR_SMOD_FIFO | CCR_SSIZ_16 | CCR_REN;
} else {
host->dma_dir = DMA_TO_DEVICE;
host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
data->sg_len, host->dma_dir);
imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz,
host->res->start + MMC_BUFFER_ACCESS_OFS, DMA_MODE_WRITE);
/*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_WRITE, IMX_DMA_WIDTH_16, CCR_REN);*/
CCR(host->dma) = CCR_SMOD_LINEAR | CCR_SSIZ_32 | CCR_DMOD_FIFO | CCR_DSIZ_16 | CCR_REN;
}
#if 1 /* This code is there only for consistency checking and can be disabled in future */
host->dma_size = 0;
for(i=0; i<host->dma_nents; i++)
host->dma_size+=data->sg[i].length;
if (datasz > host->dma_size) {
dev_err(mmc_dev(host->mmc), "imxmci_setup_data datasz 0x%x > 0x%x dm_size\n",
datasz, host->dma_size);
}
#endif
host->dma_size = datasz;
wmb();
if(host->actual_bus_width == MMC_BUS_WIDTH_4)
BLR(host->dma) = 0; /* burst 64 byte read / 64 bytes write */
else
BLR(host->dma) = 16; /* burst 16 byte read / 16 bytes write */
RSSR(host->dma) = DMA_REQ_SDHC;
set_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events);
clear_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events);
/* start DMA engine for read, write is delayed after initial response */
if (host->dma_dir == DMA_FROM_DEVICE) {
imx_dma_enable(host->dma);
}
}
static void imxmci_start_cmd(struct imxmci_host *host, struct mmc_command *cmd, unsigned int cmdat)
{
unsigned long flags;
u32 imask;
WARN_ON(host->cmd != NULL);
host->cmd = cmd;
if (cmd->flags & MMC_RSP_BUSY)
cmdat |= CMD_DAT_CONT_BUSY;
switch (mmc_resp_type(cmd)) {
case MMC_RSP_R1: /* short CRC, OPCODE */
case MMC_RSP_R1B:/* short CRC, OPCODE, BUSY */
cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R1;
break;
case MMC_RSP_R2: /* long 136 bit + CRC */
cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R2;
break;
case MMC_RSP_R3: /* short */
cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R3;
break;
case MMC_RSP_R6: /* short CRC */
cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R6;
break;
default:
break;
}
if ( test_and_clear_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events) )
cmdat |= CMD_DAT_CONT_INIT; /* This command needs init */
if ( host->actual_bus_width == MMC_BUS_WIDTH_4 )
cmdat |= CMD_DAT_CONT_BUS_WIDTH_4;
MMC_CMD = cmd->opcode;
MMC_ARGH = cmd->arg >> 16;
MMC_ARGL = cmd->arg & 0xffff;
MMC_CMD_DAT_CONT = cmdat;
atomic_set(&host->stuck_timeout, 0);
set_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events);
imask = IMXMCI_INT_MASK_DEFAULT;
imask &= ~INT_MASK_END_CMD_RES;
if ( cmdat & CMD_DAT_CONT_DATA_ENABLE ) {
/*imask &= ~INT_MASK_BUF_READY;*/
imask &= ~INT_MASK_DATA_TRAN;
if ( cmdat & CMD_DAT_CONT_WRITE )
imask &= ~INT_MASK_WRITE_OP_DONE;
if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events))
imask &= ~INT_MASK_BUF_READY;
}
spin_lock_irqsave(&host->lock, flags);
host->imask = imask;
MMC_INT_MASK = host->imask;
spin_unlock_irqrestore(&host->lock, flags);
dev_dbg(mmc_dev(host->mmc), "CMD%02d (0x%02x) mask set to 0x%04x\n",
cmd->opcode, cmd->opcode, imask);
imxmci_start_clock(host);
}
static void imxmci_finish_request(struct imxmci_host *host, struct mmc_request *req)
{
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
host->pending_events &= ~(IMXMCI_PEND_WAIT_RESP_m | IMXMCI_PEND_DMA_END_m |
IMXMCI_PEND_DMA_DATA_m | IMXMCI_PEND_CPU_DATA_m);
host->imask = IMXMCI_INT_MASK_DEFAULT;
MMC_INT_MASK = host->imask;
spin_unlock_irqrestore(&host->lock, flags);
host->req = NULL;
host->cmd = NULL;
host->data = NULL;
mmc_request_done(host->mmc, req);
}
static int imxmci_finish_data(struct imxmci_host *host, unsigned int stat)
{
struct mmc_data *data = host->data;
int data_error;
if(test_and_clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)){
imx_dma_disable(host->dma);
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_nents,
host->dma_dir);
}
if ( stat & STATUS_ERR_MASK ) {
dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",stat);
if(stat & (STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR))
data->error = MMC_ERR_BADCRC;
else if(stat & STATUS_TIME_OUT_READ)
data->error = MMC_ERR_TIMEOUT;
else
data->error = MMC_ERR_FAILED;
} else {
data->bytes_xfered = host->dma_size;
}
data_error = data->error;
host->data = NULL;
return data_error;
}
static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat)
{
struct mmc_command *cmd = host->cmd;
int i;
u32 a,b,c;
struct mmc_data *data = host->data;
if (!cmd)
return 0;
host->cmd = NULL;
if (stat & STATUS_TIME_OUT_RESP) {
dev_dbg(mmc_dev(host->mmc), "CMD TIMEOUT\n");
cmd->error = MMC_ERR_TIMEOUT;
} else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
dev_dbg(mmc_dev(host->mmc), "cmd crc error\n");
cmd->error = MMC_ERR_BADCRC;
}
if(cmd->flags & MMC_RSP_PRESENT) {
if(cmd->flags & MMC_RSP_136) {
for (i = 0; i < 4; i++) {
u32 a = MMC_RES_FIFO & 0xffff;
u32 b = MMC_RES_FIFO & 0xffff;
cmd->resp[i] = a<<16 | b;
}
} else {
a = MMC_RES_FIFO & 0xffff;
b = MMC_RES_FIFO & 0xffff;
c = MMC_RES_FIFO & 0xffff;
cmd->resp[0] = a<<24 | b<<8 | c>>8;
}
}
dev_dbg(mmc_dev(host->mmc), "RESP 0x%08x, 0x%08x, 0x%08x, 0x%08x, error %d\n",
cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], cmd->error);
if (data && (cmd->error == MMC_ERR_NONE) && !(stat & STATUS_ERR_MASK)) {
if (host->req->data->flags & MMC_DATA_WRITE) {
/* Wait for FIFO to be empty before starting DMA write */
stat = MMC_STATUS;
if(imxmci_busy_wait_for_status(host, &stat,
STATUS_APPL_BUFF_FE,
40, "imxmci_cmd_done DMA WR") < 0) {
cmd->error = MMC_ERR_FIFO;
imxmci_finish_data(host, stat);
if(host->req)
imxmci_finish_request(host, host->req);
dev_warn(mmc_dev(host->mmc), "STATUS = 0x%04x\n",
stat);
return 0;
}
if(test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
imx_dma_enable(host->dma);
}
}
} else {
struct mmc_request *req;
imxmci_stop_clock(host);
req = host->req;
if(data)
imxmci_finish_data(host, stat);
if( req ) {
imxmci_finish_request(host, req);
} else {
dev_warn(mmc_dev(host->mmc), "imxmci_cmd_done: no request to finish\n");
}
}
return 1;
}
static int imxmci_data_done(struct imxmci_host *host, unsigned int stat)
{
struct mmc_data *data = host->data;
int data_error;
if (!data)
return 0;
data_error = imxmci_finish_data(host, stat);
if (host->req->stop && (data_error == MMC_ERR_NONE)) {
imxmci_stop_clock(host);
imxmci_start_cmd(host, host->req->stop, 0);
} else {
struct mmc_request *req;
req = host->req;
if( req ) {
imxmci_finish_request(host, req);
} else {
dev_warn(mmc_dev(host->mmc), "imxmci_data_done: no request to finish\n");
}
}
return 1;
}
static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat)
{
int i;
int burst_len;
int flush_len;
int trans_done = 0;
unsigned int stat = *pstat;
if(host->actual_bus_width == MMC_BUS_WIDTH_4)
burst_len = 16;
else
burst_len = 64;
/* This is unfortunately required */
dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data running STATUS = 0x%x\n",
stat);
if(host->dma_dir == DMA_FROM_DEVICE) {
imxmci_busy_wait_for_status(host, &stat,
STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE,
20, "imxmci_cpu_driven_data read");
while((stat & (STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE)) &&
(host->data_cnt < host->dma_size)) {
if(burst_len >= host->dma_size - host->data_cnt) {
flush_len = burst_len;
burst_len = host->dma_size - host->data_cnt;
flush_len -= burst_len;
host->data_cnt = host->dma_size;
trans_done = 1;
} else {
flush_len = 0;
host->data_cnt += burst_len;
}
for(i = burst_len; i>=2 ; i-=2) {
*(host->data_ptr++) = MMC_BUFFER_ACCESS;
udelay(20); /* required for clocks < 8MHz*/
}
if(i == 1)
*(u8*)(host->data_ptr) = MMC_BUFFER_ACCESS;
stat = MMC_STATUS;
/* Flush extra bytes from FIFO */
while(flush_len >= 2){
flush_len -= 2;
i = MMC_BUFFER_ACCESS;
stat = MMC_STATUS;
stat &= ~STATUS_CRC_READ_ERR; /* Stupid but required there */
}
dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read burst %d STATUS = 0x%x\n",
burst_len, stat);
}
} else {
imxmci_busy_wait_for_status(host, &stat,
STATUS_APPL_BUFF_FE,
20, "imxmci_cpu_driven_data write");
while((stat & STATUS_APPL_BUFF_FE) &&
(host->data_cnt < host->dma_size)) {
if(burst_len >= host->dma_size - host->data_cnt) {
burst_len = host->dma_size - host->data_cnt;
host->data_cnt = host->dma_size;
trans_done = 1;
} else {
host->data_cnt += burst_len;
}
for(i = burst_len; i>0 ; i-=2)
MMC_BUFFER_ACCESS = *(host->data_ptr++);
stat = MMC_STATUS;
dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data write burst %d STATUS = 0x%x\n",
burst_len, stat);
}
}
*pstat = stat;
return trans_done;
}
static void imxmci_dma_irq(int dma, void *devid, struct pt_regs *regs)
{
struct imxmci_host *host = devid;
uint32_t stat = MMC_STATUS;
atomic_set(&host->stuck_timeout, 0);
host->status_reg = stat;
set_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events);
tasklet_schedule(&host->tasklet);
}
static irqreturn_t imxmci_irq(int irq, void *devid, struct pt_regs *regs)
{
struct imxmci_host *host = devid;
uint32_t stat = MMC_STATUS;
int handled = 1;
MMC_INT_MASK = host->imask | INT_MASK_SDIO | INT_MASK_AUTO_CARD_DETECT;
atomic_set(&host->stuck_timeout, 0);
host->status_reg = stat;
set_bit(IMXMCI_PEND_IRQ_b, &host->pending_events);
tasklet_schedule(&host->tasklet);
return IRQ_RETVAL(handled);;
}
static void imxmci_tasklet_fnc(unsigned long data)
{
struct imxmci_host *host = (struct imxmci_host *)data;
u32 stat;
unsigned int data_dir_mask = 0; /* STATUS_WR_CRC_ERROR_CODE_MASK */
int timeout = 0;
if(atomic_read(&host->stuck_timeout) > 4) {
char *what;
timeout = 1;
stat = MMC_STATUS;
host->status_reg = stat;
if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
what = "RESP+DMA";
else
what = "RESP";
else
if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events))
what = "DATA";
else
what = "DMA";
else
what = "???";
dev_err(mmc_dev(host->mmc), "%s TIMEOUT, hardware stucked STATUS = 0x%04x IMASK = 0x%04x\n",
what, stat, MMC_INT_MASK);
dev_err(mmc_dev(host->mmc), "CMD_DAT_CONT = 0x%04x, MMC_BLK_LEN = 0x%04x, MMC_NOB = 0x%04x, DMA_CCR = 0x%08x\n",
MMC_CMD_DAT_CONT, MMC_BLK_LEN, MMC_NOB, CCR(host->dma));
dev_err(mmc_dev(host->mmc), "CMD%d, bus %d-bit, dma_size = 0x%x\n",
host->cmd?host->cmd->opcode:0, 1<<host->actual_bus_width, host->dma_size);
}
if(!host->present || timeout)
host->status_reg = STATUS_TIME_OUT_RESP | STATUS_TIME_OUT_READ |
STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR;
if(test_bit(IMXMCI_PEND_IRQ_b, &host->pending_events) || timeout) {
clear_bit(IMXMCI_PEND_IRQ_b, &host->pending_events);
stat = MMC_STATUS;
/*
* This is not required in theory, but there is chance to miss some flag
* which clears automatically by mask write, FreeScale original code keeps
* stat from IRQ time so do I
*/
stat |= host->status_reg;
if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
imxmci_busy_wait_for_status(host, &stat,
STATUS_END_CMD_RESP | STATUS_ERR_MASK,
20, "imxmci_tasklet_fnc resp (ERRATUM #4)");
}
if(stat & (STATUS_END_CMD_RESP | STATUS_ERR_MASK)) {
if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
imxmci_cmd_done(host, stat);
if(host->data && (stat & STATUS_ERR_MASK))
imxmci_data_done(host, stat);
}
if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) {
stat |= MMC_STATUS;
if(imxmci_cpu_driven_data(host, &stat)){
if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
imxmci_cmd_done(host, stat);
atomic_clear_mask(IMXMCI_PEND_IRQ_m|IMXMCI_PEND_CPU_DATA_m,
&host->pending_events);
imxmci_data_done(host, stat);
}
}
}
if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events) &&
!test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
stat = MMC_STATUS;
/* Same as above */
stat |= host->status_reg;
if(host->dma_dir == DMA_TO_DEVICE) {
data_dir_mask = STATUS_WRITE_OP_DONE;
} else {
data_dir_mask = STATUS_DATA_TRANS_DONE;
}
imxmci_busy_wait_for_status(host, &stat,
data_dir_mask,
50, "imxmci_tasklet_fnc data");
if(stat & data_dir_mask) {
clear_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events);
imxmci_data_done(host, stat);
}
}
if(test_and_clear_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events)) {
if(host->cmd)
imxmci_cmd_done(host, STATUS_TIME_OUT_RESP);
if(host->data)
imxmci_data_done(host, STATUS_TIME_OUT_READ |
STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR);
if(host->req)
imxmci_finish_request(host, host->req);
mmc_detect_change(host->mmc, msecs_to_jiffies(100));
}
}
static void imxmci_request(struct mmc_host *mmc, struct mmc_request *req)
{
struct imxmci_host *host = mmc_priv(mmc);
unsigned int cmdat;
WARN_ON(host->req != NULL);
host->req = req;
cmdat = 0;
if (req->data) {
imxmci_setup_data(host, req->data);
cmdat |= CMD_DAT_CONT_DATA_ENABLE;
if (req->data->flags & MMC_DATA_WRITE)
cmdat |= CMD_DAT_CONT_WRITE;
if (req->data->flags & MMC_DATA_STREAM) {
cmdat |= CMD_DAT_CONT_STREAM_BLOCK;
}
}
imxmci_start_cmd(host, req->cmd, cmdat);
}
#define CLK_RATE 19200000
static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct imxmci_host *host = mmc_priv(mmc);
int prescaler;
dev_dbg(mmc_dev(host->mmc), "clock %u power %u vdd %u width %u\n",
ios->clock, ios->power_mode, ios->vdd,
(ios->bus_width==MMC_BUS_WIDTH_4)?4:1);
if( ios->bus_width==MMC_BUS_WIDTH_4 ) {
host->actual_bus_width = MMC_BUS_WIDTH_4;
imx_gpio_mode(PB11_PF_SD_DAT3);
}else{
host->actual_bus_width = MMC_BUS_WIDTH_1;
imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11);
}
if ( host->power_mode != ios->power_mode ) {
switch (ios->power_mode) {
case MMC_POWER_OFF:
break;
case MMC_POWER_UP:
set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events);
break;
case MMC_POWER_ON:
break;
}
host->power_mode = ios->power_mode;
}
if ( ios->clock ) {
unsigned int clk;
/* The prescaler is 5 for PERCLK2 equal to 96MHz
* then 96MHz / 5 = 19.2 MHz
*/
clk=imx_get_perclk2();
prescaler=(clk+(CLK_RATE*7)/8)/CLK_RATE;
switch(prescaler) {
case 0:
case 1: prescaler = 0;
break;
case 2: prescaler = 1;
break;
case 3: prescaler = 2;
break;
case 4: prescaler = 4;
break;
default:
case 5: prescaler = 5;
break;
}
dev_dbg(mmc_dev(host->mmc), "PERCLK2 %d MHz -> prescaler %d\n",
clk, prescaler);
for(clk=0; clk<8; clk++) {
int x;
x = CLK_RATE / (1<<clk);
if( x <= ios->clock)
break;
}
MMC_STR_STP_CLK |= STR_STP_CLK_ENABLE; /* enable controller */
imxmci_stop_clock(host);
MMC_CLK_RATE = (prescaler<<3) | clk;
imxmci_start_clock(host);
dev_dbg(mmc_dev(host->mmc), "MMC_CLK_RATE: 0x%08x\n", MMC_CLK_RATE);
} else {
imxmci_stop_clock(host);
}
}
static struct mmc_host_ops imxmci_ops = {
.request = imxmci_request,
.set_ios = imxmci_set_ios,
};
static struct resource *platform_device_resource(struct platform_device *dev, unsigned int mask, int nr)
{
int i;
for (i = 0; i < dev->num_resources; i++)
if (dev->resource[i].flags == mask && nr-- == 0)
return &dev->resource[i];
return NULL;
}
static int platform_device_irq(struct platform_device *dev, int nr)
{
int i;
for (i = 0; i < dev->num_resources; i++)
if (dev->resource[i].flags == IORESOURCE_IRQ && nr-- == 0)
return dev->resource[i].start;
return NO_IRQ;
}
static void imxmci_check_status(unsigned long data)
{
struct imxmci_host *host = (struct imxmci_host *)data;
if( host->pdata->card_present() != host->present ) {
host->present ^= 1;
dev_info(mmc_dev(host->mmc), "card %s\n",
host->present ? "inserted" : "removed");
set_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events);
tasklet_schedule(&host->tasklet);
}
if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events) ||
test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
atomic_inc(&host->stuck_timeout);
if(atomic_read(&host->stuck_timeout) > 4)
tasklet_schedule(&host->tasklet);
} else {
atomic_set(&host->stuck_timeout, 0);
}
mod_timer(&host->timer, jiffies + (HZ>>1));
}
static int imxmci_probe(struct platform_device *pdev)
{
struct mmc_host *mmc;
struct imxmci_host *host = NULL;
struct resource *r;
int ret = 0, irq;
printk(KERN_INFO "i.MX mmc driver\n");
r = platform_device_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_device_irq(pdev, 0);
if (!r || irq == NO_IRQ)
return -ENXIO;
r = request_mem_region(r->start, 0x100, "IMXMCI");
if (!r)
return -EBUSY;
mmc = mmc_alloc_host(sizeof(struct imxmci_host), &pdev->dev);
if (!mmc) {
ret = -ENOMEM;
goto out;
}
mmc->ops = &imxmci_ops;
mmc->f_min = 150000;
mmc->f_max = CLK_RATE/2;
mmc->ocr_avail = MMC_VDD_32_33;
mmc->caps |= MMC_CAP_4_BIT_DATA;
/* MMC core transfer sizes tunable parameters */
mmc->max_hw_segs = 64;
mmc->max_phys_segs = 64;
mmc->max_sectors = 64; /* default 1 << (PAGE_CACHE_SHIFT - 9) */
mmc->max_seg_size = 64*512; /* default PAGE_CACHE_SIZE */
host = mmc_priv(mmc);
host->mmc = mmc;
host->dma_allocated = 0;
host->pdata = pdev->dev.platform_data;
spin_lock_init(&host->lock);
host->res = r;
host->irq = irq;
imx_gpio_mode(PB8_PF_SD_DAT0);
imx_gpio_mode(PB9_PF_SD_DAT1);
imx_gpio_mode(PB10_PF_SD_DAT2);
/* Configured as GPIO with pull-up to ensure right MCC card mode */
/* Switched to PB11_PF_SD_DAT3 if 4 bit bus is configured */
imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11);
/* imx_gpio_mode(PB11_PF_SD_DAT3); */
imx_gpio_mode(PB12_PF_SD_CLK);
imx_gpio_mode(PB13_PF_SD_CMD);
imxmci_softreset();
if ( MMC_REV_NO != 0x390 ) {
dev_err(mmc_dev(host->mmc), "wrong rev.no. 0x%08x. aborting.\n",
MMC_REV_NO);
goto out;
}
MMC_READ_TO = 0x2db4; /* recommended in data sheet */
host->imask = IMXMCI_INT_MASK_DEFAULT;
MMC_INT_MASK = host->imask;
if(imx_dma_request_by_prio(&host->dma, DRIVER_NAME, DMA_PRIO_LOW)<0){
dev_err(mmc_dev(host->mmc), "imx_dma_request_by_prio failed\n");
ret = -EBUSY;
goto out;
}
host->dma_allocated=1;
imx_dma_setup_handlers(host->dma, imxmci_dma_irq, NULL, host);
tasklet_init(&host->tasklet, imxmci_tasklet_fnc, (unsigned long)host);
host->status_reg=0;
host->pending_events=0;
ret = request_irq(host->irq, imxmci_irq, 0, DRIVER_NAME, host);
if (ret)
goto out;
host->present = host->pdata->card_present();
init_timer(&host->timer);
host->timer.data = (unsigned long)host;
host->timer.function = imxmci_check_status;
add_timer(&host->timer);
mod_timer(&host->timer, jiffies + (HZ>>1));
platform_set_drvdata(pdev, mmc);
mmc_add_host(mmc);
return 0;
out:
if (host) {
if(host->dma_allocated){
imx_dma_free(host->dma);
host->dma_allocated=0;
}
}
if (mmc)
mmc_free_host(mmc);
release_resource(r);
return ret;
}
static int imxmci_remove(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
if (mmc) {
struct imxmci_host *host = mmc_priv(mmc);
tasklet_disable(&host->tasklet);
del_timer_sync(&host->timer);
mmc_remove_host(mmc);
free_irq(host->irq, host);
if(host->dma_allocated){
imx_dma_free(host->dma);
host->dma_allocated=0;
}
tasklet_kill(&host->tasklet);
release_resource(host->res);
mmc_free_host(mmc);
}
return 0;
}
#ifdef CONFIG_PM
static int imxmci_suspend(struct platform_device *dev, pm_message_t state)
{
struct mmc_host *mmc = platform_get_drvdata(dev);
int ret = 0;
if (mmc)
ret = mmc_suspend_host(mmc, state);
return ret;
}
static int imxmci_resume(struct platform_device *dev)
{
struct mmc_host *mmc = platform_get_drvdata(dev);
struct imxmci_host *host;
int ret = 0;
if (mmc) {
host = mmc_priv(mmc);
if(host)
set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events);
ret = mmc_resume_host(mmc);
}
return ret;
}
#else
#define imxmci_suspend NULL
#define imxmci_resume NULL
#endif /* CONFIG_PM */
static struct platform_driver imxmci_driver = {
.probe = imxmci_probe,
.remove = imxmci_remove,
.suspend = imxmci_suspend,
.resume = imxmci_resume,
.driver = {
.name = DRIVER_NAME,
}
};
static int __init imxmci_init(void)
{
return platform_driver_register(&imxmci_driver);
}
static void __exit imxmci_exit(void)
{
platform_driver_unregister(&imxmci_driver);
}
module_init(imxmci_init);
module_exit(imxmci_exit);
MODULE_DESCRIPTION("i.MX Multimedia Card Interface Driver");
MODULE_AUTHOR("Sascha Hauer, Pengutronix");
MODULE_LICENSE("GPL");
# define __REG16(x) (*((volatile u16 *)IO_ADDRESS(x)))
#define MMC_STR_STP_CLK __REG16(IMX_MMC_BASE + 0x00)
#define MMC_STATUS __REG16(IMX_MMC_BASE + 0x04)
#define MMC_CLK_RATE __REG16(IMX_MMC_BASE + 0x08)
#define MMC_CMD_DAT_CONT __REG16(IMX_MMC_BASE + 0x0C)
#define MMC_RES_TO __REG16(IMX_MMC_BASE + 0x10)
#define MMC_READ_TO __REG16(IMX_MMC_BASE + 0x14)
#define MMC_BLK_LEN __REG16(IMX_MMC_BASE + 0x18)
#define MMC_NOB __REG16(IMX_MMC_BASE + 0x1C)
#define MMC_REV_NO __REG16(IMX_MMC_BASE + 0x20)
#define MMC_INT_MASK __REG16(IMX_MMC_BASE + 0x24)
#define MMC_CMD __REG16(IMX_MMC_BASE + 0x28)
#define MMC_ARGH __REG16(IMX_MMC_BASE + 0x2C)
#define MMC_ARGL __REG16(IMX_MMC_BASE + 0x30)
#define MMC_RES_FIFO __REG16(IMX_MMC_BASE + 0x34)
#define MMC_BUFFER_ACCESS __REG16(IMX_MMC_BASE + 0x38)
#define MMC_BUFFER_ACCESS_OFS 0x38
#define STR_STP_CLK_ENDIAN (1<<5)
#define STR_STP_CLK_RESET (1<<3)
#define STR_STP_CLK_ENABLE (1<<2)
#define STR_STP_CLK_START_CLK (1<<1)
#define STR_STP_CLK_STOP_CLK (1<<0)
#define STATUS_CARD_PRESENCE (1<<15)
#define STATUS_SDIO_INT_ACTIVE (1<<14)
#define STATUS_END_CMD_RESP (1<<13)
#define STATUS_WRITE_OP_DONE (1<<12)
#define STATUS_DATA_TRANS_DONE (1<<11)
#define STATUS_WR_CRC_ERROR_CODE_MASK (3<<10)
#define STATUS_CARD_BUS_CLK_RUN (1<<8)
#define STATUS_APPL_BUFF_FF (1<<7)
#define STATUS_APPL_BUFF_FE (1<<6)
#define STATUS_RESP_CRC_ERR (1<<5)
#define STATUS_CRC_READ_ERR (1<<3)
#define STATUS_CRC_WRITE_ERR (1<<2)
#define STATUS_TIME_OUT_RESP (1<<1)
#define STATUS_TIME_OUT_READ (1<<0)
#define STATUS_ERR_MASK 0x2f
#define CLK_RATE_PRESCALER(x) ((x) & 0x7)
#define CLK_RATE_CLK_RATE(x) (((x) & 0x7) << 3)
#define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1<<12)
#define CMD_DAT_CONT_STOP_READWAIT (1<<11)
#define CMD_DAT_CONT_START_READWAIT (1<<10)
#define CMD_DAT_CONT_BUS_WIDTH_1 (0<<8)
#define CMD_DAT_CONT_BUS_WIDTH_4 (2<<8)
#define CMD_DAT_CONT_INIT (1<<7)
#define CMD_DAT_CONT_BUSY (1<<6)
#define CMD_DAT_CONT_STREAM_BLOCK (1<<5)
#define CMD_DAT_CONT_WRITE (1<<4)
#define CMD_DAT_CONT_DATA_ENABLE (1<<3)
#define CMD_DAT_CONT_RESPONSE_FORMAT_R1 (1)
#define CMD_DAT_CONT_RESPONSE_FORMAT_R2 (2)
#define CMD_DAT_CONT_RESPONSE_FORMAT_R3 (3)
#define CMD_DAT_CONT_RESPONSE_FORMAT_R4 (4)
#define CMD_DAT_CONT_RESPONSE_FORMAT_R5 (5)
#define CMD_DAT_CONT_RESPONSE_FORMAT_R6 (6)
#define INT_MASK_AUTO_CARD_DETECT (1<<6)
#define INT_MASK_DAT0_EN (1<<5)
#define INT_MASK_SDIO (1<<4)
#define INT_MASK_BUF_READY (1<<3)
#define INT_MASK_END_CMD_RES (1<<2)
#define INT_MASK_WRITE_OP_DONE (1<<1)
#define INT_MASK_DATA_TRAN (1<<0)
#define INT_ALL (0x7f)
/*
* include/asm-arm/arch-at91rm9200/at91rm9200_mci.h
*
* Copyright (C) 2005 Ivan Kokshaysky
* Copyright (C) SAN People
*
* MultiMedia Card Interface (MCI) registers.
* Based on AT91RM9200 datasheet revision E.
*
* 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 AT91RM9200_MCI_H
#define AT91RM9200_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 (3 << 8) /* Power Saving Divider */
#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 (0xf << 0) /* SD Card Selector */
#define AT91_MCI_SDCBUS (1 << 7) /* 1-bit or 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_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_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_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_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) /* Reponse 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
#ifndef ASMARM_ARCH_MMC_H
#define ASMARM_ARCH_MMC_H
#include <linux/mmc/protocol.h>
struct imxmmc_platform_data {
int (*card_present)(void);
};
extern void imx_set_mmc_info(struct imxmmc_platform_data *info);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment