mmc: core: rk_sdmmc: prepare for next Soc(s)
authorlintao <lintao@rock-chips.com>
Wed, 25 Jun 2014 08:30:26 +0000 (16:30 +0800)
committerlintao <lintao@rock-chips.com>
Wed, 25 Jun 2014 08:31:51 +0000 (16:31 +0800)
complete  external dma itf ops for mmc

drivers/mmc/host/Kconfig
drivers/mmc/host/rk_sdmmc.c
include/linux/mmc/rk_mmc.h

index 761e0808dcf08eb7b2c1bce7ab831ded5f56e5ff..d34baf9a47d5f5f8879e42729ade4fa3d03816a3 100755 (executable)
@@ -533,6 +533,15 @@ config MMC_DW_IDMAC
          Designware Mobile Storage IP block. This disables the external DMA
          interface.
 
+config MMC_DW_EDMAC
+       bool "External DMAC interface"
+       depends on MMC_DW && MMC_DW_ROCKCHIP && (!MMC_DW_IDMAC)
+       help
+         This selects support for the external DMAC block outside the Synopsys
+          Designware Mobile Storage IP block. This disables the internal DMA
+          interface.
+
+
 config MMC_DW_PLTFM
        tristate "Synopsys Designware MCI Support as platform device"
        depends on MMC_DW
index 5e8b7de7c08a1ab90d1e2d5542b5151c5a0c9ed2..899bf5a6eb3a25386ebfd83ef0f029a8d8c9daf8 100755 (executable)
@@ -18,6 +18,7 @@
 #include <linux/debugfs.h>
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -453,8 +454,9 @@ static void dw_mci_idmac_stop_dma(struct dw_mci *host)
        mci_writel(host, BMOD, temp);
 }
 
-static void dw_mci_idmac_complete_dma(struct dw_mci *host)
+static void dw_mci_idmac_complete_dma(void *arg)
 {
+        struct dw_mci *host = arg;
        struct mmc_data *data = host->data;
 
        dev_vdbg(host->dev, "DMA complete\n");
@@ -567,6 +569,138 @@ static const struct dw_mci_dma_ops dw_mci_idmac_ops = {
 };
 #endif /* CONFIG_MMC_DW_IDMAC */
 
+#ifdef CONFIG_MMC_DW_EDMAC
+static void dw_mci_edma_cleanup(struct dw_mci *host)
+{
+       struct mmc_data *data = host->data;
+
+       if (data)
+                if (!data->host_cookie)
+                       dma_unmap_sg(host->dev,
+                                        data->sg, data->sg_len,
+                                        dw_mci_get_dma_dir(data));
+}
+
+static void dw_mci_edmac_stop_dma(struct dw_mci *host)
+{
+        dmaengine_terminate_all(host->dms->ch);
+}
+
+static void dw_mci_edmac_complete_dma(void *arg)
+{
+        struct dw_mci *host = arg;
+        struct mmc_data *data = host->data;
+
+        dev_vdbg(host->dev, "DMA complete\n");
+
+       host->dma_ops->cleanup(host);
+
+       /*
+        * If the card was removed, data will be NULL. No point in trying to
+        * send the stop command or waiting for NBUSY in this case.
+        */
+       if (data) {
+               set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+               tasklet_schedule(&host->tasklet);
+       }
+}
+
+static void dw_mci_edmac_start_dma(struct dw_mci *host, unsigned int sg_len)
+{
+        struct dma_slave_config slave_config;
+        struct dma_async_tx_descriptor *desc = NULL;
+        struct scatterlist *sgl = host->data->sg;
+        int ret = 0;
+
+       /* set external dma config: burst size, burst width*/
+       if(host->data->flags & MMC_DATA_WRITE){
+                slave_config.direction = DMA_MEM_TO_DEV;
+                slave_config.dst_addr = (dma_addr_t)(host->regs + host->data_offset);
+                slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+                slave_config.dst_maxburst = 16;
+
+                ret = dmaengine_slave_config(host->dms->ch, &slave_config);
+                if (ret) {
+                        dev_err(host->dev, "error in dw_mci edma configuration.\n");
+                        return;
+                }
+
+                desc = dmaengine_prep_slave_sg(host->dms->ch, sgl, sg_len,
+                                DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
+                if (!desc) {
+                       dev_err(host->dev, "We cannot prepare for the dw_mci slave edma!\n");
+                       return;
+               }
+                /* set dw_mci_edmac_complete_dma as callback */
+               desc->callback = dw_mci_edmac_complete_dma;
+               desc->callback_param = (void *)host;
+               dmaengine_submit(desc);
+               /* flush cache before write & Invalidate cache after read ??? */
+                //dma_sync_single_for_device(host->dms->ch->device->dev, host->sg_dma,
+                //                        sg_len, DMA_TO_DEVICE);
+                dma_async_issue_pending(host->dms->ch);
+       }else{
+                /* MMC_DATA_READ*/
+                slave_config.direction = DMA_DEV_TO_MEM;
+                slave_config.src_addr = (dma_addr_t)(host->regs + host->data_offset);
+                slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+                slave_config.src_maxburst = 16;
+
+                ret = dmaengine_slave_config(host->dms->ch, &slave_config);
+                if (ret) {
+                        dev_err(host->dev, "error in dw_mci edma configuration.\n");
+                        return;
+                }
+                desc = dmaengine_prep_slave_sg(host->dms->ch, sgl, sg_len,
+                       DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+                if (!desc) {
+                       dev_err(host->dev, "We cannot prepare for the dw_mci slave edma!\n");
+                       return;
+               }
+                /* set dw_mci_edmac_complete_dma as callback */
+               desc->callback = dw_mci_edmac_complete_dma;
+               desc->callback_param = (void *)host;
+               dmaengine_submit(desc);
+               dma_async_issue_pending(host->dms->ch);
+       }
+
+       return;
+}
+
+static int dw_mci_edmac_init(struct dw_mci *host)
+{
+        MMC_DBG_BOOT_FUNC(host->mmc,"dw_mci_edmac_init: Soc is 0x%x [%s]\n",
+                                (unsigned int)(rockchip_soc_id & ROCKCHIP_CPU_MASK), mmc_hostname(host->mmc));
+
+        /* 1) request external dma channel, SHOULD decide chn in dts */
+        host->dms->ch = dma_request_slave_channel(host->dev, "dw_mci");
+        if (!host->dms->ch)
+       {
+               dev_err(host->dev, "Failed to get external DMA channel: channel id = %d\n",
+                               host->dms->ch->chan_id);
+               goto err_exit;
+       }
+
+        /* anything? */
+
+       return 0;
+
+err_exit:
+        return -ENODEV;
+
+}
+
+
+
+static const struct dw_mci_dma_ops dw_mci_edmac_ops = {
+        .init = dw_mci_edmac_init,
+        .start = dw_mci_edmac_start_dma,
+       .stop = dw_mci_edmac_stop_dma,
+       .complete = dw_mci_edmac_complete_dma,
+       .cleanup = dw_mci_edma_cleanup,
+};
+#endif
+
 static int dw_mci_pre_dma_transfer(struct dw_mci *host,
                                   struct mmc_data *data,
                                   bool next)
@@ -2680,7 +2814,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
        if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
                mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI);
                mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
-               host->dma_ops->complete(host);
+               host->dma_ops->complete((void *)host);
        }
 #endif
 
@@ -3183,9 +3317,12 @@ static void dw_mci_init_dma(struct dw_mci *host)
        }
 
        /* Determine which DMA interface to use */
