mmc: dw_mmc: adjust the fifoth with block size
Subsystems (2)
PATCH 15/22 mmc: dw_mmc: adjust the fifoth with block size
This change helps to choose msize, rx_watermark and tx_watermak 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 <[email protected]>
Tested-by Alim Akhtar <[email protected]>
--- drivers/mmc/host/dw_mmc.c | 74 ++++++++++++++++++++++++++++++++++++++++++-- drivers/mmc/host/dw_mmc.h | 4 ++ include/linux/mmc/dw_mmc.h | 1 + 3 files changed, 76 insertions(+), 3 deletions(-)
drivers/mmc/host/dw_mmc.c +71 -3
@@ -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 );
drivers/mmc/host/dw_mmc.h +4 -0
@@ -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)
include/linux/mmc/dw_mmc.h +1 -0
@@ -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*/
Signature · 1 line
1.7.0.4