#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)
enum dw_mci_rockchip_type {
DW_MCI_TYPE_RK3188,
DW_MCI_TYPE_RK3288,
+ DW_MCI_TYPE_RK3036,
};
/* Rockchip implementation specific driver private data */
},{
.compatible = "rockchip,rk32xx-sdmmc",
.ctrl_type = DW_MCI_TYPE_RK3288,
+ },{
+ .compatible = "rockchip,rk3036-sdmmc",
+ .ctrl_type = DW_MCI_TYPE_RK3036,
},
};
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;
}
{
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;
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)
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)
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)
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);
#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>
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,
};
#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)
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;
* 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;
}
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
}
/* 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)
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);
*/
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);
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