feat(nxp-mmc): add data buffer
The prepare callback is used to set buffer details for a transfer. The
actual transfer may not occur immediately. Therefore, the details are
saved and applied to the next data transfer command. This mechanism also
helps distinguish between single and multi-block transfers based on the
transfer size.
Change-Id: I7bcfde0521ad628e5950dfc71482191ac35433d1
Signed-off-by: Ghennadi Procopciuc <ghennadi.procopciuc@nxp.com>
diff --git a/drivers/imx/usdhc/imx_usdhc.c b/drivers/imx/usdhc/imx_usdhc.c
index 610c3e6..dd99734 100644
--- a/drivers/imx/usdhc/imx_usdhc.c
+++ b/drivers/imx/usdhc/imx_usdhc.c
@@ -23,6 +23,13 @@
BIT_32(24U) | BIT_32(25U))
#define ADTC_MASK_ACMD (BIT_64(51U))
+struct imx_usdhc_device_data {
+ uint32_t addr;
+ uint32_t blk_size;
+ uint32_t blks;
+ bool valid;
+};
+
static void imx_usdhc_initialize(void);
static int imx_usdhc_send_cmd(struct mmc_cmd *cmd);
static int imx_usdhc_set_ios(unsigned int clk, unsigned int width);
@@ -40,6 +47,61 @@
};
static imx_usdhc_params_t imx_usdhc_params;
+static struct imx_usdhc_device_data imx_usdhc_data;
+
+static bool imx_usdhc_is_buf_valid(void)
+{
+ return imx_usdhc_data.valid;
+}
+
+static bool imx_usdhc_is_buf_multiblk(void)
+{
+ return imx_usdhc_data.blks > 1U;
+}
+
+static void imx_usdhc_inval_buf_data(void)
+{
+ imx_usdhc_data.valid = false;
+}
+
+static int imx_usdhc_save_buf_data(uintptr_t buf, size_t size)
+{
+ uint32_t block_size;
+ uint64_t blks;
+
+ if (size <= MMC_BLOCK_SIZE) {
+ block_size = (uint32_t)size;
+ } else {
+ block_size = MMC_BLOCK_SIZE;
+ }
+
+ if (buf > UINT32_MAX) {
+ return -EOVERFLOW;
+ }
+
+ imx_usdhc_data.addr = (uint32_t)buf;
+ imx_usdhc_data.blk_size = block_size;
+ blks = size / block_size;
+ imx_usdhc_data.blks = (uint32_t)blks;
+
+ imx_usdhc_data.valid = true;
+
+ return 0;
+}
+
+static void imx_usdhc_write_buf_data(void)
+{
+ uintptr_t reg_base = imx_usdhc_params.reg_base;
+ uint32_t addr, blks, blk_size;
+
+ addr = imx_usdhc_data.addr;
+ blks = imx_usdhc_data.blks;
+ blk_size = imx_usdhc_data.blk_size;
+
+ mmio_write_32(reg_base + DSADDR, addr);
+ mmio_write_32(reg_base + BLKATT, BLKATT_BLKCNT(blks) |
+ BLKATT_BLKSIZE(blk_size));
+}
#define IMX7_MMC_SRC_CLK_RATE (200 * 1000 * 1000)
static void imx_usdhc_set_clk(int clk)
@@ -120,13 +182,6 @@
return (cmd_idx == MMC_CMD(24)) || (cmd_idx == MMC_CMD(25));
}
-static bool is_multiple_block_transfer(const struct mmc_cmd *cmd)
-{
- unsigned int cmd_idx = cmd->cmd_idx;
-
- return cmd_idx == MMC_CMD(18) || cmd_idx == MMC_CMD(25);
-}
-
static bool is_data_transfer_cmd(const struct mmc_cmd *cmd)
{
uintptr_t reg_base = imx_usdhc_params.reg_base;
@@ -213,11 +268,6 @@
mmio_write_32(reg_base + INTSIGEN, 0);
udelay(1000);
- if (is_multiple_block_transfer(cmd)) {
- mixctl |= MIXCTRL_MSBSEL;
- mixctl |= MIXCTRL_BCEN;
- }
-
if (data) {
mixctl |= MIXCTRL_DMAEN;
}
@@ -226,6 +276,15 @@
mixctl |= MIXCTRL_DTDSEL;
}
+ if ((cmd->cmd_idx != MMC_CMD(55)) && imx_usdhc_is_buf_valid()) {
+ if (imx_usdhc_is_buf_multiblk()) {
+ mixctl |= MIXCTRL_MSBSEL | MIXCTRL_BCEN;
+ }
+
+ imx_usdhc_write_buf_data();
+ imx_usdhc_inval_buf_data();
+ }
+
/* Send the command */
mmio_write_32(reg_base + CMDARG, cmd->cmd_arg);
mmio_clrsetbits32(reg_base + MIXCTRL, MIXCTRL_DATMASK, mixctl);
@@ -316,13 +375,7 @@
static int imx_usdhc_prepare(int lba, uintptr_t buf, size_t size)
{
- uintptr_t reg_base = imx_usdhc_params.reg_base;
-
- mmio_write_32(reg_base + DSADDR, buf);
- mmio_write_32(reg_base + BLKATT,
- (size / MMC_BLOCK_SIZE) << 16 | MMC_BLOCK_SIZE);
-
- return 0;
+ return imx_usdhc_save_buf_data(buf, size);
}
static int imx_usdhc_read(int lba, uintptr_t buf, size_t size)
diff --git a/drivers/imx/usdhc/imx_usdhc.h b/drivers/imx/usdhc/imx_usdhc.h
index 8d64dfc..3ef2474 100644
--- a/drivers/imx/usdhc/imx_usdhc.h
+++ b/drivers/imx/usdhc/imx_usdhc.h
@@ -23,6 +23,8 @@
/* iMX MMC registers definition */
#define DSADDR 0x000U
#define BLKATT 0x004U
+#define BLKATT_BLKCNT(x) (((x) << 16U) & GENMASK_32(31U, 16U))
+#define BLKATT_BLKSIZE(x) ((x) & GENMASK_32(12U, 0U))
#define CMDARG 0x008U
#define CMDRSP0 0x010U
#define CMDRSP1 0x014U