]> Pileus Git - ~andy/linux/blobdiff - drivers/mmc/host/omap_hsmmc.c
Merge tag 'drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[~andy/linux] / drivers / mmc / host / omap_hsmmc.c
index 33e81c24e1408734bf8b431bbb319926a362fa00..9a7a60aeb19ea35dc921cd43f2e796c2bfd75469 100644 (file)
 #define BRR_ENABLE             (1 << 5)
 #define DTO_ENABLE             (1 << 20)
 #define INIT_STREAM            (1 << 1)
+#define ACEN_ACMD12            (1 << 2)
 #define DP_SELECT              (1 << 21)
 #define DDIR                   (1 << 4)
 #define DMA_EN                 0x1
 #define MSBS                   (1 << 5)
 #define BCE                    (1 << 1)
 #define FOUR_BIT               (1 << 1)
+#define DDR                    (1 << 19)
 #define DW8                    (1 << 5)
 #define CC                     0x1
 #define TC                     0x02
 #define OMAP_MMC_MAX_CLOCK     52000000
 #define DRIVER_NAME            "omap_hsmmc"
 
+#define AUTO_CMD12             (1 << 0)        /* Auto CMD12 support */
 /*
  * One controller can have multiple slots, like on some omap boards using
  * omap.c controller driver. Luckily this is not currently done on any known
@@ -167,7 +170,6 @@ struct omap_hsmmc_host {
        int                     use_dma, dma_ch;
        int                     dma_line_tx, dma_line_rx;
        int                     slot_id;
-       int                     got_dbclk;
        int                     response_busy;
        int                     context_loss;
        int                     vdd;
@@ -175,6 +177,7 @@ struct omap_hsmmc_host {
        int                     reqs_blocked;
        int                     use_reg;
        int                     req_in_progress;
+       unsigned int            flags;
        struct omap_hsmmc_next  next_data;
 
        struct  omap_mmc_platform_data  *pdata;
@@ -520,6 +523,10 @@ static void omap_hsmmc_set_bus_width(struct omap_hsmmc_host *host)
        u32 con;
 
        con = OMAP_HSMMC_READ(host->base, CON);
+       if (ios->timing == MMC_TIMING_UHS_DDR50)
+               con |= DDR;     /* configure in DDR mode */
+       else
+               con &= ~DDR;
        switch (ios->bus_width) {
        case MMC_BUS_WIDTH_8:
                OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
@@ -766,6 +773,8 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
                cmdtype = 0x3;
 
        cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22);
+       if ((host->flags & AUTO_CMD12) && mmc_op_multi(cmd->opcode))
+               cmdreg |= ACEN_ACMD12;
 
        if (data) {
                cmdreg |= DP_SELECT | MSBS | BCE;
@@ -796,11 +805,12 @@ omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data)
 static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)
 {
        int dma_ch;
+       unsigned long flags;
 
-       spin_lock(&host->irq_lock);
+       spin_lock_irqsave(&host->irq_lock, flags);
        host->req_in_progress = 0;
        dma_ch = host->dma_ch;
-       spin_unlock(&host->irq_lock);
+       spin_unlock_irqrestore(&host->irq_lock, flags);
 
        omap_hsmmc_disable_irq(host);
        /* Do not complete the request if DMA is still in progress */
@@ -837,11 +847,14 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)
        else
                data->bytes_xfered = 0;
 
-       if (!data->stop) {
+       if (data->stop && ((!(host->flags & AUTO_CMD12)) || data->error)) {
+               omap_hsmmc_start_command(host, data->stop, NULL);
+       } else {
+               if (data->stop)
+                       data->stop->resp[0] = OMAP_HSMMC_READ(host->base,
+                                                       RSP76);
                omap_hsmmc_request_done(host, data->mrq);
-               return;
        }
-       omap_hsmmc_start_command(host, data->stop, NULL);
 }
 
 /*
@@ -874,13 +887,14 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
 static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
 {
        int dma_ch;
+       unsigned long flags;
 
        host->data->error = errno;
 
-       spin_lock(&host->irq_lock);
+       spin_lock_irqsave(&host->irq_lock, flags);
        dma_ch = host->dma_ch;
        host->dma_ch = -1;
-       spin_unlock(&host->irq_lock);
+       spin_unlock_irqrestore(&host->irq_lock, flags);
 
        if (host->use_dma && dma_ch != -1) {
                dma_unmap_sg(mmc_dev(host->mmc), host->data->sg,
@@ -1082,7 +1096,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
 
        /* Disable the clocks */
        pm_runtime_put_sync(host->dev);
-       if (host->got_dbclk)
+       if (host->dbclk)
                clk_disable(host->dbclk);
 
        /* Turn the power off */
@@ -1093,7 +1107,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
                ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1,
                                               vdd);
        pm_runtime_get_sync(host->dev);
