mmc: core: rk_sdmmc: prepare for next Soc(s)
authorlintao <lintao@rock-chips.com>
Thu, 26 Jun 2014 13:27:48 +0000 (21:27 +0800)
committerlintao <lintao@rock-chips.com>
Thu, 26 Jun 2014 13:27:48 +0000 (21:27 +0800)
Merge branch from develop-3.10-next into develop-3.10

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

index 761e0808dcf08eb7b2c1bce7ab831ded5f56e5ff..230a57b15d50e6c066a06087ea281599f98af158 100755 (executable)
@@ -533,6 +533,13 @@ 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 0fe9cca379904cfc0ffe75a463318ea02b4cf8bd..8e17e6111eb4bfd883f5161c0f238f162372a61f 100755 (executable)
@@ -19,6 +19,7 @@
 #include <linux/of_gpio.h>
 #include <linux/slab.h>
 #include <linux/rockchip/cpu.h>
+#include <linux/rockchip/cru.h>
 
 #include "rk_sdmmc.h"
 #include "dw_mmc-pltfm.h"
 *   sdmmc,sdio0,sdio1,emmc id=0~3
 *   cclk_in_drv, cclk_in_sample  i=0,1
 */
-#define CRU_SDMMC_CON(id, tuning_type) (0x200 + ((id) * 8) + ((tuning_type) * 4))
+
+static  u32 cru_tuning_base = 0;
+
+#define CRU_SDMMC_CON(id, tuning_type) (cru_tuning_base + ((id) * 8) + ((tuning_type) * 4))
 
 #define MAX_DELAY_LINE  (0xff)
 #define FREQ_REF_150MHZ (150000000)
@@ -67,6 +71,7 @@ enum{
 enum dw_mci_rockchip_type {
        DW_MCI_TYPE_RK3188,
        DW_MCI_TYPE_RK3288,
+       DW_MCI_TYPE_RK3036,
 };
 
 /* Rockchip implementation specific driver private data */
@@ -88,6 +93,9 @@ static struct dw_mci_rockchip_compatible {
        },{
                .compatible     = "rockchip,rk32xx-sdmmc",
                .ctrl_type      = DW_MCI_TYPE_RK3288,
+       },{
+               .compatible     = "rockchip,rk3036-sdmmc",
+               .ctrl_type      = DW_MCI_TYPE_RK3036,
        },
 };
 
@@ -97,14 +105,14 @@ static int dw_mci_rockchip_priv_init(struct dw_mci *host)
        int idx;
 
        priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
