Commit 52426899 authored by Seungwon Jeon's avatar Seungwon Jeon Committed by Chris Ball

mmc: dw_mmc: adjust the fifoth with block size

This change helps to choose msize, rx_watermark and tx_watermark
depending on block size for IDMAC mode.  For SDIO block size can be
variable, so if these values are set incorrectly, card clock may stop.
Signed-off-by: default avatarSeungwon Jeon <tgih.jun@samsung.com>
Tested-by: default avatarAlim Akhtar <alim.akhtar@samsung.com>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent 1f44a2a5
...@@ -529,6 +529,47 @@ static void dw_mci_post_req(struct mmc_host *mmc, ...@@ -529,6 +529,47 @@ static void dw_mci_post_req(struct mmc_host *mmc,
data->host_cookie = 0; data->host_cookie = 0;
} }
static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data)
{
#ifdef CONFIG_MMC_DW_IDMAC
unsigned int blksz = data->blksz;
const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256};
u32 fifo_width = 1 << host->data_shift;
u32 blksz_depth = blksz / fifo_width, fifoth_val;
u32 msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers;
int idx = (sizeof(mszs) / sizeof(mszs[0])) - 1;
tx_wmark = (host->fifo_depth) / 2;
tx_wmark_invers = host->fifo_depth - tx_wmark;
/*
* MSIZE is '1',
* if blksz is not a multiple of the FIFO width
*/
if (blksz % fifo_width) {
msize = 0;
rx_wmark = 1;
goto done;
}
do {
if (!((blksz_depth % mszs[idx]) ||
(tx_wmark_invers % mszs[idx]))) {
msize = idx;
rx_wmark = mszs[idx] - 1;
break;
}
} while (--idx > 0);
/*
* If idx is '0', it won't be tried
* Thus, initial values are uesed
*/
done:
fifoth_val = SDMMC_SET_FIFOTH(msize, rx_wmark, tx_wmark);
mci_writel(host, FIFOTH, fifoth_val);
#endif
}
static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
{ {
int sg_len; int sg_len;
...@@ -553,6 +594,14 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) ...@@ -553,6 +594,14 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
(unsigned long)host->sg_cpu, (unsigned long)host->sg_dma, (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
sg_len); sg_len);
/*
* Decide the MSIZE and RX/TX Watermark.
* If current block size is same with previous size,
* no need to update fifoth.
*/
if (host->prev_blksz != data->blksz)
dw_mci_adjust_fifoth(host, data);
/* Enable the DMA interface */ /* Enable the DMA interface */
temp = mci_readl(host, CTRL); temp = mci_readl(host, CTRL);
temp |= SDMMC_CTRL_DMA_ENABLE; temp |= SDMMC_CTRL_DMA_ENABLE;
...@@ -603,6 +652,21 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) ...@@ -603,6 +652,21 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
temp = mci_readl(host, CTRL); temp = mci_readl(host, CTRL);
temp &= ~SDMMC_CTRL_DMA_ENABLE; temp &= ~SDMMC_CTRL_DMA_ENABLE;
mci_writel(host, CTRL, temp); mci_writel(host, CTRL, temp);
/*
* Use the initial fifoth_val for PIO mode.
* If next issued data may be transfered by DMA mode,
* prev_blksz should be invalidated.
*/
mci_writel(host, FIFOTH, host->fifoth_val);
host->prev_blksz = 0;
} else {
/*
* Keep the current block size.
* It will be used to decide whether to update
* fifoth register next time.
*/
host->prev_blksz = data->blksz;
} }
} }
...@@ -2368,8 +2432,8 @@ int dw_mci_probe(struct dw_mci *host) ...@@ -2368,8 +2432,8 @@ int dw_mci_probe(struct dw_mci *host)
fifo_size = host->pdata->fifo_depth; fifo_size = host->pdata->fifo_depth;
} }
host->fifo_depth = fifo_size; host->fifo_depth = fifo_size;
host->fifoth_val = ((0x2 << 28) | ((fifo_size/2 - 1) << 16) | host->fifoth_val =
((fifo_size/2) << 0)); SDMMC_SET_FIFOTH(0x2, fifo_size / 2 - 1, fifo_size / 2);
mci_writel(host, FIFOTH, host->fifoth_val); mci_writel(host, FIFOTH, host->fifoth_val);
/* disable clock to CIU */ /* disable clock to CIU */
...@@ -2552,8 +2616,12 @@ int dw_mci_resume(struct dw_mci *host) ...@@ -2552,8 +2616,12 @@ int dw_mci_resume(struct dw_mci *host)
if (host->use_dma && host->dma_ops->init) if (host->use_dma && host->dma_ops->init)
host->dma_ops->init(host); host->dma_ops->init(host);
/* Restore the old value at FIFOTH register */ /*
* Restore the initial value at FIFOTH register
* And Invalidate the prev_blksz with zero
*/
mci_writel(host, FIFOTH, host->fifoth_val); mci_writel(host, FIFOTH, host->fifoth_val);
host->prev_blksz = 0;
/* Put in max timeout */ /* Put in max timeout */
mci_writel(host, TMOUT, 0xFFFFFFFF); mci_writel(host, TMOUT, 0xFFFFFFFF);
......
...@@ -128,6 +128,10 @@ ...@@ -128,6 +128,10 @@
#define SDMMC_CMD_INDX(n) ((n) & 0x1F) #define SDMMC_CMD_INDX(n) ((n) & 0x1F)
/* Status register defines */ /* Status register defines */
#define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF) #define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF)
/* FIFOTH register defines */
#define SDMMC_SET_FIFOTH(m, r, t) (((m) & 0x7) << 28 | \
((r) & 0xFFF) << 16 | \
((t) & 0xFFF))
/* Internal DMAC interrupt defines */ /* Internal DMAC interrupt defines */
#define SDMMC_IDMAC_INT_AI BIT(9) #define SDMMC_IDMAC_INT_AI BIT(9)
#define SDMMC_IDMAC_INT_NI BIT(8) #define SDMMC_IDMAC_INT_NI BIT(8)
......
...@@ -129,6 +129,7 @@ struct dw_mci { ...@@ -129,6 +129,7 @@ struct dw_mci {
struct mmc_request *mrq; struct mmc_request *mrq;
struct mmc_command *cmd; struct mmc_command *cmd;
struct mmc_data *data; struct mmc_data *data;
unsigned int prev_blksz;
struct workqueue_struct *card_workqueue; struct workqueue_struct *card_workqueue;
/* DMA interface members*/ /* DMA interface members*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment