mmc: core: Adjust ACMD22 to SDUC
authorAvri Altman <avri.altman@wdc.com>
Sun, 6 Oct 2024 05:11:45 +0000 (08:11 +0300)
committerUlf Hansson <ulf.hansson@linaro.org>
Mon, 14 Oct 2024 10:36:50 +0000 (12:36 +0200)
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.

Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Avri Altman <avri.altman@wdc.com>
Link: https://lore.kernel.org/r/20241006051148.160278-8-avri.altman@wdc.com
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
[Stephen Rothwell: Fix build error when moving to new rc from Linus's tree]
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
drivers/mmc/core/block.c

index 26a67d2..04f3165 100644 (file)
@@ -50,6 +50,7 @@
 #include <linux/mmc/sd.h>
 
 #include <linux/uaccess.h>
+#include <linux/unaligned.h>
 
 #include "queue.h"
 #include "block.h"
@@ -993,11 +994,10 @@ static int mmc_sd_num_wr_blocks(struct mmc_card *card, u32 *written_blocks)
        int err;
        u32 result;
        __be32 *blocks;
-
+       u8 resp_sz = mmc_card_ult_capacity(card) ? 8 : 4;
        struct mmc_request mrq = {};
        struct mmc_command cmd = {};
        struct mmc_data data = {};
-
        struct scatterlist sg;
 
        err = mmc_app_cmd(card->host, card);
@@ -1008,7 +1008,7 @@ 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;
+       data.blksz = resp_sz;
        data.blocks = 1;
        data.flags = MMC_DATA_READ;
        data.sg = &sg;
@@ -1018,15 +1018,27 @@ 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)) {
+               /*
+                * 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.
+                * 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 = clamp_val(get_unaligned_be64(blocks), 0, UINT_MAX);
+       } else {
+               result = ntohl(*blocks);
+       }
        kfree(blocks);
 
        if (cmd.error || data.error)