-       if (!priv) {
+       if(!priv){
                dev_err(host->dev, "mem alloc failed for private data\n");
                return -ENOMEM;
        }
 
-       for (idx = 0; idx < ARRAY_SIZE(rockchip_compat); idx++) {
-               if (of_device_is_compatible(host->dev->of_node,
-                                       rockchip_compat[idx].compatible))
+       for(idx = 0; idx < ARRAY_SIZE(rockchip_compat); idx++){
+                if(of_device_is_compatible(host->dev->of_node,
+                                rockchip_compat[idx].compatible))
                        priv->ctrl_type = rockchip_compat[idx].ctrl_type;
        }
 
@@ -116,7 +124,8 @@ static int dw_mci_rockchip_setup_clock(struct dw_mci *host)
 {
        struct dw_mci_rockchip_priv_data *priv = host->priv;
 
-       if (priv->ctrl_type == DW_MCI_TYPE_RK3288)
+       if ((priv->ctrl_type == DW_MCI_TYPE_RK3288) ||
+               (priv->ctrl_type == DW_MCI_TYPE_RK3036))
                host->bus_hz /= (priv->ciu_div + 1);
 
        return 0;
@@ -124,8 +133,7 @@ static int dw_mci_rockchip_setup_clock(struct dw_mci *host)
 
 static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
 {
-//     if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL)))
-//             *cmdr |= SDMMC_CMD_USE_HOLD_REG;
+
 }
 
 static void dw_mci_rockchip_set_ios(struct dw_mci *host, struct mmc_ios *ios)
@@ -210,9 +218,16 @@ static inline u8 dw_mci_rockchip_move_next_clksmpl(struct dw_mci *host, u8 con_i
        return val;
 }
 
+static void dw_mci_rockchip_load_tuning_base(void)
+{
+        if(cpu_is_rk3288())
+                cru_tuning_base =  RK3288_CRU_SDMMC_CON0;
 
-    
-        
+     /* Fixme: 3036
+        else if(cpu_is_rk3036())
+                cru_tuning_base =  RK3036_CRU_SDMMC_CON0;
+     */
+}
 
 static int inline __dw_mci_rockchip_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
                                        u8 *blk_test, unsigned int blksz)
@@ -273,7 +288,9 @@ static int dw_mci_rockchip_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
        int ref = 0;
        unsigned int blksz = tuning_data->blksz;
 
-        MMC_DBG_INFO_FUNC(host->mmc,"execute tuning:  [%s]", mmc_hostname(host->mmc));
+       MMC_DBG_INFO_FUNC(host->mmc,"execute tuning:  [%s]", mmc_hostname(host->mmc));
+
+       dw_mci_rockchip_load_tuning_base();
        
        blk_test = kmalloc(blksz, GFP_KERNEL);
        if (!blk_test)
@@ -293,6 +310,7 @@ static int dw_mci_rockchip_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
            0.9 / 60ps = 15 delayline
          */
         if(cpu_is_rk3288()){
+                /* Fixme: 3036:  dose it compatitable? */
                  ref = ((FREQ_REF_150MHZ + host->bus_hz - 1) / host->bus_hz);
                  step = (15 * ref);
 
index b6d8b2098362e58887b86f9372da0ea215c3e70a..373a128d22e53f7b64b66c3f720d85ff98ba13d5 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>
@@ -450,27 +451,29 @@ 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 mmc_data *data = host->data;
+        struct dw_mci *host = arg;
+        struct mmc_data *data = host->data;
 
-       dev_vdbg(host->dev, "DMA complete\n");
+        dev_vdbg(host->dev, "DMA complete\n");
 
-    /*
-    MMC_DBG_CMD_FUNC(host->mmc," DMA complete cmd=%d(arg=0x%x), blocks=%d,blksz=%d[%s]", \
-        host->mrq->cmd->opcode,host->mrq->cmd->arg,data->blocks,data->blksz,mmc_hostname(host->mmc));
-    */
-    
-       host->dma_ops->cleanup(host);
+        /*
+        MMC_DBG_CMD_FUNC(host->mmc," DMA complete cmd=%d(arg=0x%x), blocks=%d,blksz=%d[%s]", \
+                host->mrq->cmd->opcode,host->mrq->cmd->arg,
+                data->blocks,data->blksz,mmc_hostname(host->mmc));
+        */
+
+        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);
-       }
+        if(data){
+                set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+                tasklet_schedule(&host->tasklet);
+        }
 }
 
 static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data,
@@ -564,6 +567,151 @@ 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");
+
+        if(data->flags & MMC_DATA_READ)
+        {
+                /* Invalidate cache after read */
+                dma_sync_sg_for_cpu(host->dms->ch->device->dev, data->sg,
+                        data->sg_len, DMA_FROM_DEVICE);
+        }
+
+        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;
+        u32 sg_elems = host->data->sg_len;
+        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;
+                slave_config.dst_maxburst = ((host->fifoth_val) >> 28) && 0x7;
+
+                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 | DMA_CTRL_ACK);
+                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 */
+                dma_sync_sg_for_device(host->dms->ch->device->dev, sgl,
+                                        sg_elems, 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;
+                slave_config.dst_maxburst = ((host->fifoth_val) >> 28) && 0x7;
+
+                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 | DMA_CTRL_ACK);
+                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 void dw_mci_edmac_exit(struct dw_mci *host)
+{
+        dma_release_channel(host->dms->ch);
+}
+
+static const struct dw_mci_dma_ops dw_mci_edmac_ops = {
+        .init = dw_mci_edmac_init,
+        .exit = dw_mci_edmac_exit,
+        .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)
@@ -642,7 +790,7 @@ static void dw_mci_post_req(struct mmc_host *mmc,
 
 static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data)
 {
-#ifdef CONFIG_MMC_DW_IDMAC
+#if defined(CONFIG_MMC_DW_IDMAC) || defined(CONFIG_MMC_DW_EDMAC)
        unsigned int blksz = data->blksz;
        const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256};
        u32 fifo_width = 1 << host->data_shift;
@@ -2593,7 +2741,8 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
        * is configured.
         */
         if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) {
-                if (!pending &&((mci_readl(host, STATUS) >> 17) & 0x1fff))
+                       if (!pending &&
+                           ((mci_readl(host, STATUS) >> 17) & 0x1fff))
                                pending |= SDMMC_INT_DATA_OVER;
        }
 
@@ -2695,7 +2844,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
 
@@ -3200,9 +3349,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)
@@ -3672,36 +3824,38 @@ EXPORT_SYMBOL(dw_mci_probe);
 void dw_mci_remove(struct dw_mci *host)
 {
        int i;
+       del_timer_sync(&host->dto_timer);
 
-        del_timer_sync(&host->dto_timer);
-       mci_writel(host, RINTSTS, 0xFFFFFFFF);
-       mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+       if(host->use_dma && host->dma_ops->exit)
+               host->dma_ops->exit(host);
 
-       for (i = 0; i < host->num_slots; i++) {
-               dev_dbg(host->dev, "remove slot %d\n", i);
-               if (host->slot[i])
-                       dw_mci_cleanup_slot(host->slot[i], i);
-       }
+        mci_writel(host, RINTSTS, 0xFFFFFFFF);
+        mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
 
-       /* disable clock to CIU */
-       mci_writel(host, CLKENA, 0);
-       mci_writel(host, CLKSRC, 0);
+        for(i = 0; i < host->num_slots; i++){
+                dev_dbg(host->dev, "remove slot %d\n", i);
+                if(host->slot[i])
+                        dw_mci_cleanup_slot(host->slot[i], i);
+        }
 
-       destroy_workqueue(host->card_workqueue);
+        /* disable clock to CIU */
+        mci_writel(host, CLKENA, 0);
+        mci_writel(host, CLKSRC, 0);
 
-       if (host->use_dma && host->dma_ops->exit)
-               host->dma_ops->exit(host);
+        destroy_workqueue(host->card_workqueue);
 
-       if (host->vmmc){
-               regulator_disable(host->vmmc);
-               regulator_put(host->vmmc);
-       }
-               
-    if (!IS_ERR(host->clk_mmc))
-               clk_disable_unprepare(host->clk_mmc);
+        if(host->use_dma && host->dma_ops->exit)
+                host->dma_ops->exit(host);
 
-    if (!IS_ERR(host->hclk_mmc))
-               clk_disable_unprepare(host->hclk_mmc);
+        if(host->vmmc){
+                regulator_disable(host->vmmc);
+                regulator_put(host->vmmc);
+        }
+       if(!IS_ERR(host->clk_mmc))
+                clk_disable_unprepare(host->clk_mmc);
+
+        if(!IS_ERR(host->hclk_mmc))
+                clk_disable_unprepare(host->hclk_mmc);
 }
 EXPORT_SYMBOL(dw_mci_remove);
 
@@ -3713,26 +3867,29 @@ EXPORT_SYMBOL(dw_mci_remove);
  */
 int dw_mci_suspend(struct dw_mci *host)
 {
-       
-       if (host->vmmc)
-               regulator_disable(host->vmmc);
+        if(host->vmmc)
+                regulator_disable(host->vmmc);
 
-       /*only for sdmmc controller*/
-       if(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD) {
-               host->mmc->rescan_disable = 1;
-               if (cancel_delayed_work_sync(&host->mmc->detect)){
+        if(host->use_dma && host->dma_ops->exit)
+                host->dma_ops->exit(host);
+
+        /*only for sdmmc controller*/
+        if(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD){
+                host->mmc->rescan_disable = 1;
+                if(cancel_delayed_work_sync(&host->mmc->detect))
                        wake_unlock(&host->mmc->detect_wake_lock);
-               }
-               disable_irq(host->irq);
-               if(pinctrl_select_state(host->pinctrl, host->pins_idle) < 0)
-                       printk("%s: Warning :  Idle pinctrl setting failed!\n", mmc_hostname(host->mmc));  
-               dw_mci_of_get_cd_gpio(host->dev,0,host->mmc);
-               mci_writel(host, RINTSTS, 0xFFFFFFFF);
-               mci_writel(host, INTMASK, 0x00);
-               mci_writel(host, CTRL, 0x00);
-               enable_irq_wake(host->mmc->slot.cd_irq);
-    }
-       return 0;
+
+                disable_irq(host->irq);
+                if(pinctrl_select_state(host->pinctrl, host->pins_idle) < 0)
+                        MMC_DBG_ERR_FUNC(host->mmc, "Idle pinctrl setting failed! [%s]",
+                                                mmc_hostname(host->mmc));
+                dw_mci_of_get_cd_gpio(host->dev,0,host->mmc);
+                mci_writel(host, RINTSTS, 0xFFFFFFFF);
+                mci_writel(host, INTMASK, 0x00);
+                mci_writel(host, CTRL, 0x00);
+                enable_irq_wake(host->mmc->slot.cd_irq);
+        }
+        return 0;
 }
 EXPORT_SYMBOL(dw_mci_suspend);
 
@@ -3753,7 +3910,8 @@ int dw_mci_resume(struct dw_mci *host)
                disable_irq_wake(host->mmc->slot.cd_irq);
                mmc_gpio_free_cd(host->mmc);
                if(pinctrl_select_state(host->pinctrl, host->pins_default) < 0)
-                       printk("%s: Warning :  Default pinctrl setting failed!\n", mmc_hostname(host->mmc));  
+                        MMC_DBG_ERR_FUNC(host->mmc, "Default pinctrl setting failed! [%s]",
+                                                mmc_hostname(host->mmc));
                host->mmc->rescan_disable = 0;
                if(cpu_is_rk3288()) {
                        grf_writel(((1<<12)<<16)|(0<<12), RK3288_GRF_SOC_CON0);//disable jtag
index 7ea1106c06b4a702bcf8dab5e605b1a39cc5d58e..abf91d0e9d86e088182764f4ad25cd9b30fef001 100755 (executable)
@@ -16,6 +16,7 @@
 
 #include <linux/scatterlist.h>
 #include <linux/mmc/core.h>
+#include <linux/dmaengine.h>
 #include <linux/timer.h>
 
 #define MAX_MCI_SLOTS  2
@@ -36,6 +37,11 @@ enum {
        EVENT_DATA_ERROR,
        EVENT_XFER_ERROR
 };
+struct dw_mci_dma_slave {
+       struct dma_chan *ch;
+       enum dma_transfer_direction direction;
+       unsigned int dmach;
+};
 
 struct mmc_data;
 
@@ -148,6 +154,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;
@@ -176,9 +186,10 @@ struct dw_mci {
        struct dw_mci_slot      *slot[MAX_MCI_SLOTS];
        struct mmc_host         *mmc;
        struct mmc_command      *pre_cmd;
-       unsigned int    hold_reg_flag;//to fix the hold_reg value
-
-       struct timer_list       dto_timer;     //the timer for INT_DTO
+        /* Fix the hold_reg value */
+        unsigned int    hold_reg_flag;
+        /* Timer for INT_DTO */
+        struct timer_list       dto_timer;
        /* FIFO push and pull */
        int                     fifo_depth;
        int                     data_shift;
@@ -211,7 +222,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);