|
@@ -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);
|