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:
parent
55eb094f8a
commit
47f7fd3a52
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
Loading…
Reference in New Issue