blk/mmc: add function blk_dread_prepare

This function prepares to read data without confirming completed.
We can use it to prefetch data and run other process.

Signed-off-by: Jason Zhu <jason.zhu@rock-chips.com>
Change-Id: I76116c25dfdb7559b80a0216c414189e85409a3e
This commit is contained in:
Jason Zhu 2020-06-05 20:39:31 +08:00 committed by Jianhong Chen
parent 55eb094f8a
commit 47f7fd3a52
8 changed files with 329 additions and 1 deletions

View File

@ -22,6 +22,15 @@ config SPL_BLK
be partitioned into several areas, called 'partitions' in U-Boot.
A filesystem can be placed in each partition.
config SPL_BLK_READ_PREPARE
bool "Support block devices prepare to read data in SPL"
depends on SPL_BLK
help
Enable support for block devices to prefetch data. MMC and mtd_blk
devices can be attached to block devices. It is applied to prefetch
data in the background and the device run some other process in the
same time.
config BLOCK_CACHE
bool "Use block device cache"
default n

View File

@ -454,6 +454,20 @@ unsigned long blk_dread(struct blk_desc *block_dev, lbaint_t start,
return blks_read;
}
#ifdef CONFIG_SPL_BLK_READ_PREPARE
unsigned long blk_dread_prepare(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt, void *buffer)
{
struct udevice *dev = block_dev->bdev;
const struct blk_ops *ops = blk_get_ops(dev);
if (!ops->read)
return -ENOSYS;
return ops->read_prepare(dev, start, blkcnt, buffer);
}
#endif
unsigned long blk_dwrite(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt, const void *buffer)
{

View File

@ -6,8 +6,8 @@
* SPDX-License-Identifier: GPL-2.0+
*/
#include <bouncebuf.h>
#include <common.h>
#include <bouncebuf.h>
#include <errno.h>
#include <malloc.h>
#include <memalign.h>
@ -437,6 +437,135 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
return ret;
}
#ifdef CONFIG_SPL_BLK_READ_PREPARE
#ifdef CONFIG_DM_MMC
static int dwmci_send_cmd_prepare(struct udevice *dev, struct mmc_cmd *cmd,
struct mmc_data *data)
{
struct mmc *mmc = mmc_get_mmc_dev(dev);
#else
static int dwmci_send_cmd_prepare(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
#endif
struct dwmci_host *host = mmc->priv;
struct dwmci_idmac *cur_idmac;
int ret = 0, flags = 0, i;
unsigned int timeout = 500;
u32 retry = 100000;
u32 mask;
ulong start = get_timer(0);
struct bounce_buffer bbstate;
cur_idmac = malloc(ROUND(DIV_ROUND_UP(data->blocks, 8) *
sizeof(struct dwmci_idmac),
ARCH_DMA_MINALIGN) + ARCH_DMA_MINALIGN - 1);
if (!cur_idmac)
return -ENODATA;
while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) {
if (get_timer(start) > timeout) {
debug("%s: Timeout on data busy\n", __func__);
return -ETIMEDOUT;
}
}
dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL);
if (data) {
if (host->fifo_mode) {
dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize);
dwmci_writel(host, DWMCI_BYTCNT,
data->blocksize * data->blocks);
dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET);
} else {
if (data->flags == MMC_DATA_READ) {
bounce_buffer_start(&bbstate, (void *)data->dest,
data->blocksize *
data->blocks, GEN_BB_WRITE);
} else {
bounce_buffer_start(&bbstate, (void *)data->src,
data->blocksize *
data->blocks, GEN_BB_READ);
}
dwmci_prepare_data(host, data, cur_idmac,
bbstate.bounce_buffer);
}
}
dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg);
if (data)
flags = dwmci_set_transfer_mode(host, data);
if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
return -1;
if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
flags |= DWMCI_CMD_ABORT_STOP;
else
flags |= DWMCI_CMD_PRV_DAT_WAIT;
if (cmd->resp_type & MMC_RSP_PRESENT) {
flags |= DWMCI_CMD_RESP_EXP;
if (cmd->resp_type & MMC_RSP_136)
flags |= DWMCI_CMD_RESP_LENGTH;
}
if (cmd->resp_type & MMC_RSP_CRC)
flags |= DWMCI_CMD_CHECK_CRC;
flags |= (cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG);
debug("Sending CMD%d\n", cmd->cmdidx);
dwmci_writel(host, DWMCI_CMD, flags);
for (i = 0; i < retry; i++) {
mask = dwmci_readl(host, DWMCI_RINTSTS);
if (mask & DWMCI_INTMSK_CDONE) {
if (!data)
dwmci_writel(host, DWMCI_RINTSTS, mask);
break;
}
}
if (i == retry) {
debug("%s: Timeout.\n", __func__);
return -ETIMEDOUT;
}
if (mask & DWMCI_INTMSK_RTO) {
/*
* Timeout here is not necessarily fatal. (e)MMC cards
* will splat here when they receive CMD55 as they do
* not support this command and that is exactly the way
* to tell them apart from SD cards. Thus, this output
* below shall be debug(). eMMC cards also do not favor
* CMD8, please keep that in mind.
*/
debug("%s: Response Timeout.\n", __func__);
return -ETIMEDOUT;
} else if (mask & DWMCI_INTMSK_RE) {
debug("%s: Response Error.\n", __func__);
return -EIO;
}
if (cmd->resp_type & MMC_RSP_PRESENT) {
if (cmd->resp_type & MMC_RSP_136) {
cmd->response[0] = dwmci_readl(host, DWMCI_RESP3);
cmd->response[1] = dwmci_readl(host, DWMCI_RESP2);
cmd->response[2] = dwmci_readl(host, DWMCI_RESP1);
cmd->response[3] = dwmci_readl(host, DWMCI_RESP0);
} else {
cmd->response[0] = dwmci_readl(host, DWMCI_RESP0);
}
}
return ret;
}
#endif
static int dwmci_setup_bus(struct dwmci_host *host, u32 freq)
{
u32 div, status;
@ -669,6 +798,9 @@ int dwmci_probe(struct udevice *dev)
const struct dm_mmc_ops dm_dwmci_ops = {
.card_busy = dwmci_card_busy,
.send_cmd = dwmci_send_cmd,
#ifdef CONFIG_SPL_BLK_READ_PREPARE
.send_cmd_prepare = dwmci_send_cmd_prepare,
#endif
.set_ios = dwmci_set_ios,
.get_cd = dwmci_get_cd,
.execute_tuning = dwmci_execute_tuning,

View File

@ -32,11 +32,37 @@ int dm_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
return ret;
}
#ifdef CONFIG_SPL_BLK_READ_PREPARE
int dm_mmc_send_cmd_prepare(struct udevice *dev, struct mmc_cmd *cmd,
struct mmc_data *data)
{
struct mmc *mmc = mmc_get_mmc_dev(dev);
struct dm_mmc_ops *ops = mmc_get_ops(dev);
int ret;
mmmc_trace_before_send(mmc, cmd);
if (ops->send_cmd_prepare)
ret = ops->send_cmd_prepare(dev, cmd, data);
else
ret = -ENOSYS;
mmmc_trace_after_send(mmc, cmd, ret);
return ret;
}
#endif
int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
return dm_mmc_send_cmd(mmc->dev, cmd, data);
}
#ifdef CONFIG_SPL_BLK_READ_PREPARE
int mmc_send_cmd_prepare(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
return dm_mmc_send_cmd_prepare(mmc->dev, cmd, data);
}
#endif
bool mmc_card_busy(struct mmc *mmc)
{
struct dm_mmc_ops *ops = mmc_get_ops(mmc->dev);
@ -289,6 +315,9 @@ static int mmc_blk_probe(struct udevice *dev)
static const struct blk_ops mmc_blk_ops = {
.read = mmc_bread,
#ifdef CONFIG_SPL_BLK_READ_PREPARE
.read_prepare = mmc_bread_prepare,
#endif
#ifndef CONFIG_SPL_BUILD
.write = mmc_bwrite,
.erase = mmc_berase,

View File

@ -263,6 +263,37 @@ static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,
return blkcnt;
}
#ifdef CONFIG_SPL_BLK_READ_PREPARE
static int mmc_read_blocks_prepare(struct mmc *mmc, void *dst, lbaint_t start,
lbaint_t blkcnt)
{
struct mmc_cmd cmd;
struct mmc_data data;
if (blkcnt > 1)
cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
else
cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
if (mmc->high_capacity)
cmd.cmdarg = start;
else
cmd.cmdarg = start * mmc->read_bl_len;
cmd.resp_type = MMC_RSP_R1;
data.dest = dst;
data.blocks = blkcnt;
data.blocksize = mmc->read_bl_len;
data.flags = MMC_DATA_READ;
if (mmc_send_cmd_prepare(mmc, &cmd, &data))
return 0;
return blkcnt;
}
#endif
#if CONFIG_IS_ENABLED(BLK)
ulong mmc_bread(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *dst)
#else
@ -339,6 +370,77 @@ re_init_retry:
return blkcnt;
}
#ifdef CONFIG_SPL_BLK_READ_PREPARE
#if CONFIG_IS_ENABLED(BLK)
ulong mmc_bread_prepare(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *dst)
#else
ulong mmc_bread_prepare(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
void *dst)
#endif
{
#if CONFIG_IS_ENABLED(BLK)
struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
#endif
int dev_num = block_dev->devnum;
int timeout = 0;
int err;
if (blkcnt == 0)
return 0;
struct mmc *mmc = find_mmc_device(dev_num);
if (!mmc)
return 0;
if (CONFIG_IS_ENABLED(MMC_TINY))
err = mmc_switch_part(mmc, block_dev->hwpart);
else
err = blk_dselect_hwpart(block_dev, block_dev->hwpart);
if (err < 0)
return 0;
if ((start + blkcnt) > block_dev->lba) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
start + blkcnt, block_dev->lba);
#endif
return 0;
}
if (mmc_set_blocklen(mmc, mmc->read_bl_len)) {
debug("%s: Failed to set blocklen\n", __func__);
return 0;
}
if (mmc_read_blocks_prepare(mmc, dst, start, blkcnt) != blkcnt) {
debug("%s: Failed to read blocks\n", __func__);
re_init_retry:
timeout++;
/*
* Try re-init seven times.
*/
if (timeout > 7) {
printf("Re-init retry timeout\n");
return 0;
}
mmc->has_init = 0;
if (mmc_init(mmc))
return 0;
if (mmc_read_blocks_prepare(mmc, dst, start, blkcnt) != blkcnt) {
printf("%s: Re-init mmc_read_blocks_prepare error\n",
__func__);
goto re_init_retry;
}
}
return blkcnt;
}
#endif
void mmc_set_clock(struct mmc *mmc, uint clock)
{
if (clock > mmc->cfg->f_max)

View File

@ -14,6 +14,10 @@
extern int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data);
#ifdef CONFIG_SPL_BLK_READ_PREPARE
int mmc_send_cmd_prepare(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data);
#endif
extern int mmc_send_status(struct mmc *mmc, int timeout);
extern int mmc_set_blocklen(struct mmc *mmc, int len);
#ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
@ -23,9 +27,17 @@ void mmc_adapter_card_type_ident(void);
#if CONFIG_IS_ENABLED(BLK)
ulong mmc_bread(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
void *dst);
#ifdef CONFIG_SPL_BLK_READ_PREPARE
ulong mmc_bread_prepare(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
void *dst);
#endif
#else
ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
void *dst);
#ifdef CONFIG_SPL_BLK_READ_PREPARE
ulong mmc_bread_prepare(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
void *dst);
#endif
#endif
#if !(defined(CONFIG_SPL_BUILD) && !defined(CONFIG_SPL_SAVEENV))