-       if (host->got_dbclk)
+       if (host->dbclk)
                clk_enable(host->dbclk);
 
        if (ret != 0)
@@ -1234,6 +1248,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
        struct omap_hsmmc_host *host = cb_data;
        struct mmc_data *data;
        int dma_ch, req_in_progress;
+       unsigned long flags;
 
        if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
                dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n",
@@ -1241,9 +1256,9 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
                return;
        }
 
-       spin_lock(&host->irq_lock);
+       spin_lock_irqsave(&host->irq_lock, flags);
        if (host->dma_ch < 0) {
-               spin_unlock(&host->irq_lock);
+               spin_unlock_irqrestore(&host->irq_lock, flags);
                return;
        }
 
@@ -1253,7 +1268,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
                /* Fire up the next transfer. */
                omap_hsmmc_config_dma_params(host, data,
                                           data->sg + host->dma_sg_idx);
-               spin_unlock(&host->irq_lock);
+               spin_unlock_irqrestore(&host->irq_lock, flags);
                return;
        }
 
@@ -1264,7 +1279,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
        req_in_progress = host->req_in_progress;
        dma_ch = host->dma_ch;
        host->dma_ch = -1;
-       spin_unlock(&host->irq_lock);
+       spin_unlock_irqrestore(&host->irq_lock, flags);
 
        omap_free_dma(dma_ch);
 
@@ -1844,6 +1859,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
        host->mapbase   = res->start + pdata->reg_offset;
        host->base      = ioremap(host->mapbase, SZ_4K);
        host->power_mode = MMC_POWER_OFF;
+       host->flags     = AUTO_CMD12;
        host->next_data.cookie = 1;
 
        platform_set_drvdata(pdev, host);
@@ -1885,21 +1901,17 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 
        omap_hsmmc_context_save(host);
 
-       if (cpu_is_omap2430()) {
-               host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
-               /*
-                * MMC can still work without debounce clock.
-                */
-               if (IS_ERR(host->dbclk))
-                       dev_warn(mmc_dev(host->mmc),
-                               "Failed to get debounce clock\n");
-               else
-                       host->got_dbclk = 1;
-
-               if (host->got_dbclk)
-                       if (clk_enable(host->dbclk) != 0)
-                               dev_dbg(mmc_dev(host->mmc), "Enabling debounce"
-                                                       " clk failed\n");
+       host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
+       /*
+        * MMC can still work without debounce clock.
+        */
+       if (IS_ERR(host->dbclk)) {
+               dev_warn(mmc_dev(host->mmc), "Failed to get debounce clk\n");
+               host->dbclk = NULL;
+       } else if (clk_enable(host->dbclk) != 0) {
+               dev_warn(mmc_dev(host->mmc), "Failed to enable debounce clk\n");
+               clk_put(host->dbclk);
+               host->dbclk = NULL;
        }
 
        /* Since we do only SG emulation, we can have as many segs
@@ -1969,7 +1981,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
                ret = request_threaded_irq(mmc_slot(host).card_detect_irq,
                                           NULL,
                                           omap_hsmmc_detect,
-                                          IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                                          IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                                           mmc_hostname(mmc), host);
                if (ret) {
                        dev_dbg(mmc_dev(host->mmc),
@@ -2019,7 +2031,7 @@ err_irq:
        pm_runtime_put_sync(host->dev);
        pm_runtime_disable(host->dev);
        clk_put(host->fclk);
-       if (host->got_dbclk) {
+       if (host->dbclk) {
                clk_disable(host->dbclk);
                clk_put(host->dbclk);
        }
@@ -2030,7 +2042,9 @@ err1:
 err_alloc:
        omap_hsmmc_gpio_free(pdata);
 err:
-       release_mem_region(res->start, resource_size(res));
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res)
+               release_mem_region(res->start, resource_size(res));
        return ret;
 }
 
@@ -2052,7 +2066,7 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev)
        pm_runtime_put_sync(host->dev);
        pm_runtime_disable(host->dev);
        clk_put(host->fclk);
-       if (host->got_dbclk) {
+       if (host->dbclk) {
                clk_disable(host->dbclk);
                clk_put(host->dbclk);
        }
@@ -2110,7 +2124,7 @@ static int omap_hsmmc_suspend(struct device *dev)
                                OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
        }
 
-       if (host->got_dbclk)
+       if (host->dbclk)
                clk_disable(host->dbclk);
 err:
        pm_runtime_put_sync(host->dev);
@@ -2131,7 +2145,7 @@ static int omap_hsmmc_resume(struct device *dev)
 
        pm_runtime_get_sync(host->dev);
 
-       if (host->got_dbclk)
+       if (host->dbclk)
                clk_enable(host->dbclk);
 
        if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER))