From 78f99b8039f7feafe8b5c635edea790858876246 Mon Sep 17 00:00:00 2001 From: lintao Date: Thu, 26 Jun 2014 21:27:48 +0800 Subject: [PATCH] mmc: core: rk_sdmmc: prepare for next Soc(s) Merge branch from develop-3.10-next into develop-3.10 --- drivers/mmc/host/Kconfig | 7 + drivers/mmc/host/dw_mmc-rockchip.c | 40 +++-- drivers/mmc/host/rk_sdmmc.c | 276 +++++++++++++++++++++++------ include/linux/mmc/rk_mmc.h | 19 +- 4 files changed, 268 insertions(+), 74 deletions(-) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 761e0808dcf0..230a57b15d50 100755 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -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 diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 0fe9cca37990..8e17e6111eb4 100755 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "rk_sdmmc.h" #include "dw_mmc-pltfm.h" @@ -31,7 +32,10 @@ * 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); diff --git a/drivers/mmc/host/rk_sdmmc.c b/drivers/mmc/host/rk_sdmmc.c index b6d8b2098362..373a128d22e5 100755 --- a/drivers/mmc/host/rk_sdmmc.c +++ b/drivers/mmc/host/rk_sdmmc.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -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 diff --git a/include/linux/mmc/rk_mmc.h b/include/linux/mmc/rk_mmc.h index 7ea1106c06b4..abf91d0e9d86 100755 --- a/include/linux/mmc/rk_mmc.h +++ b/include/linux/mmc/rk_mmc.h @@ -16,6 +16,7 @@ #include #include +#include #include #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); -- 2.34.1