]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/mmc/host/at91_mci.c
mmc: at91_mci: do not read irq status twice as it will forget some errors
[karo-tx-linux.git] / drivers / mmc / host / at91_mci.c
index b1edcefdd4f9e7a615e2e38238f4d43e8456a9b4..e1f91a42d521002a99d9c6e623a15636aea293cb 100644 (file)
 
 #include <asm/io.h>
 #include <asm/irq.h>
+#include <asm/gpio.h>
+
 #include <asm/mach/mmc.h>
 #include <asm/arch/board.h>
 #include <asm/arch/cpu.h>
-#include <asm/arch/gpio.h>
 #include <asm/arch/at91_mci.h>
 
 #define DRIVER_NAME "at91_mci"
@@ -124,8 +125,71 @@ struct at91mci_host
 
        /* 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
  */
@@ -232,11 +296,11 @@ static void at91_mci_pre_dma_read(struct at91mci_host *host)
 
                if (i == 0) {
                        at91_mci_write(host, ATMEL_PDC_RPR, sg->dma_address);
-                       at91_mci_write(host, ATMEL_PDC_RCR, sg->length / 4);
+                       at91_mci_write(host, ATMEL_PDC_RCR, (data->blksz & 0x3) ? sg->length : sg->length / 4);
                }
                else {
                        at91_mci_write(host, ATMEL_PDC_RNPR, sg->dma_address);
-                       at91_mci_write(host, ATMEL_PDC_RNCR, sg->length / 4);
+                       at91_mci_write(host, ATMEL_PDC_RNCR, (data->blksz & 0x3) ? sg->length : sg->length / 4);
                }
        }
 
@@ -276,8 +340,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;
@@ -293,6 +355,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? */
@@ -333,10 +397,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 = host->total_length;
+               }
+       }
 }
 
+
 /*Handle after command sent ready*/
 static int at91_mci_handle_cmdrdy(struct at91mci_host *host)
 {
@@ -349,8 +435,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);
        }
 
