diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 26760895f9..209fb5ebe6 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -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 diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c index 0d3574ffb7..3d8adc7094 100644 --- a/drivers/block/blk-uclass.c +++ b/drivers/block/blk-uclass.c @@ -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) { diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 6d7cfdfd45..7e8fde320e 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -6,8 +6,8 @@ * SPDX-License-Identifier: GPL-2.0+ */ -#include #include +#include #include #include #include @@ -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, diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 7feb225186..06c3b8fc14 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -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, diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 14141601a3..941005a19e 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -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) diff --git a/drivers/mmc/mmc_private.h b/drivers/mmc/mmc_private.h index 1290eed590..f76e6f8490 100644 --- a/drivers/mmc/mmc_private.h +++ b/drivers/mmc/mmc_private.h @@ -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)) diff --git a/include/blk.h b/include/blk.h index 215929c945..d4a259335c 100644 --- a/include/blk.h +++ b/include/blk.h @@ -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, diff --git a/include/mmc.h b/include/mmc.h index 8bf4761457..0d23e40dd5 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -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 *