diff mbox series

[V2,4/5] mmc: Add option to adjust b_max before long read

Message ID 20200404104506.386314-4-marek.vasut+renesas@gmail.com
State New
Headers show
Series [V2,1/5] common: bouncebuf: Permit passing custom alignment check function | expand

Commit Message

Marek Vasut April 4, 2020, 10:45 a.m. UTC
Add getter function which permits adjusting the maximum number of
blocks that could be read in a single sustained read transfer based
on the location of the source/target buffer and length, before such
transfer starts.

This is mainly useful on systems which have various DMA restrictions
for different memory locations, e.g. DMA limited to 32bit addresses,
and where a bounce buffer is used to work around such restrictions.
Since the U-Boot bounce buffer is mallocated, it's size is limited
by the malloc area size, and the read transfer to such a buffer must
also be limited. However, as not all areas are limited equally, the
b_max should be adjusted accordinly as needed to avoid degrading
performance unnecessarily.

Signed-off-by: Marek Vasut <marek.vasut+renesas at gmail.com>
Cc: Daniel Schwierzeck <daniel.schwierzeck at gmail.com>
Cc: Masahiro Yamada <yamada.masahiro at socionext.com>
Cc: Peng Fan <peng.fan at nxp.com>
Cc: Simon Glass <sjg at chromium.org>
Cc: Tom Rini <trini at konsulko.com>
---
V2: No change
---
 drivers/mmc/mmc-uclass.c | 16 ++++++++++++++++
 drivers/mmc/mmc.c        | 16 ++++++++++++++--
 include/mmc.h            | 16 ++++++++++++++++
 3 files changed, 46 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c
index c75892a72c..cb26d841be 100644
--- a/drivers/mmc/mmc-uclass.c
+++ b/drivers/mmc/mmc-uclass.c
@@ -13,6 +13,22 @@ 
 #include <linux/compat.h>
 #include "mmc_private.h"
 
+int dm_mmc_get_b_max(struct udevice *dev, void *dst, lbaint_t blkcnt)
+{
+	struct dm_mmc_ops *ops = mmc_get_ops(dev);
+	struct mmc *mmc = mmc_get_mmc_dev(dev);
+
+	if (ops->get_b_max)
+		return ops->get_b_max(dev, dst, blkcnt);
+	else
+		return mmc->cfg->b_max;
+}
+
+int mmc_get_b_max(struct mmc *mmc, void *dst, lbaint_t blkcnt)
+{
+	return dm_mmc_get_b_max(mmc->dev, dst, blkcnt);
+}
+
 int dm_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
 		    struct mmc_data *data)
 {
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 3e36566693..e22834398f 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -409,6 +409,16 @@  static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,
 	return blkcnt;
 }
 
+#if !CONFIG_IS_ENABLED(DM_MMC)
+static int mmc_get_b_max(struct mmc *mmc, void *dst, lbaint_t blkcnt)
+{
+	if (mmc->cfg->ops->get_b_max)
+		return mmc->cfg->ops->get_b_max(mmc, dst, blkcnt);
+	else
+		return mmc->cfg->b_max;
+}
+#endif
+
 #if CONFIG_IS_ENABLED(BLK)
 ulong mmc_bread(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *dst)
 #else
@@ -422,6 +432,7 @@  ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
 	int dev_num = block_dev->devnum;
 	int err;
 	lbaint_t cur, blocks_todo = blkcnt;
+	uint b_max;
 
 	if (blkcnt == 0)
 		return 0;
@@ -451,9 +462,10 @@  ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
 		return 0;
 	}
 
+	b_max = mmc_get_b_max(mmc, dst, blkcnt);
+
 	do {
-		cur = (blocks_todo > mmc->cfg->b_max) ?
-			mmc->cfg->b_max : blocks_todo;
+		cur = (blocks_todo > b_max) ? b_max : blocks_todo;
 		if (mmc_read_blocks(mmc, dst, start, cur) != cur) {
 			pr_debug("%s: Failed to read blocks\n", __func__);
 			return 0;
diff --git a/include/mmc.h b/include/mmc.h
index e83c22423b..a24e58b867 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -488,6 +488,19 @@  struct dm_mmc_ops {
 	 * @return 0 if not present, 1 if present, -ve on error
 	 */
 	int (*host_power_cycle)(struct udevice *dev);
+
+	/**
+	 * get_b_max - get maximum length of single transfer
+	 *	       Called before reading blocks from the card,
+	 *	       useful for system which have e.g. DMA limits
+	 *	       on various memory ranges.
+	 *
+	 * @dev:	Device to check
+	 * @dst:	Destination buffer in memory
+	 * @blkcnt:	Total number of blocks in this transfer
+	 * @return maximum number of blocks for this transfer
+	 */
+	int (*get_b_max)(struct udevice *dev, void *dst, lbaint_t blkcnt);
 };
 
 #define mmc_get_ops(dev)        ((struct dm_mmc_ops *)(dev)->driver->ops)
@@ -501,6 +514,7 @@  int dm_mmc_execute_tuning(struct udevice *dev, uint opcode);
 int dm_mmc_wait_dat0(struct udevice *dev, int state, int timeout_us);
 int dm_mmc_host_power_cycle(struct udevice *dev);
 int dm_mmc_deferred_probe(struct udevice *dev);
+int dm_mmc_get_b_max(struct udevice *dev, void *dst, lbaint_t blkcnt);
 
 /* Transition functions for compatibility */
 int mmc_set_ios(struct mmc *mmc);
@@ -511,6 +525,7 @@  int mmc_wait_dat0(struct mmc *mmc, int state, int timeout_us);
 int mmc_set_enhanced_strobe(struct mmc *mmc);
 int mmc_host_power_cycle(struct mmc *mmc);
 int mmc_deferred_probe(struct mmc *mmc);
+int mmc_get_b_max(struct mmc *mmc, void *dst, lbaint_t blkcnt);
 
 #else
 struct mmc_ops {
@@ -521,6 +536,7 @@  struct mmc_ops {
 	int (*getcd)(struct mmc *mmc);
 	int (*getwp)(struct mmc *mmc);
 	int (*host_power_cycle)(struct mmc *mmc);
+	int (*get_b_max)(struct mmc *mmc, void *dst, lbaint_t blkcnt);
 };
 #endif