#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/log2.h>
+#include <asm-generic/dma-mapping-common.h>
#include "rk_sdmmc.h"
#include "rk_sdmmc_dbg.h"
#include <linux/regulator/rockchip_io_vol_domain.h>
#include "../../clk/rockchip/clk-ops.h"
+#include <linux/rk_keys.h>
#define RK_SDMMC_DRIVER_VERSION "Ver 2.00 2015-06-10"
static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host);
static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset);
static void dw_mci_disable_low_power(struct dw_mci_slot *slot);
-extern void rk_send_wakeup_key(void);
#if defined(CONFIG_DEBUG_FS)
static int dw_mci_req_show(struct seq_file *s, void *v)
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 %d\n",
- host->dms->ch->chan_id);
+ "Failed to get external DMA channel\n");
kfree(host->dms);
host->dms = NULL;
return -ENXIO;
};
#endif /* CONFIG_MMC_DW_IDMAC */
+static struct dma_attrs dw_mci_direct_attrs;
+
static int dw_mci_pre_dma_transfer(struct dw_mci *host,
struct mmc_data *data,
bool next)
{
struct scatterlist *sg;
unsigned int i, sg_len;
+#ifdef CONFIG_MMC_DW_SKIP_CACHE_OP
+ struct dma_attrs *attrs;
+#endif
if (!next && data->host_cookie)
return data->host_cookie;
if (sg->offset & 3 || sg->length & 3)
return -EINVAL;
}
+#ifdef CONFIG_MMC_DW_SKIP_CACHE_OP
+ attrs = (data->flags & MMC_DATA_DIRECT) ? &dw_mci_direct_attrs : NULL;
+ sg_len = dma_map_sg_attrs(host->dev,
+ data->sg,
+ data->sg_len,
+ dw_mci_get_dma_dir(data), attrs);
+#else
sg_len = dma_map_sg(host->dev,
data->sg,
data->sg_len,
dw_mci_get_dma_dir(data));
+#endif
if (sg_len == 0)
return -EINVAL;
host->pending_events = 0;
host->completed_events = 0;
host->data_status = 0;
+ host->cmd_status = 0;
data = cmd->data;
if (data) {
{
struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci *host = slot->host;
- struct mmc_data *data;
- u32 ret, i, regs, cmd_flags;
+ u32 i, regs, cmd_flags;
u32 sdio_int;
unsigned long timeout = 0;
- bool ret_timeout = true;
- u32 opcode;
+ bool ret_timeout = true, is_retry = false;
+ u32 opcode, offset;
+ if (host->cur_slot->mrq->data)
+ dw_mci_stop_dma(host);
+
+ offset = host->cru_reset_offset;
opcode = host->mrq->cmd->opcode;
host->cur_slot->mrq = NULL;
host->mrq = NULL;
host->state = STATE_IDLE;
+ host->data = NULL;
- data = host->data;
-
- if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) &&
+ if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) ||
(opcode == MMC_SEND_TUNING_BLOCK))
- return;
+ return;
printk("[%s] -- Timeout recovery procedure start --\n",
mmc_hostname(host->mmc));
+ /* unmask irq */
+ mci_writel(host, INTMASK, 0x0);
+
+retry_stop:
+ /* send stop cmd */
mci_writel(host, CMDARG, 0);
wmb();
cmd_flags = SDMMC_CMD_STOP | SDMMC_CMD_RESP_CRC |
mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
wmb();
- timeout = jiffies + msecs_to_jiffies(500);
+ timeout = jiffies + msecs_to_jiffies(10);
while(ret_timeout) {
ret_timeout = time_before(jiffies, timeout);
break;
}
- if (false == ret_timeout)
+ if (false == ret_timeout) {
MMC_DBG_ERR_FUNC(host->mmc, "stop recovery failed![%s]",
mmc_hostname(host->mmc));
-
- if (!dw_mci_ctrl_all_reset(host)) {
- ret = -ENODEV;
- return ;
+ if (host->cid == DW_MCI_TYPE_RK3368) {
+ /* pd_peri mmc AHB bus software reset request */
+ regmap_write(host->cru, host->cru_regsbase,
+ (0x1<<offset)<<16 | (0x1 << offset));
+ mdelay(1);
+ regmap_write(host->cru, host->cru_regsbase,
+ (0x1<<offset)<<16 | (0x0 << offset));
+ } else {
+ /* pd_peri mmc AHB bus software reset request */
+ cru_writel(((0x1<<offset)<<16) | (0x1 << offset),
+ host->cru_regsbase);
+ mdelay(1);
+ cru_writel(((0x1<<offset)<<16) | (0x0 << offset),
+ host->cru_regsbase);
+ }
+ } else {
+ if (!dw_mci_ctrl_all_reset(host))
+ return;
+ if (is_retry == true)
+ goto recovery_end;
}
#ifdef CONFIG_MMC_DW_IDMAC
#endif
/*
- * Restore the initial value at FIFOTH register
- * And Invalidate the prev_blksz with zero
- */
+ * Restore the initial value at FIFOTH register
+ * And Invalidate the prev_blksz with zero
+ */
mci_writel(host, FIFOTH, host->fifoth_val);
host->prev_blksz = 0;
mci_writel(host, TMOUT, 0xFFFFFFFF);
mci_writel(host, RINTSTS, 0xFFFFFFFF);
- regs = SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | SDMMC_INT_TXDR
- | SDMMC_INT_RXDR | SDMMC_INT_VSI | DW_MCI_ERROR_FLAGS;
+ regs = SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
+ SDMMC_INT_TXDR | SDMMC_INT_RXDR | SDMMC_INT_VSI |
+ DW_MCI_ERROR_FLAGS;
if (!(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO))
regs |= SDMMC_INT_CD;
}
}
mci_writel(host, RINTSTS, 0xFFFFFFFF);
+ if (ret_timeout == false) {
+ ret_timeout = true;
+ is_retry = true;
+ goto retry_stop;
+ }
+recovery_end:
printk("[%s] -- Timeout recovery procedure finished --\n",
mmc_hostname(host->mmc));
}
regmap_write(host->grf, 0x43c, (1<<13)<<16 | (0 << 13));
} else if (cpu_is_rk3288()) {
grf_writel(((1 << 12) << 16) | (0 << 12), RK3288_GRF_SOC_CON0);
+ } else if (host->cid == DW_MCI_TYPE_RK3228) {
+ grf_writel(((1 << 8) << 16) | (0 << 8), RK3228_GRF_SOC_CON6);
}
/* We assume only low-level chip use gpio_cd */
#ifdef CONFIG_MMC_DW_IDMAC
mmc->max_segs = host->ring_size;
mmc->max_blk_size = 65536;
- mmc->max_blk_count = host->ring_size;
mmc->max_seg_size = 0x1000;
- mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count;
+ mmc->max_req_size = mmc->max_seg_size * host->ring_size;
+ mmc->max_blk_count = mmc->max_req_size / 512;
+
if(cpu_is_rk3036() || cpu_is_rk312x()){
/* fixup for external dmac setting */
mmc->max_segs = 64;
if (of_get_property(np, "assume_removable", NULL))
mmc_assume_removable = 0;
+ if (!of_property_read_u32(np, "cru_regsbase", &host->cru_regsbase)) {
+ printk("dw cru_regsbase addr 0x%03x.\n", host->cru_regsbase);
+ } else {
+ pr_err("dw cru_regsbase addr is missing!\n");
+ return ERR_PTR(-1);
+ }
+
+ if (!of_property_read_u32(np, "cru_reset_offset", &host->cru_reset_offset)) {
+ printk("dw cru_reset_offset val %d.\n", host->cru_reset_offset);
+ } else {
+ pr_err("dw cru_reset_offset val is missing!\n");
+ return ERR_PTR(-1);
+ }
+
return pdata;
}
if (!dw_mci_ctrl_all_reset(host))
return -ENODEV;
+ init_dma_attrs(&dw_mci_direct_attrs);
+ dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &dw_mci_direct_attrs);
+
host->dma_ops = host->pdata->dma_ops;
dw_mci_init_dma(host);