View File

@ -224,6 +224,20 @@ struct blk_ops {
unsigned long (*read)(struct udevice *dev, lbaint_t start,
lbaint_t blkcnt, void *buffer);
/**
* read_prepare() - read from a block device
*
* @dev: Device to read from
* @start: Start block number to read (0=first)
* @blkcnt: Number of blocks to read
* @buffer: Destination buffer for data read
* @return number of blocks read, or -ve error number (see the
* IS_ERR_VALUE() macro
*/
#ifdef CONFIG_SPL_BLK_READ_PREPARE
unsigned long (*read_prepare)(struct udevice *dev, lbaint_t start,
lbaint_t blkcnt, void *buffer);
#endif
/**
* write() - write to a block device
*
@ -279,6 +293,10 @@ struct blk_ops {
*/
unsigned long blk_dread(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt, void *buffer);
#ifdef CONFIG_SPL_BLK_READ_PREPARE
unsigned long blk_dread_prepare(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt, void *buffer);
#endif
unsigned long blk_dwrite(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt, const void *buffer);
unsigned long blk_derase(struct blk_desc *block_dev, lbaint_t start,

View File

@ -416,6 +416,18 @@ struct dm_mmc_ops {
int (*send_cmd)(struct udevice *dev, struct mmc_cmd *cmd,
struct mmc_data *data);
/**
* send_cmd_prepare() - Send a command to the MMC device
*
* @dev: Device to receive the command
* @cmd: Command to send
* @data: Additional data to send/receive
* @return 0 if OK, -ve on error
*/
#ifdef CONFIG_SPL_BLK_READ_PREPARE
int (*send_cmd_prepare)(struct udevice *dev, struct mmc_cmd *cmd,
struct mmc_data *data);
#endif
/**
* card_busy() - Query the card device status
*