From fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 Mon Sep 17 00:00:00 2001 From: Jonathan Herman Date: Tue, 22 Jan 2013 10:38:37 -0500 Subject: Added missing tegra files. --- drivers/mmc/host/at91_mci.c | 1216 +++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/at91_mci.h | 115 ++++ drivers/mmc/host/imxmmc.c | 1169 +++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/imxmmc.h | 64 +++ 4 files changed, 2564 insertions(+) create mode 100644 drivers/mmc/host/at91_mci.c create mode 100644 drivers/mmc/host/at91_mci.h create mode 100644 drivers/mmc/host/imxmmc.c create mode 100644 drivers/mmc/host/imxmmc.h (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c new file mode 100644 index 00000000000..a4aa3af86fe --- /dev/null +++ b/drivers/mmc/host/at91_mci.c @@ -0,0 +1,1216 @@ +/* + * linux/drivers/mmc/host/at91_mci.c - ATMEL AT91 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 AT91 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 AT91RM9200 controller, when a read is completed, all the words are byte + swapped in the scatterlist buffers. AT91SAM926x are not affected by this bug. + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include "at91_mci.h" + +#define DRIVER_NAME "at91_mci" + +static inline int at91mci_is_mci1rev2xx(void) +{ + return ( cpu_is_at91sam9260() + || cpu_is_at91sam9263() + || cpu_is_at91cap9() + || cpu_is_at91sam9rl() + || cpu_is_at91sam9g10() + || cpu_is_at91sam9g20() + ); +} + +#define FL_SENT_COMMAND (1 << 0) +#define FL_SENT_STOP (1 << 1) + +#define AT91_MCI_ERRORS (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) + +#define at91_mci_read(host, reg) __raw_readl((host)->baseaddr + (reg)) +#define at91_mci_write(host, reg, val) __raw_writel((val), (host)->baseaddr + (reg)) + +#define MCI_BLKSIZE 512 +#define MCI_MAXBLKSIZE 4095 +#define MCI_BLKATONCE 256 +#define MCI_BUFSIZE (MCI_BLKSIZE * MCI_BLKATONCE) + +/* + * Low level type for this driver + */ +struct at91mci_host +{ + struct mmc_host *mmc; + struct mmc_command *cmd; + struct mmc_request *request; + + void __iomem *baseaddr; + int irq; + + struct at91_mmc_data *board; + int present; + + struct clk *mci_clk; + + /* + * 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; + + /* Timer for timeouts */ + struct timer_list timer; +}; + +/* + * Reset the controller and restore most of the state + */ +static void at91_reset_host(struct at91mci_host *host) +{ + unsigned long flags; + u32 mr; + u32 sdcr; + u32 dtor; + u32 imr; + + local_irq_save(flags); + imr = at91_mci_read(host, AT91_MCI_IMR); + + at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); + + /* save current state */ + mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; + sdcr = at91_mci_read(host, AT91_MCI_SDCR); + dtor = at91_mci_read(host, AT91_MCI_DTOR); + + /* reset the controller */ + at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST); + + /* restore state */ + at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); + at91_mci_write(host, AT91_MCI_MR, mr); + at91_mci_write(host, AT91_MCI_SDCR, sdcr); + at91_mci_write(host, AT91_MCI_DTOR, dtor); + at91_mci_write(host, AT91_MCI_IER, imr); + + /* make sure sdio interrupts will fire */ + at91_mci_read(host, AT91_MCI_SR); + + local_irq_restore(flags); +} + +static void at91_timeout_timer(unsigned long data) +{ + struct at91mci_host *host; + + host = (struct at91mci_host *)data; + + if (host->request) { + dev_err(host->mmc->parent, "Timeout waiting end of packet\n"); + + if (host->cmd && host->cmd->data) { + host->cmd->data->error = -ETIMEDOUT; + } else { + if (host->cmd) + host->cmd->error = -ETIMEDOUT; + else + host->request->cmd->error = -ETIMEDOUT; + } + + at91_reset_host(host); + mmc_request_done(host->mmc, host->request); + } +} + +/* + * Copy from sg to a dma block - used for transfers + */ +static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data) +{ + unsigned int len, i, size; + unsigned *dmabuf = host->buffer; + + size = data->blksz * data->blocks; + len = data->sg_len; + + /* MCI1 rev2xx Data Write Operation and number of bytes erratum */ + if (at91mci_is_mci1rev2xx()) + if (host->total_length == 12) + memset(dmabuf, 0, 12); + + /* + * 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; + unsigned int *sgbuffer; + + sg = &data->sg[i]; + + sgbuffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; + amount = min(size, sg->length); + size -= amount; + + if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ + int index; + + for (index = 0; index < (amount / 4); index++) + *dmabuf++ = swab32(sgbuffer[index]); + } else { + char *tmpv = (char *)dmabuf; + memcpy(tmpv, sgbuffer, amount); + tmpv += amount; + dmabuf = (unsigned *)tmpv; + } + + 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); +} + +/* + * Handle after a dma read + */ +static void at91_mci_post_dma_read(struct at91mci_host *host) +{ + struct mmc_command *cmd; + struct mmc_data *data; + unsigned int len, i, size; + unsigned *dmabuf = host->buffer; + + pr_debug("post dma read\n"); + + cmd = host->cmd; + if (!cmd) { + pr_debug("no command\n"); + return; + } + + data = cmd->data; + if (!data) { + pr_debug("no data\n"); + return; + } + + size = data->blksz * data->blocks; + len = data->sg_len; + + at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX); + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); + + for (i = 0; i < len; i++) { + struct scatterlist *sg; + int amount; + unsigned int *sgbuffer; + + sg = &data->sg[i]; + + sgbuffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; + amount = min(size, sg->length); + size -= amount; + + if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ + int index; + for (index = 0; index < (amount / 4); index++) + sgbuffer[index] = swab32(*dmabuf++); + } else { + char *tmpv = (char *)dmabuf; + memcpy(sgbuffer, tmpv, amount); + tmpv += amount; + dmabuf = (unsigned *)tmpv; + } + + flush_kernel_dcache_page(sg_page(sg)); + kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ); + data->bytes_xfered += amount; + if (size == 0) + break; + } + + pr_debug("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; + + pr_debug("Handling the transmit\n"); + + /* Disable the transfer */ + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); + + /* Now wait for cmd ready */ + at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE); + + cmd = host->cmd; + if (!cmd) return; + + data = cmd->data; + if (!data) return; + + if (cmd->data->blocks > 1) { + pr_debug("multiple write : wait for BLKE...\n"); + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE); + } else + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); +} + +/* + * Update bytes tranfered count during a write operation + */ +static void at91_mci_update_bytes_xfered(struct at91mci_host *host) +{ + struct mmc_data *data; + + /* always deal with the effective request (and not the current cmd) */ + + if (host->request->cmd && host->request->cmd->error != 0) + return; + + if (host->request->data) { + data = host->request->data; + if (data->flags & MMC_DATA_WRITE) { + /* card is in IDLE mode now */ + pr_debug("-> bytes_xfered %d, total_length = %d\n", + data->bytes_xfered, host->total_length); + data->bytes_xfered = data->blksz * data->blocks; + } + } +} + + +/*Handle after command sent ready*/ +static int at91_mci_handle_cmdrdy(struct at91mci_host *host) +{ + if (!host->cmd) + return 1; + else if (!host->cmd->data) { + if (host->flags & FL_SENT_STOP) { + /*After multi block write, we must wait for NOTBUSY*/ + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); + } else return 1; + } else if (host->cmd->data->flags & MMC_DATA_WRITE) { + /*After sendding multi-block-write command, start DMA transfer*/ + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE | AT91_MCI_BLKE); + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN); + } + + /* command not completed, have to wait */ + return 0; +} + + +/* + * Enable the controller + */ +static void at91_mci_enable(struct at91mci_host *host) +{ + unsigned int mr; + + at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); + at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); + at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC); + mr = AT91_MCI_PDCMODE | 0x34a; + + if (at91mci_is_mci1rev2xx()) + mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF; + + at91_mci_write(host, AT91_MCI_MR, mr); + + /* use Slot A or B (only one at same time) */ + at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b); +} + +/* + * Disable the controller + */ +static void at91_mci_disable(struct at91mci_host *host) +{ + at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST); +} + +/* + * Send a command + */ +static void 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; + + /* Needed for leaving busy state before CMD1 */ + if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) { + pr_debug("Clearing timeout\n"); + at91_mci_write(host, AT91_MCI_ARGR, 0); + at91_mci_write(host, AT91_MCI_CMDR, AT91_MCI_OPDCMD); + while (!(at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_CMDRDY)) { + /* spin */ + pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR)); + } + } + + 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) { + + if (cpu_is_at91rm9200() || cpu_is_at91sam9261()) { + if (data->blksz & 0x3) { + pr_debug("Unsupported block size\n"); + cmd->error = -EINVAL; + mmc_request_done(host->mmc, host->request); + return; + } + if (data->flags & MMC_DATA_STREAM) { + pr_debug("Stream commands not supported\n"); + cmd->error = -EINVAL; + mmc_request_done(host->mmc, host->request); + return; + } + } + + block_length = data->blksz; + 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 (cmd->opcode == SD_IO_RW_EXTENDED) { + cmdr |= AT91_MCI_TRTYP_SDIO_BLOCK; + } else { + if (data->flags & MMC_DATA_STREAM) + cmdr |= AT91_MCI_TRTYP_STREAM; + if (data->blocks > 1) + cmdr |= AT91_MCI_TRTYP_MULTIPLE; + } + } + else { + block_length = 0; + blocks = 0; + } + + if (host->flags & FL_SENT_STOP) + cmdr |= AT91_MCI_TRCMD_STOP; + + if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) + cmdr |= AT91_MCI_OPDCMD; + + /* + * Set the arguments and send the command + */ + pr_debug("Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08X)\n", + cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(host, AT91_MCI_MR)); + + if (!data) { + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS | ATMEL_PDC_RXTDIS); + at91_mci_write(host, ATMEL_PDC_RPR, 0); + at91_mci_write(host, ATMEL_PDC_RCR, 0); + at91_mci_write(host, ATMEL_PDC_RNPR, 0); + at91_mci_write(host, ATMEL_PDC_RNCR, 0); + at91_mci_write(host, ATMEL_PDC_TPR, 0); + at91_mci_write(host, ATMEL_PDC_TCR, 0); + at91_mci_write(host, ATMEL_PDC_TNPR, 0); + at91_mci_write(host, ATMEL_PDC_TNCR, 0); + ier = AT91_MCI_CMDRDY; + } else { + /* zero block length and PDC mode */ + mr = at91_mci_read(host, AT91_MCI_MR) & 0x5fff; + mr |= (data->blksz & 0x3) ? AT91_MCI_PDCFBYTE : 0; + mr |= (block_length << 16); + mr |= AT91_MCI_PDCMODE; + at91_mci_write(host, AT91_MCI_MR, mr); + + if (!(cpu_is_at91rm9200() || cpu_is_at91sam9261())) + at91_mci_write(host, AT91_MCI_BLKR, + AT91_MCI_BLKR_BCNT(blocks) | + AT91_MCI_BLKR_BLKLEN(block_length)); + + /* + * Disable the PDC controller + */ + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_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->total_length = 0; + + at91_mci_write(host, ATMEL_PDC_RPR, host->physical_address); + at91_mci_write(host, ATMEL_PDC_RCR, (data->blksz & 0x3) ? + (blocks * block_length) : (blocks * block_length) / 4); + at91_mci_write(host, ATMEL_PDC_RNPR, 0); + at91_mci_write(host, ATMEL_PDC_RNCR, 0); + + ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */; + } + else { + /* + * Handle a write + */ + host->total_length = block_length * blocks; + /* + * MCI1 rev2xx Data Write Operation and + * number of bytes erratum + */ + if (at91mci_is_mci1rev2xx()) + if (host->total_length < 12) + host->total_length = 12; + + at91_mci_sg_to_dma(host, data); + + pr_debug("Transmitting %d bytes\n", host->total_length); + + at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); + at91_mci_write(host, ATMEL_PDC_TCR, (data->blksz & 0x3) ? + host->total_length : host->total_length / 4); + + ier = AT91_MCI_CMDRDY; + } + } + } + + /* + * Send the command and then enable the PDC - not the other way round as + * the data sheet says + */ + + at91_mci_write(host, AT91_MCI_ARGR, cmd->arg); + at91_mci_write(host, AT91_MCI_CMDR, cmdr); + + if (cmdr & AT91_MCI_TRCMD_START) { + if (cmdr & AT91_MCI_TRDIR) + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN); + } + + /* Enable selected interrupts */ + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier); +} + +/* + * Process the next step in the request + */ +static void at91_mci_process_next(struct at91mci_host *host) +{ + if (!(host->flags & FL_SENT_COMMAND)) { + host->flags |= FL_SENT_COMMAND; + at91_mci_send_command(host, host->request->cmd); + } + else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) { + host->flags |= FL_SENT_STOP; + at91_mci_send_command(host, host->request->stop); + } else { + del_timer(&host->timer); + /* the at91rm9200 mci controller hangs after some transfers, + * and the workaround is to reset it after each transfer. + */ + if (cpu_is_at91rm9200()) + at91_reset_host(host); + mmc_request_done(host->mmc, host->request); + } +} + +/* + * Handle a command that has been completed + */ +static void at91_mci_completed_command(struct at91mci_host *host, unsigned int status) +{ + struct mmc_command *cmd = host->cmd; + struct mmc_data *data = cmd->data; + + at91_mci_write(host, AT91_MCI_IDR, 0xffffffff & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB)); + + cmd->resp[0] = at91_mci_read(host, AT91_MCI_RSPR(0)); + cmd->resp[1] = at91_mci_read(host, AT91_MCI_RSPR(1)); + cmd->resp[2] = at91_mci_read(host, AT91_MCI_RSPR(2)); + cmd->resp[3] = at91_mci_read(host, AT91_MCI_RSPR(3)); + + pr_debug("Status = %08X/%08x [%08X %08X %08X %08X]\n", + status, at91_mci_read(host, AT91_MCI_SR), + cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); + + if (status & AT91_MCI_ERRORS) { + if ((status & AT91_MCI_RCRCE) && !(mmc_resp_type(cmd) & MMC_RSP_CRC)) { + cmd->error = 0; + } + else { + if (status & (AT91_MCI_DTOE | AT91_MCI_DCRCE)) { + if (data) { + if (status & AT91_MCI_DTOE) + data->error = -ETIMEDOUT; + else if (status & AT91_MCI_DCRCE) + data->error = -EILSEQ; + } + } else { + if (status & AT91_MCI_RTOE) + cmd->error = -ETIMEDOUT; + else if (status & AT91_MCI_RCRCE) + cmd->error = -EILSEQ; + else + cmd->error = -EIO; + } + + pr_debug("Error detected and set to %d/%d (cmd = %d, retries = %d)\n", + cmd->error, data ? data->error : 0, + cmd->opcode, cmd->retries); + } + } + else + cmd->error = 0; + + at91_mci_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; + + /* more than 1s timeout needed with slow SD cards */ + mod_timer(&host->timer, jiffies + msecs_to_jiffies(2000)); + + at91_mci_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(host->mci_clk); + + host->bus_mode = ios->bus_mode; + + if (ios->clock == 0) { + /* Disable the MCI controller */ + at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS); + clkdiv = 0; + } + else { + /* Enable the MCI controller */ + at91_mci_write(host, 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; + + pr_debug("clkdiv = %d. mcck = %ld\n", clkdiv, + at91_master_clock / (2 * (clkdiv + 1))); + } + if (ios->bus_width == MMC_BUS_WIDTH_4 && host->board->wire4) { + pr_debug("MMC: Setting controller bus width to 4\n"); + at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) | AT91_MCI_SDCBUS); + } + else { + pr_debug("MMC: Setting controller bus width to 1\n"); + at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); + } + + /* Set the clock divider */ + at91_mci_write(host, AT91_MCI_MR, (at91_mci_read(host, AT91_MCI_MR) & ~AT91_MCI_CLKDIV) | clkdiv); + + /* maybe switch power to the card */ + if (host->board->vcc_pin) { + switch (ios->power_mode) { + case MMC_POWER_OFF: + gpio_set_value(host->board->vcc_pin, 0); + break; + case MMC_POWER_UP: + gpio_set_value(host->board->vcc_pin, 1); + break; + case MMC_POWER_ON: + break; + default: + WARN_ON(1); + } + } +} + +/* + * Handle an interrupt + */ +static irqreturn_t at91_mci_irq(int irq, void *devid) +{ + struct at91mci_host *host = devid; + int completed = 0; + unsigned int int_status, int_mask; + + int_status = at91_mci_read(host, AT91_MCI_SR); + int_mask = at91_mci_read(host, AT91_MCI_IMR); + + pr_debug("MCI irq: status = %08X, %08X, %08X\n", int_status, int_mask, + int_status & int_mask); + + int_status = int_status & int_mask; + + if (int_status & AT91_MCI_ERRORS) { + completed = 1; + + if (int_status & AT91_MCI_UNRE) + pr_debug("MMC: Underrun error\n"); + if (int_status & AT91_MCI_OVRE) + pr_debug("MMC: Overrun error\n"); + if (int_status & AT91_MCI_DTOE) + pr_debug("MMC: Data timeout\n"); + if (int_status & AT91_MCI_DCRCE) + pr_debug("MMC: CRC error in data\n"); + if (int_status & AT91_MCI_RTOE) + pr_debug("MMC: Response timeout\n"); + if (int_status & AT91_MCI_RENDE) + pr_debug("MMC: Response end bit error\n"); + if (int_status & AT91_MCI_RCRCE) + pr_debug("MMC: Response CRC error\n"); + if (int_status & AT91_MCI_RDIRE) + pr_debug("MMC: Response direction error\n"); + if (int_status & AT91_MCI_RINDE) + pr_debug("MMC: Response index error\n"); + } else { + /* Only continue processing if no errors */ + + if (int_status & AT91_MCI_TXBUFE) { + pr_debug("TX buffer empty\n"); + at91_mci_handle_transmitted(host); + } + + if (int_status & AT91_MCI_ENDRX) { + pr_debug("ENDRX\n"); + at91_mci_post_dma_read(host); + } + + if (int_status & AT91_MCI_RXBUFF) { + pr_debug("RX buffer full\n"); + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); + at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_RXBUFF | AT91_MCI_ENDRX); + completed = 1; + } + + if (int_status & AT91_MCI_ENDTX) + pr_debug("Transmit has ended\n"); + + if (int_status & AT91_MCI_NOTBUSY) { + pr_debug("Card is ready\n"); + at91_mci_update_bytes_xfered(host); + completed = 1; + } + + if (int_status & AT91_MCI_DTIP) + pr_debug("Data transfer in progress\n"); + + if (int_status & AT91_MCI_BLKE) { + pr_debug("Block transfer has ended\n"); + if (host->request->data && host->request->data->blocks > 1) { + /* multi block write : complete multi write + * command and send stop */ + completed = 1; + } else { + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); + } + } + + if (int_status & AT91_MCI_SDIOIRQA) + mmc_signal_sdio_irq(host->mmc); + + if (int_status & AT91_MCI_SDIOIRQB) + mmc_signal_sdio_irq(host->mmc); + + if (int_status & AT91_MCI_TXRDY) + pr_debug("Ready to transmit\n"); + + if (int_status & AT91_MCI_RXRDY) + pr_debug("Ready to receive\n"); + + if (int_status & AT91_MCI_CMDRDY) { + pr_debug("Command ready\n"); + completed = at91_mci_handle_cmdrdy(host); + } + } + + if (completed) { + pr_debug("Completed command\n"); + at91_mci_write(host, AT91_MCI_IDR, 0xffffffff & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB)); + at91_mci_completed_command(host, int_status); + } else + at91_mci_write(host, AT91_MCI_IDR, int_status & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB)); + + return IRQ_HANDLED; +} + +static irqreturn_t at91_mmc_det_irq(int irq, void *_host) +{ + struct at91mci_host *host = _host; + int present = !gpio_get_value(irq_to_gpio(irq)); + + /* + * we expect this irq on both insert and remove, + * and use a short delay to debounce. + */ + if (present != host->present) { + host->present = present; + pr_debug("%s: card %s\n", mmc_hostname(host->mmc), + present ? "insert" : "remove"); + if (!present) { + pr_debug("****** Resetting SD-card bus width ******\n"); + at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); + } + /* 0.5s needed because of early card detect switch firing */ + mmc_detect_change(host->mmc, msecs_to_jiffies(500)); + } + return IRQ_HANDLED; +} + +static int at91_mci_get_ro(struct mmc_host *mmc) +{ + struct at91mci_host *host = mmc_priv(mmc); + + if (host->board->wp_pin) + return !!gpio_get_value(host->board->wp_pin); + /* + * Board doesn't support read only detection; let the mmc core + * decide what to do. + */ + return -ENOSYS; +} + +static void at91_mci_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct at91mci_host *host = mmc_priv(mmc); + + pr_debug("%s: sdio_irq %c : %s\n", mmc_hostname(host->mmc), + host->board->slot_b ? 'B':'A', enable ? "enable" : "disable"); + at91_mci_write(host, enable ? AT91_MCI_IER : AT91_MCI_IDR, + host->board->slot_b ? AT91_MCI_SDIOIRQB : AT91_MCI_SDIOIRQA); + +} + +static const struct mmc_host_ops at91_mci_ops = { + .request = at91_mci_request, + .set_ios = at91_mci_set_ios, + .get_ro = at91_mci_get_ro, + .enable_sdio_irq = at91_mci_enable_sdio_irq, +}; + +/* + * Probe for the device + */ +static int __init at91_mci_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct at91mci_host *host; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) + return -EBUSY; + + mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + dev_dbg(&pdev->dev, "couldn't allocate mmc host\n"); + goto fail6; + } + + mmc->ops = &at91_mci_ops; + mmc->f_min = 375000; + mmc->f_max = 25000000; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->caps = 0; + + mmc->max_blk_size = MCI_MAXBLKSIZE; + mmc->max_blk_count = MCI_BLKATONCE; + mmc->max_req_size = MCI_BUFSIZE; + mmc->max_segs = MCI_BLKATONCE; + mmc->max_seg_size = MCI_BUFSIZE; + + host = mmc_priv(mmc); + host->mmc = mmc; + host->bus_mode = 0; + host->board = pdev->dev.platform_data; + if (host->board->wire4) { + if (at91mci_is_mci1rev2xx()) + mmc->caps |= MMC_CAP_4_BIT_DATA; + else + dev_warn(&pdev->dev, "4 wire bus mode not supported" + " - using 1 wire\n"); + } + + host->buffer = dma_alloc_coherent(&pdev->dev, MCI_BUFSIZE, + &host->physical_address, GFP_KERNEL); + if (!host->buffer) { + ret = -ENOMEM; + dev_err(&pdev->dev, "Can't allocate transmit buffer\n"); + goto fail5; + } + + /* Add SDIO capability when available */ + if (at91mci_is_mci1rev2xx()) { + /* at91mci MCI1 rev2xx sdio interrupt erratum */ + if (host->board->wire4 || !host->board->slot_b) + mmc->caps |= MMC_CAP_SDIO_IRQ; + } + + /* + * Reserve GPIOs ... board init code makes sure these pins are set + * up as GPIOs with the right direction (input, except for vcc) + */ + if (host->board->det_pin) { + ret = gpio_request(host->board->det_pin, "mmc_detect"); + if (ret < 0) { + dev_dbg(&pdev->dev, "couldn't claim card detect pin\n"); + goto fail4b; + } + } + if (host->board->wp_pin) { + ret = gpio_request(host->board->wp_pin, "mmc_wp"); + if (ret < 0) { + dev_dbg(&pdev->dev, "couldn't claim wp sense pin\n"); + goto fail4; + } + } + if (host->board->vcc_pin) { + ret = gpio_request(host->board->vcc_pin, "mmc_vcc"); + if (ret < 0) { + dev_dbg(&pdev->dev, "couldn't claim vcc switch pin\n"); + goto fail3; + } + } + + /* + * Get Clock + */ + host->mci_clk = clk_get(&pdev->dev, "mci_clk"); + if (IS_ERR(host->mci_clk)) { + ret = -ENODEV; + dev_dbg(&pdev->dev, "no mci_clk?\n"); + goto fail2; + } + + /* + * Map I/O region + */ + host->baseaddr = ioremap(res->start, resource_size(res)); + if (!host->baseaddr) { + ret = -ENOMEM; + goto fail1; + } + + /* + * Reset hardware + */ + clk_enable(host->mci_clk); /* Enable the peripheral clock */ + at91_mci_disable(host); + at91_mci_enable(host); + + /* + * Allocate the MCI interrupt + */ + host->irq = platform_get_irq(pdev, 0); + ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED, + mmc_hostname(mmc), host); + if (ret) { + dev_dbg(&pdev->dev, "request MCI interrupt failed\n"); + goto fail0; + } + + setup_timer(&host->timer, at91_timeout_timer, (unsigned long)host); + + platform_set_drvdata(pdev, mmc); + + /* + * Add host to MMC layer + */ + if (host->board->det_pin) { + host->present = !gpio_get_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(gpio_to_irq(host->board->det_pin), + at91_mmc_det_irq, 0, mmc_hostname(mmc), host); + if (ret) + dev_warn(&pdev->dev, "request MMC detect irq failed\n"); + else + device_init_wakeup(&pdev->dev, 1); + } + + pr_debug("Added MCI driver\n"); + + return 0; + +fail0: + clk_disable(host->mci_clk); + iounmap(host->baseaddr); +fail1: + clk_put(host->mci_clk); +fail2: + if (host->board->vcc_pin) + gpio_free(host->board->vcc_pin); +fail3: + if (host->board->wp_pin) + gpio_free(host->board->wp_pin); +fail4: + if (host->board->det_pin) + gpio_free(host->board->det_pin); +fail4b: + if (host->buffer) + dma_free_coherent(&pdev->dev, MCI_BUFSIZE, + host->buffer, host->physical_address); +fail5: + mmc_free_host(mmc); +fail6: + release_mem_region(res->start, resource_size(res)); + dev_err(&pdev->dev, "probe failed, err %d\n", ret); + return ret; +} + +/* + * Remove a device + */ +static int __exit at91_mci_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct at91mci_host *host; + struct resource *res; + + if (!mmc) + return -1; + + host = mmc_priv(mmc); + + if (host->buffer) + dma_free_coherent(&pdev->dev, MCI_BUFSIZE, + host->buffer, host->physical_address); + + if (host->board->det_pin) { + if (device_can_wakeup(&pdev->dev)) + free_irq(gpio_to_irq(host->board->det_pin), host); + device_init_wakeup(&pdev->dev, 0); + gpio_free(host->board->det_pin); + } + + at91_mci_disable(host); + del_timer_sync(&host->timer); + mmc_remove_host(mmc); + free_irq(host->irq, host); + + clk_disable(host->mci_clk); /* Disable the peripheral clock */ + clk_put(host->mci_clk); + + if (host->board->vcc_pin) + gpio_free(host->board->vcc_pin); + if (host->board->wp_pin) + gpio_free(host->board->wp_pin); + + iounmap(host->baseaddr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + mmc_free_host(mmc); + platform_set_drvdata(pdev, NULL); + pr_debug("MCI 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); + struct at91mci_host *host = mmc_priv(mmc); + int ret = 0; + + if (host->board->det_pin && device_may_wakeup(&pdev->dev)) + enable_irq_wake(host->board->det_pin); + + if (mmc) + ret = mmc_suspend_host(mmc); + + return ret; +} + +static int at91_mci_resume(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct at91mci_host *host = mmc_priv(mmc); + int ret = 0; + + if (host->board->det_pin && device_may_wakeup(&pdev->dev)) + disable_irq_wake(host->board->det_pin); + + 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 = { + .remove = __exit_p(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_probe(&at91_mci_driver, at91_mci_probe); +} + +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"); +MODULE_ALIAS("platform:at91_mci"); diff --git a/drivers/mmc/host/at91_mci.h b/drivers/mmc/host/at91_mci.h new file mode 100644 index 00000000000..eec3a6b1c2b --- /dev/null +++ b/drivers/mmc/host/at91_mci.h @@ -0,0 +1,115 @@ +/* + * drivers/mmc/host/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 (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_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/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c new file mode 100644 index 00000000000..881f7ba545a --- /dev/null +++ b/drivers/mmc/host/imxmmc.c @@ -0,0 +1,1169 @@ +/* + * linux/drivers/mmc/host/imxmmc.c - Motorola i.MX MMCI driver + * + * Copyright (C) 2004 Sascha Hauer, Pengutronix + * Copyright (C) 2006 Pavel Pisa, PiKRON + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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; + void __iomem *base; + int irq; + imx_dmach_t dma; + 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 two 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; + + int prev_cmd_code; + + struct clk *clk; +}; + +#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_STARTED_b 8 + +#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) +#define IMXMCI_PEND_STARTED_m (1 << IMXMCI_PEND_STARTED_b) + +static void imxmci_stop_clock(struct imxmci_host *host) +{ + int i = 0; + u16 reg; + + reg = readw(host->base + MMC_REG_STR_STP_CLK); + writew(reg & ~STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK); + while (i < 0x1000) { + if (!(i & 0x7f)) { + reg = readw(host->base + MMC_REG_STR_STP_CLK); + writew(reg | STR_STP_CLK_STOP_CLK, + host->base + MMC_REG_STR_STP_CLK); + } + + reg = readw(host->base + MMC_REG_STATUS); + if (!(reg & STATUS_CARD_BUS_CLK_RUN)) { + /* Check twice before cut */ + reg = readw(host->base + MMC_REG_STATUS); + if (!(reg & STATUS_CARD_BUS_CLK_RUN)) + return; + } + + i++; + } + dev_dbg(mmc_dev(host->mmc), "imxmci_stop_clock blocked, no luck\n"); +} + +static int imxmci_start_clock(struct imxmci_host *host) +{ + unsigned int trials = 0; + unsigned int delay_limit = 128; + unsigned long flags; + u16 reg; + + reg = readw(host->base + MMC_REG_STR_STP_CLK); + writew(reg & ~STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK); + + clear_bit(IMXMCI_PEND_STARTED_b, &host->pending_events); + + /* + * Command start of the clock, this usually succeeds in less + * then 6 delay loops, but during card detection (low clockrate) + * it takes up to 5000 delay loops and sometimes fails for the first time + */ + reg = readw(host->base + MMC_REG_STR_STP_CLK); + writew(reg | STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK); + + do { + unsigned int delay = delay_limit; + + while (delay--) { + reg = readw(host->base + MMC_REG_STATUS); + if (reg & STATUS_CARD_BUS_CLK_RUN) { + /* Check twice before cut */ + reg = readw(host->base + MMC_REG_STATUS); + if (reg & STATUS_CARD_BUS_CLK_RUN) + return 0; + } + + if (test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events)) + return 0; + } + + local_irq_save(flags); + /* + * Ensure, that request is not doubled under all possible circumstances. + * It is possible, that cock running state is missed, because some other + * IRQ or schedule delays this function execution and the clocks has + * been already stopped by other means (response processing, SDHC HW) + */ + if (!test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events)) { + reg = readw(host->base + MMC_REG_STR_STP_CLK); + writew(reg | STR_STP_CLK_START_CLK, + host->base + MMC_REG_STR_STP_CLK); + } + local_irq_restore(flags); + + } while (++trials < 256); + + dev_err(mmc_dev(host->mmc), "imxmci_start_clock blocked, no luck\n"); + + return -1; +} + +static void imxmci_softreset(struct imxmci_host *host) +{ + int i; + + /* reset sequence */ + writew(0x08, host->base + MMC_REG_STR_STP_CLK); + writew(0x0D, host->base + MMC_REG_STR_STP_CLK); + + for (i = 0; i < 8; i++) + writew(0x05, host->base + MMC_REG_STR_STP_CLK); + + writew(0xff, host->base + MMC_REG_RES_TO); + writew(512, host->base + MMC_REG_BLK_LEN); + writew(1, host->base + MMC_REG_NOB); +} + +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 |= readw(host->base + MMC_REG_STATUS); + } + if (!loops) + return 0; + + /* The busy-wait is expected there for clock <8MHz due to SDHC hardware flaws */ + if (!(stat_mask & STATUS_END_CMD_RESP) || (host->mmc->ios.clock >= 8000000)) + 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 = data->blksz; + unsigned int datasz = nob * blksz; + int i; + + if (data->flags & MMC_DATA_STREAM) + nob = 0xffff; + + host->data = data; + data->bytes_xfered = 0; + + writew(nob, host->base + MMC_REG_NOB); + writew(blksz, host->base + MMC_REG_BLK_LEN); + + /* + * 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 < 512) { + host->dma_size = datasz; + if (data->flags & MMC_DATA_READ) { + host->dma_dir = DMA_FROM_DEVICE; + + /* Hack to enable read SCR */ + writew(1, host->base + MMC_REG_NOB); + writew(512, host->base + MMC_REG_BLK_LEN); + } else { + host->dma_dir = DMA_TO_DEVICE; + } + + /* Convert back to virtual address */ + host->data_ptr = (u16 *)sg_virt(data->sg); + 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_REG_BUFFER_ACCESS, + 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_REG_BUFFER_ACCESS, + 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(); + + 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; + + /* Ensure, that clock are stopped else command programming and start fails */ + imxmci_stop_clock(host); + + 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; + 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; + + writew(cmd->opcode, host->base + MMC_REG_CMD); + writew(cmd->arg >> 16, host->base + MMC_REG_ARGH); + writew(cmd->arg & 0xffff, host->base + MMC_REG_ARGL); + writew(cmdat, host->base + MMC_REG_CMD_DAT_CONT); + + 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; + writew(host->imask, host->base + MMC_REG_INT_MASK); + 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; + writew(host->imask, host->base + MMC_REG_INT_MASK); + + spin_unlock_irqrestore(&host->lock, flags); + + if (req && req->cmd) + host->prev_cmd_code = req->cmd->opcode; + + 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 = -EILSEQ; + else if (stat & STATUS_TIME_OUT_READ) + data->error = -ETIMEDOUT; + else + data->error = -EIO; + } 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 = -ETIMEDOUT; + } else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) { + dev_dbg(mmc_dev(host->mmc), "cmd crc error\n"); + cmd->error = -EILSEQ; + } + + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) { + for (i = 0; i < 4; i++) { + a = readw(host->base + MMC_REG_RES_FIFO); + b = readw(host->base + MMC_REG_RES_FIFO); + cmd->resp[i] = a << 16 | b; + } + } else { + a = readw(host->base + MMC_REG_RES_FIFO); + b = readw(host->base + MMC_REG_RES_FIFO); + c = readw(host->base + MMC_REG_RES_FIFO); + 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 && !(stat & STATUS_ERR_MASK)) { + if (host->req->data->flags & MMC_DATA_WRITE) { + + /* Wait for FIFO to be empty before starting DMA write */ + + stat = readw(host->base + MMC_REG_STATUS); + if (imxmci_busy_wait_for_status(host, &stat, + STATUS_APPL_BUFF_FE, + 40, "imxmci_cmd_done DMA WR") < 0) { + cmd->error = -EIO; + 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) { + 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 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); + + udelay(20); /* required for clocks < 8MHz*/ + + if (host->dma_dir == DMA_FROM_DEVICE) { + imxmci_busy_wait_for_status(host, &stat, + STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE | + STATUS_TIME_OUT_READ, + 50, "imxmci_cpu_driven_data read"); + + while ((stat & (STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE)) && + !(stat & STATUS_TIME_OUT_READ) && + (host->data_cnt < 512)) { + + udelay(20); /* required for clocks < 8MHz*/ + + for (i = burst_len; i >= 2 ; i -= 2) { + u16 data; + data = readw(host->base + MMC_REG_BUFFER_ACCESS); + udelay(10); /* required for clocks < 8MHz*/ + if (host->data_cnt+2 <= host->dma_size) { + *(host->data_ptr++) = data; + } else { + if (host->data_cnt < host->dma_size) + *(u8 *)(host->data_ptr) = data; + } + host->data_cnt += 2; + } + + stat = readw(host->base + MMC_REG_STATUS); + + dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read %d burst %d STATUS = 0x%x\n", + host->data_cnt, burst_len, stat); + } + + if ((stat & STATUS_DATA_TRANS_DONE) && (host->data_cnt >= 512)) + trans_done = 1; + + if (host->dma_size & 0x1ff) + stat &= ~STATUS_CRC_READ_ERR; + + if (stat & STATUS_TIME_OUT_READ) { + dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read timeout STATUS = 0x%x\n", + stat); + trans_done = -1; + } + + } 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) + writew(*(host->data_ptr++), host->base + MMC_REG_BUFFER_ACCESS); + + stat = readw(host->base + MMC_REG_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 imxmci_host *host = devid; + u32 stat = readw(host->base + MMC_REG_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 imxmci_host *host = devid; + u32 stat = readw(host->base + MMC_REG_STATUS); + int handled = 1; + + writew(host->imask | INT_MASK_SDIO | INT_MASK_AUTO_CARD_DETECT, + host->base + MMC_REG_INT_MASK); + + atomic_set(&host->stuck_timeout, 0); + host->status_reg = stat; + set_bit(IMXMCI_PEND_IRQ_b, &host->pending_events); + set_bit(IMXMCI_PEND_STARTED_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 = readw(host->base + MMC_REG_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, + readw(host->base + MMC_REG_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", + readw(host->base + MMC_REG_CMD_DAT_CONT), + readw(host->base + MMC_REG_BLK_LEN), + readw(host->base + MMC_REG_NOB), + CCR(host->dma)); + dev_err(mmc_dev(host->mmc), "CMD%d, prevCMD%d, bus %d-bit, dma_size = 0x%x\n", + host->cmd ? host->cmd->opcode : 0, + host->prev_cmd_code, + 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 = readw(host->base + MMC_REG_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_CPU_DATA_b, &host->pending_events)) + stat &= ~STATUS_CRC_READ_ERR; + + 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 |= readw(host->base + MMC_REG_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 = readw(host->base + MMC_REG_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; + + 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; + + if (ios->bus_width == MMC_BUS_WIDTH_4) { + host->actual_bus_width = MMC_BUS_WIDTH_4; + imx_gpio_mode(PB11_PF_SD_DAT3); + BLR(host->dma) = 0; /* burst 64 byte read/write */ + } else { + host->actual_bus_width = MMC_BUS_WIDTH_1; + imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11); + BLR(host->dma) = 16; /* burst 16 byte read/write */ + } + + 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; + u16 reg; + + /* The prescaler is 5 for PERCLK2 equal to 96MHz + * then 96MHz / 5 = 19.2 MHz + */ + clk = clk_get_rate(host->clk); + 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; + } + + /* enable controller */ + reg = readw(host->base + MMC_REG_STR_STP_CLK); + writew(reg | STR_STP_CLK_ENABLE, + host->base + MMC_REG_STR_STP_CLK); + + imxmci_stop_clock(host); + writew((prescaler << 3) | clk, host->base + MMC_REG_CLK_RATE); + /* + * Under my understanding, clock should not be started there, because it would + * initiate SDHC sequencer and send last or random command into card + */ + /* imxmci_start_clock(host); */ + + dev_dbg(mmc_dev(host->mmc), + "MMC_CLK_RATE: 0x%08x\n", + readw(host->base + MMC_REG_CLK_RATE)); + } else { + imxmci_stop_clock(host); + } +} + +static int imxmci_get_ro(struct mmc_host *mmc) +{ + struct imxmci_host *host = mmc_priv(mmc); + + if (host->pdata && host->pdata->get_ro) + return !!host->pdata->get_ro(mmc_dev(mmc)); + /* + * Board doesn't support read only detection; let the mmc core + * decide what to do. + */ + return -ENOSYS; +} + + +static const struct mmc_host_ops imxmci_ops = { + .request = imxmci_request, + .set_ios = imxmci_set_ios, + .get_ro = imxmci_get_ro, +}; + +static void imxmci_check_status(unsigned long data) +{ + struct imxmci_host *host = (struct imxmci_host *)data; + + if (host->pdata && host->pdata->card_present && + host->pdata->card_present(mmc_dev(host->mmc)) != 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 __init imxmci_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct imxmci_host *host = NULL; + struct resource *r; + int ret = 0, irq; + u16 rev_no; + + printk(KERN_INFO "i.MX mmc driver\n"); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!r || irq < 0) + return -ENXIO; + + r = request_mem_region(r->start, resource_size(r), pdev->name); + 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_segs = 64; + mmc->max_seg_size = 64*512; /* default PAGE_CACHE_SIZE */ + mmc->max_req_size = 64*512; /* default PAGE_CACHE_SIZE */ + mmc->max_blk_size = 2048; + mmc->max_blk_count = 65535; + + host = mmc_priv(mmc); + host->base = ioremap(r->start, resource_size(r)); + if (!host->base) { + ret = -ENOMEM; + goto out; + } + + host->mmc = mmc; + host->dma_allocated = 0; + host->pdata = pdev->dev.platform_data; + if (!host->pdata) + dev_warn(&pdev->dev, "No platform data provided!\n"); + + spin_lock_init(&host->lock); + host->res = r; + host->irq = irq; + + host->clk = clk_get(&pdev->dev, "perclk2"); + if (IS_ERR(host->clk)) { + ret = PTR_ERR(host->clk); + goto out; + } + clk_enable(host->clk); + + 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(host); + + rev_no = readw(host->base + MMC_REG_REV_NO); + if (rev_no != 0x390) { + dev_err(mmc_dev(host->mmc), "wrong rev.no. 0x%08x. aborting.\n", + readw(host->base + MMC_REG_REV_NO)); + goto out; + } + + /* recommended in data sheet */ + writew(0x2db4, host->base + MMC_REG_READ_TO); + + host->imask = IMXMCI_INT_MASK_DEFAULT; + writew(host->imask, host->base + MMC_REG_INT_MASK); + + host->dma = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_LOW); + if(host->dma < 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); + RSSR(host->dma) = DMA_REQ_SDHC; + + 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; + + if (host->pdata && host->pdata->card_present) + host->present = host->pdata->card_present(mmc_dev(mmc)); + else /* if there is no way to detect assume that card is present */ + host->present = 1; + + 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 (host->clk) { + clk_disable(host->clk); + clk_put(host->clk); + } + if (host->base) + iounmap(host->base); + } + if (mmc) + mmc_free_host(mmc); + release_mem_region(r->start, resource_size(r)); + return ret; +} + +static int __exit 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); + iounmap(host->base); + if (host->dma_allocated) { + imx_dma_free(host->dma); + host->dma_allocated = 0; + } + + tasklet_kill(&host->tasklet); + + clk_disable(host->clk); + clk_put(host->clk); + + release_mem_region(host->res->start, resource_size(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); + + 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 = { + .remove = __exit_p(imxmci_remove), + .suspend = imxmci_suspend, + .resume = imxmci_resume, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + } +}; + +static int __init imxmci_init(void) +{ + return platform_driver_probe(&imxmci_driver, imxmci_probe); +} + +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"); +MODULE_ALIAS("platform:imx-mmc"); diff --git a/drivers/mmc/host/imxmmc.h b/drivers/mmc/host/imxmmc.h new file mode 100644 index 00000000000..09d5d4ee3a7 --- /dev/null +++ b/drivers/mmc/host/imxmmc.h @@ -0,0 +1,64 @@ +#define MMC_REG_STR_STP_CLK 0x00 +#define MMC_REG_STATUS 0x04 +#define MMC_REG_CLK_RATE 0x08 +#define MMC_REG_CMD_DAT_CONT 0x0C +#define MMC_REG_RES_TO 0x10 +#define MMC_REG_READ_TO 0x14 +#define MMC_REG_BLK_LEN 0x18 +#define MMC_REG_NOB 0x1C +#define MMC_REG_REV_NO 0x20 +#define MMC_REG_INT_MASK 0x24 +#define MMC_REG_CMD 0x28 +#define MMC_REG_ARGH 0x2C +#define MMC_REG_ARGL 0x30 +#define MMC_REG_RES_FIFO 0x34 +#define MMC_REG_BUFFER_ACCESS 0x38 + +#define STR_STP_CLK_IPG_CLK_GATE_DIS (1<<15) +#define STR_STP_CLK_IPG_PERCLK_GATE_DIS (1<<14) +#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) -- cgit v1.2.2