ACMD22 is used to verify the previously write operation. Normally, it returns the number of written sectors as u32. SDUC, however, returns it as u64. This is not a superfluous requirement, because SDUC writes may exceeds 2TB. For Linux mmc however, the previously write operation could not be more than the block layer limits, thus we make room for a u64 and cast the returning value to u32. Signed-off-by: Avri Altman <avri.altman@xxxxxxx> --- drivers/mmc/core/block.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 0484d7031c0c..894bd53c49b2 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -48,6 +48,7 @@ #include <linux/mmc/sd.h> #include <linux/uaccess.h> +#include <asm/unaligned.h> #include "queue.h" #include "block.h" @@ -948,11 +949,10 @@ static int mmc_sd_num_wr_blocks(struct mmc_card *card, u32 *written_blocks) int err; u32 result; __be32 *blocks; - + u8 resp_sz; struct mmc_request mrq = {}; struct mmc_command cmd = {}; struct mmc_data data = {}; - struct scatterlist sg; err = mmc_app_cmd(card->host, card); @@ -963,7 +963,14 @@ static int mmc_sd_num_wr_blocks(struct mmc_card *card, u32 *written_blocks) cmd.arg = 0; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; - data.blksz = 4; + /* + * Normally, ACMD22 returns the number of written sectors as u32. + * SDUC, however, returns it as u64. This is not a superfluous + * requirement, because SDUC writes may exceed 2TB. + */ + resp_sz = mmc_card_ult_capacity(card) ? 8 : 4; + + data.blksz = resp_sz; data.blocks = 1; data.flags = MMC_DATA_READ; data.sg = &sg; @@ -973,15 +980,25 @@ static int mmc_sd_num_wr_blocks(struct mmc_card *card, u32 *written_blocks) mrq.cmd = &cmd; mrq.data = &data; - blocks = kmalloc(4, GFP_KERNEL); + blocks = kmalloc(resp_sz, GFP_KERNEL); if (!blocks) return -ENOMEM; - sg_init_one(&sg, blocks, 4); + sg_init_one(&sg, blocks, resp_sz); mmc_wait_for_req(card->host, &mrq); - result = ntohl(*blocks); + if (mmc_card_ult_capacity(card)) { + u64 blocks_64 = get_unaligned_be64(blocks); + /* + * For Linux mmc however, the previously write operation could + * not be more than the block layer limits, thus just make room + * for a u64 and cast the response back to u32. + */ + result = blocks_64 > UINT_MAX ? UINT_MAX : (u32)blocks_64; + } else { + result = ntohl(*blocks); + } kfree(blocks); if (cmd.error || data.error) -- 2.25.1