mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 10:49:33 +00:00
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: Seungwon Jeon <tgih.jun@samsung.com> Tested-by: Alim Akhtar <alim.akhtar@samsung.com> Signed-off-by: Chris Ball <cjb@laptop.org>
This commit is contained in:
parent
1f44a2a557
commit
524268990e
3 changed files with 76 additions and 3 deletions
|
@ -529,6 +529,47 @@ static void dw_mci_post_req(struct mmc_host *mmc,
|
|||
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)
|
||||
{
|
||||
int sg_len;
|
||||
|
@ -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,
|
||||
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 */
|
||||
temp = mci_readl(host, CTRL);
|
||||
temp |= SDMMC_CTRL_DMA_ENABLE;
|
||||
|
@ -603,6 +652,21 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
|
|||
temp = mci_readl(host, CTRL);
|
||||
temp &= ~SDMMC_CTRL_DMA_ENABLE;
|
||||
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)
|
|||
fifo_size = host->pdata->fifo_depth;
|
||||
}
|
||||
host->fifo_depth = fifo_size;
|
||||
host->fifoth_val = ((0x2 << 28) | ((fifo_size/2 - 1) << 16) |
|
||||
((fifo_size/2) << 0));
|
||||
host->fifoth_val =
|
||||
SDMMC_SET_FIFOTH(0x2, fifo_size / 2 - 1, fifo_size / 2);
|
||||
mci_writel(host, FIFOTH, host->fifoth_val);
|
||||
|
||||
/* disable clock to CIU */
|
||||
|
@ -2552,8 +2616,12 @@ int dw_mci_resume(struct dw_mci *host)
|
|||
if (host->use_dma && host->dma_ops->init)
|
||||
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);
|
||||
host->prev_blksz = 0;
|
||||
|
||||
/* Put in max timeout */
|
||||
mci_writel(host, TMOUT, 0xFFFFFFFF);
|
||||
|
|
|
@ -128,6 +128,10 @@
|
|||
#define SDMMC_CMD_INDX(n) ((n) & 0x1F)
|
||||
/* Status register defines */
|
||||
#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 */
|
||||
#define SDMMC_IDMAC_INT_AI BIT(9)
|
||||
#define SDMMC_IDMAC_INT_NI BIT(8)
|
||||
|
|
|
@ -129,6 +129,7 @@ struct dw_mci {
|
|||
struct mmc_request *mrq;
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_data *data;
|
||||
unsigned int prev_blksz;
|
||||
struct workqueue_struct *card_workqueue;
|
||||
|
||||
/* DMA interface members*/
|
||||
|
|
Loading…
Reference in a new issue