V4L/DVB (8682): V4L: fix return value of register video func
[safe/jmp/linux-2.6] / drivers / mmc / host / at91_mci.c
index fce171c..6915f40 100644 (file)
@@ -73,9 +73,9 @@
 #include <asm/gpio.h>
 
 #include <asm/mach/mmc.h>
-#include <asm/arch/board.h>
-#include <asm/arch/cpu.h>
-#include <asm/arch/at91_mci.h>
+#include <mach/board.h>
+#include <mach/cpu.h>
+#include <mach/at91_mci.h>
 
 #define DRIVER_NAME "at91_mci"
 
@@ -130,6 +130,43 @@ struct at91mci_host
        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;
@@ -148,6 +185,7 @@ static void at91_timeout_timer(unsigned long data)
                                host->request->cmd->error = -ETIMEDOUT;
                }
 
+               at91_reset_host(host);
                mmc_request_done(host->mmc, host->request);
        }
 }
@@ -160,9 +198,14 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data
        unsigned int len, i, size;
        unsigned *dmabuf = host->buffer;
 
-       size = host->total_length;
+       size = data->blksz * data->blocks;
        len = data->sg_len;
 
+       /* AT91SAM926[0/3] Data Write Operation and number of bytes erratum */
+       if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
+               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
@@ -184,9 +227,10 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data
 
                        for (index = 0; index < (amount / 4); index++)
                                *dmabuf++ = swab32(sgbuffer[index]);
-               }
-               else
+               } else {
                        memcpy(dmabuf, sgbuffer, amount);
+                       dmabuf += amount;
+               }
 
                kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ);
 
@@ -302,8 +346,6 @@ static void at91_mci_post_dma_read(struct at91mci_host *host)
 
                dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE);
 
-               data->bytes_xfered += sg->length;
-
                if (cpu_is_at91rm9200()) {      /* AT91RM9200 errata */
                        unsigned int *buffer;
                        int index;
@@ -319,6 +361,8 @@ static void at91_mci_post_dma_read(struct at91mci_host *host)
                }
 
                flush_dcache_page(sg_page(sg));
+
+               data->bytes_xfered += sg->length;
        }
 
        /* Is there another transfer to trigger? */
@@ -359,10 +403,32 @@ static void at91_mci_handle_transmitted(struct at91mci_host *host)
                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;
 
-       data->bytes_xfered = host->total_length;
+       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)
 {
@@ -375,8 +441,7 @@ static int at91_mci_handle_cmdrdy(struct at91mci_host *host)
                } 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_write(host, AT91_MCI_IER, AT91_MCI_BLKE);
+               at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE | AT91_MCI_BLKE);
                at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
        }
 
@@ -455,11 +520,19 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
 
        if (data) {
 
-               if ( cpu_is_at91rm9200() && (data->blksz & 0x3) ) {
-                       pr_debug("Unsupported block size\n");
-                       cmd->error = -EINVAL;
-                       mmc_request_done(host->mmc, host->request);
-                       return;
+               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;
@@ -506,12 +579,17 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
                ier = AT91_MCI_CMDRDY;
        } else {
                /* zero block length and PDC mode */
-               mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff;
+               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
                 */
@@ -536,6 +614,13 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
                                 * Handle a write
                                 */
                                host->total_length = block_length * blocks;
+                               /*
+                                * AT91SAM926[0/3] Data Write Operation and
+                                * number of bytes erratum
+                                */
+                               if (cpu_is_at91sam9260 () || cpu_is_at91sam9263())
+                                       if (host->total_length < 12)
+                                               host->total_length = 12;
                                host->buffer = dma_alloc_coherent(NULL,
                                                host->total_length,
                                                &host->physical_address, GFP_KERNEL);
@@ -584,6 +669,11 @@ static void at91_mci_process_next(struct at91mci_host *host)
                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);
        }
 }
@@ -591,12 +681,12 @@ static void at91_mci_process_next(struct at91mci_host *host)
 /*
  * Handle a command that has been completed
  */
-static void at91_mci_completed_command(struct at91mci_host *host)
+static void at91_mci_completed_command(struct at91mci_host *host, unsigned int status)
 {
        struct mmc_command *cmd = host->cmd;
-       unsigned int status;
+       struct mmc_data *data = cmd->data;
 
-       at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
+       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));
@@ -608,25 +698,34 @@ static void at91_mci_completed_command(struct at91mci_host *host)
                host->buffer = NULL;
        }
 
-       status = at91_mci_read(host, AT91_MCI_SR);
-
-       pr_debug("Status = %08X [%08X %08X %08X %08X]\n",
-                status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[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_RTOE | AT91_MCI_DTOE))
-                               cmd->error = -ETIMEDOUT;
-                       else if (status & (AT91_MCI_RCRCE | AT91_MCI_DCRCE))
-                               cmd->error = -EILSEQ;
-                       else
-                               cmd->error = -EIO;
+                       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 (cmd = %d, retries = %d)\n",
-                                cmd->error, cmd->opcode, cmd->retries);
+                       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
@@ -769,6 +868,7 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)
 
                if (int_status & AT91_MCI_NOTBUSY) {
                        pr_debug("Card is ready\n");
+                       at91_mci_update_bytes_xfered(host);
                        completed = 1;
                }
 
@@ -777,9 +877,21 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)
 
                if (int_status & AT91_MCI_BLKE) {
                        pr_debug("Block transfer has ended\n");
-                       completed = 1;
+                       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");
 
@@ -794,10 +906,10 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)
 
        if (completed) {
                pr_debug("Completed command\n");
-               at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
-               at91_mci_completed_command(host);
+               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_write(host, AT91_MCI_IDR, int_status & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB));
 
        return IRQ_HANDLED;
 }
@@ -837,10 +949,22 @@ static int at91_mci_get_ro(struct mmc_host *mmc)
        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,
 };
 
 /*
@@ -871,6 +995,7 @@ static int __init at91_mci_probe(struct platform_device *pdev)
        mmc->f_min = 375000;
        mmc->f_max = 25000000;
        mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+       mmc->caps = MMC_CAP_SDIO_IRQ;
 
        mmc->max_blk_size = 4095;
        mmc->max_blk_count = mmc->max_req_size;