@@ -429,7 +514,7 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
 
        if (data) {
 
-               if ( data->blksz & 0x3 ) {
+               if ( cpu_is_at91rm9200() && (data->blksz & 0x3) ) {
                        pr_debug("Unsupported block size\n");
                        cmd->error = -EINVAL;
                        mmc_request_done(host->mmc, host->request);
@@ -481,7 +566,15 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
        } else {
                /* zero block length and PDC mode */
                mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff;
-               at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE);
+               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())
+                       at91_mci_write(host, AT91_MCI_BLKR,
+                               AT91_MCI_BLKR_BCNT(blocks) |
+                               AT91_MCI_BLKR_BLKLEN(block_length));
 
                /*
                 * Disable the PDC controller
@@ -516,7 +609,9 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
                                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, host->total_length / 4);
+                               at91_mci_write(host, ATMEL_PDC_TCR, (data->blksz & 0x3) ?
+                                               host->total_length : host->total_length / 4);
+
                                ier = AT91_MCI_CMDRDY;
                        }
                }
@@ -551,20 +646,25 @@ static void at91_mci_process_next(struct at91mci_host *host)
        else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) {
                host->flags |= FL_SENT_STOP;
                at91_mci_send_command(host, host->request->stop);
-       }
-       else
+       } 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)
+static void at91_mci_completed_command(struct at91mci_host *host, unsigned int status)
 {
        struct mmc_command *cmd = host->cmd;
-       unsigned int status;
 
-       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));
@@ -576,10 +676,9 @@ 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)) {
@@ -612,6 +711,8 @@ static void at91_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
        host->request = mrq;
        host->flags = 0;
 
+       mod_timer(&host->timer, jiffies +  HZ);
+
        at91_mci_process_next(host);
 }
 
@@ -659,12 +760,15 @@ static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        if (host->board->vcc_pin) {
                switch (ios->power_mode) {
                        case MMC_POWER_OFF:
-                               at91_set_gpio_value(host->board->vcc_pin, 0);
+                               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:
-                               at91_set_gpio_value(host->board->vcc_pin, 1);
                                break;
+                       default:
+                               WARN_ON(1);
                }
        }
 }
@@ -732,6 +836,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;
                }
 
@@ -740,9 +845,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");
 
@@ -757,10 +874,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;
 }
@@ -768,7 +885,7 @@ static irqreturn_t at91_mci_irq(int irq, void *devid)
 static irqreturn_t at91_mmc_det_irq(int irq, void *_host)
 {
        struct at91mci_host *host = _host;
-       int present = !at91_get_gpio_value(irq);
+       int present = !gpio_get_value(irq_to_gpio(irq));
 
        /*
         * we expect this irq on both insert and remove,
@@ -789,25 +906,33 @@ static irqreturn_t at91_mmc_det_irq(int irq, void *_host)
 
 static 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;
+       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,
 };
 
 /*
@@ -820,8 +945,6 @@ static int __init at91_mci_probe(struct platform_device *pdev)
        struct resource *res;
        int ret;
 
-       pr_debug("Probe MCI devices\n");
-
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res)
                return -ENXIO;
@@ -831,15 +954,16 @@ static int __init at91_mci_probe(struct platform_device *pdev)
 
        mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev);
        if (!mmc) {
-               pr_debug("Failed to allocate mmc host\n");
-               release_mem_region(res->start, res->end - res->start + 1);
-               return -ENOMEM;
+               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 = MMC_CAP_MULTIWRITE | MMC_CAP_SDIO_IRQ;
 
        mmc->max_blk_size = 4095;
        mmc->max_blk_count = mmc->max_req_size;
@@ -853,19 +977,44 @@ static int __init at91_mci_probe(struct platform_device *pdev)
                if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
                        mmc->caps |= MMC_CAP_4_BIT_DATA;
                else
-                       printk("AT91 MMC: 4 wire bus mode not supported"
+                       dev_warn(&pdev->dev, "4 wire bus mode not supported"
                                " - using 1 wire\n");
        }
 
+       /*
+        * 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 fail5;
+               }
+       }
+       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)) {
-               printk(KERN_ERR "AT91 MMC: no clock defined.\n");
-               mmc_free_host(mmc);
-               release_mem_region(res->start, res->end - res->start + 1);
-               return -ENODEV;
+               ret = -ENODEV;
+               dev_dbg(&pdev->dev, "no mci_clk?\n");
+               goto fail2;
        }
 
        /*
@@ -873,10 +1022,8 @@ static int __init at91_mci_probe(struct platform_device *pdev)
         */
        host->baseaddr = ioremap(res->start, res->end - res->start + 1);
        if (!host->baseaddr) {
-               clk_put(host->mci_clk);
-               mmc_free_host(mmc);
-               release_mem_region(res->start, res->end - res->start + 1);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto fail1;
        }
 
        /*
@@ -890,15 +1037,11 @@ static int __init at91_mci_probe(struct platform_device *pdev)
         * Allocate the MCI interrupt
         */
        host->irq = platform_get_irq(pdev, 0);
-       ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED, DRIVER_NAME, host);
+       ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED,
+                       mmc_hostname(mmc), host);
        if (ret) {
-               printk(KERN_ERR "AT91 MMC: Failed to request MCI interrupt\n");
-               clk_disable(host->mci_clk);
-               clk_put(host->mci_clk);
-               mmc_free_host(mmc);
-               iounmap(host->baseaddr);
-               release_mem_region(res->start, res->end - res->start + 1);
-               return ret;
+               dev_dbg(&pdev->dev, "request MCI interrupt failed\n");
+               goto fail0;
        }
 
        platform_set_drvdata(pdev, mmc);
@@ -907,27 +1050,51 @@ static int __init at91_mci_probe(struct platform_device *pdev)
         * Add host to MMC layer
         */
        if (host->board->det_pin) {
-               host->present = !at91_get_gpio_value(host->board->det_pin);
-               device_init_wakeup(&pdev->dev, 1);
+               host->present = !gpio_get_value(host->board->det_pin);
        }
        else
                host->present = -1;
 
        mmc_add_host(mmc);
 
+       setup_timer(&host->timer, at91_timeout_timer, (unsigned long)host);
+
        /*
         * monitor card insertion/removal if we can
         */
        if (host->board->det_pin) {
-               ret = request_irq(host->board->det_pin, at91_mmc_det_irq,
-                               0, DRIVER_NAME, host);
+               ret = request_irq(gpio_to_irq(host->board->det_pin),
+                               at91_mmc_det_irq, 0, mmc_hostname(mmc), host);
                if (ret)
-                       printk(KERN_ERR "AT91 MMC: Couldn't allocate MMC detect irq\n");
+                       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);
+fail5:
+       mmc_free_host(mmc);
+fail6:
+       release_mem_region(res->start, res->end - res->start + 1);
+       dev_err(&pdev->dev, "probe failed, err %d\n", ret);
+       return ret;
 }
 
 /*
@@ -945,18 +1112,25 @@ static int __exit at91_mci_remove(struct platform_device *pdev)
        host = mmc_priv(mmc);
 
        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);
-               free_irq(host->board->det_pin, host);
-               cancel_delayed_work(&host->mmc->detect);
+               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, res->end - res->start + 1);
@@ -1029,3 +1203,4 @@ module_exit(at91_mci_exit);
 MODULE_DESCRIPTION("AT91 Multimedia Card Interface driver");
 MODULE_AUTHOR("Nick Randell");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:at91_mci");