mmc: core: add mmc_read_tuning

Provide a function to the MMC hosts to read some blocks of data as part
of their tuning.

This function only returns the status of the read operation, not the
data read.

Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
Link: https://lore.kernel.org/r/20250818-mobileye-emmc-for-upstream-4-v4-5-34ecb3995e96@bootlin.com
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
Benoît Monin 2025-08-18 16:02:50 +02:00 committed by Ulf Hansson
parent f33bba9b64
commit 99e6cc80d5
2 changed files with 73 additions and 0 deletions

View File

@ -1077,3 +1077,75 @@ int mmc_sanitize(struct mmc_card *card, unsigned int timeout_ms)
return err;
}
EXPORT_SYMBOL_GPL(mmc_sanitize);
/**
* mmc_read_tuning() - read data blocks from the mmc
* @host: mmc host doing the read
* @blksz: data block size
* @blocks: number of blocks to read
*
* Read one or more blocks of data from the beginning of the mmc. This is a
* low-level helper for tuning operation. It is assumed that CMD23 can be used
* for multi-block read if the host supports it.
*
* Note: Allocate and free a temporary buffer to store the data read. The data
* is not available outside of the function, only the status of the read
* operation.
*
* Return: 0 in case of success, otherwise -EIO / -ENOMEM / -E2BIG
*/
int mmc_read_tuning(struct mmc_host *host, unsigned int blksz, unsigned int blocks)
{
struct mmc_request mrq = {};
struct mmc_command sbc = {};
struct mmc_command cmd = {};
struct mmc_command stop = {};
struct mmc_data data = {};
struct scatterlist sg;
void *buf;
unsigned int len;
if (blocks > 1) {
if (mmc_host_can_cmd23(host)) {
mrq.sbc = &sbc;
sbc.opcode = MMC_SET_BLOCK_COUNT;
sbc.arg = blocks;
sbc.flags = MMC_RSP_R1 | MMC_CMD_AC;
}
cmd.opcode = MMC_READ_MULTIPLE_BLOCK;
mrq.stop = &stop;
stop.opcode = MMC_STOP_TRANSMISSION;
stop.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
} else {
cmd.opcode = MMC_READ_SINGLE_BLOCK;
}
mrq.cmd = &cmd;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
mrq.data = &data;
data.flags = MMC_DATA_READ;
data.blksz = blksz;
data.blocks = blocks;
data.blk_addr = 0;
data.sg = &sg;
data.sg_len = 1;
data.timeout_ns = 1000000000;
if (check_mul_overflow(blksz, blocks, &len))
return -E2BIG;
buf = kmalloc(len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
sg_init_one(&sg, buf, len);
mmc_wait_for_req(host, &mrq);
kfree(buf);
if (sbc.error || cmd.error || data.error)
return -EIO;
return 0;
}
EXPORT_SYMBOL_GPL(mmc_read_tuning);

View File

@ -743,5 +743,6 @@ int mmc_send_status(struct mmc_card *card, u32 *status);
int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error);
int mmc_send_abort_tuning(struct mmc_host *host, u32 opcode);
int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd);
int mmc_read_tuning(struct mmc_host *host, unsigned int blksz, unsigned int blocks);
#endif /* LINUX_MMC_HOST_H */