-#ifdef CONFIG_MMC_DW_IDMAC
+#if defined(CONFIG_MMC_DW_IDMAC)
        host->dma_ops = &dw_mci_idmac_ops;
        dev_info(host->dev, "Using internal DMA controller.\n");
+#elif defined(CONFIG_MMC_DW_EDMAC)
+        host->dma_ops = &dw_mci_edmac_ops;
+        dev_info(host->dev, "Using external DMA controller.\n"); 
 #endif
 
        if (!host->dma_ops)
index c0b0e344a5548659c59967b5364d0d133cbb4c40..ffbabea05742a6c8fd79f0e069938efb1b74d98c 100755 (executable)
@@ -16,6 +16,7 @@
 
 #include <linux/scatterlist.h>
 #include <linux/mmc/core.h>
+#include <linux/dmaengine.h>
 
 #define MAX_MCI_SLOTS  2
 
@@ -36,6 +37,13 @@ enum {
        EVENT_XFER_ERROR
 };
 
+struct dw_mci_dma_slave {
+       struct dma_chan *ch;
+       enum dma_transfer_direction direction;
+       unsigned int dmach;
+};
+
+
 struct mmc_data;
 
 /**
@@ -147,6 +155,10 @@ struct dw_mci {
 #else
        struct dw_mci_dma_data  *dma_data;
 #endif
+
+#ifdef CONFIG_MMC_DW_EDMAC
+        struct dw_mci_dma_slave        *dms;
+#endif
        u32                     cmd_status;
        u32                     data_status;
        u32                     stop_cmdr;
@@ -209,7 +221,7 @@ struct dw_mci_dma_ops {
        /* DMA Ops */
        int (*init)(struct dw_mci *host);
        void (*start)(struct dw_mci *host, unsigned int sg_len);
-       void (*complete)(struct dw_mci *host);
+       void (*complete)(void *host);
        void (*stop)(struct dw_mci *host);
        void (*cleanup)(struct dw_mci *host);
        void (*exit)(struct dw_mci *host);