aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/core/sd_ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/core/sd_ops.c')
-rw-r--r--drivers/mmc/core/sd_ops.c134
1 files changed, 134 insertions, 0 deletions
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index cd86463dd306..5d03b6b6e69b 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -8,6 +8,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/export.h>
+#include <linux/ktime.h>
#include <linux/scatterlist.h>
#include <linux/mmc/host.h>
@@ -417,3 +418,136 @@ int mmc_app_sd_status(struct mmc_card *card, void *ssr)
return 0;
}
+
+
+int mmc_sd_write_ext_reg(struct mmc_card *card, u8 fno, u8 page, u16 offset,
+ u8 reg_data)
+{
+ struct mmc_host *host = card->host;
+ struct mmc_request mrq = {};
+ struct mmc_command cmd = {};
+ struct mmc_data data = {};
+ struct scatterlist sg;
+ u8 *reg_buf;
+
+ reg_buf = card->ext_reg_buf;
+ memset(reg_buf, 0, 512);
+
+ mrq.cmd = &cmd;
+ mrq.data = &data;
+
+ /*
+ * Arguments of CMD49:
+ * [31:31] MIO (0 = memory).
+ * [30:27] FNO (function number).
+ * [26:26] MW - mask write mode (0 = disable).
+ * [25:18] page number.
+ * [17:9] offset address.
+ * [8:0] length (0 = 1 byte).
+ */
+ cmd.arg = fno << 27 | page << 18 | offset << 9;
+
+ /* The first byte in the buffer is the data to be written. */
+ reg_buf[0] = reg_data;
+
+ data.flags = MMC_DATA_WRITE;
+ data.blksz = 512;
+ data.blocks = 1;
+ data.sg = &sg;
+ data.sg_len = 1;
+ sg_init_one(&sg, reg_buf, 512);
+
+ cmd.opcode = SD_WRITE_EXTR_SINGLE;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ mmc_set_data_timeout(&data, card);
+ mmc_wait_for_req(host, &mrq);
+
+ /*
+ * Note that, the SD card is allowed to signal busy on DAT0 up to 1s
+ * after the CMD49. Although, let's leave this to be managed by the
+ * caller.
+ */
+
+ if (cmd.error)
+ return cmd.error;
+ if (data.error)
+ return data.error;
+
+ return 0;
+}
+
+int mmc_sd_read_ext_reg(struct mmc_card *card, u8 fno, u8 page,
+ u16 offset, u16 len, u8 *reg_buf)
+{
+ u32 cmd_args;
+
+ /*
+ * Command arguments of CMD48:
+ * [31:31] MIO (0 = memory).
+ * [30:27] FNO (function number).
+ * [26:26] reserved (0).
+ * [25:18] page number.
+ * [17:9] offset address.
+ * [8:0] length (0 = 1 byte, 1ff = 512 bytes).
+ */
+ cmd_args = fno << 27 | page << 18 | offset << 9 | (len - 1);
+
+ return mmc_send_adtc_data(card, card->host, SD_READ_EXTR_SINGLE,
+ cmd_args, reg_buf, 512);
+}
+
+static int mmc_sd_cmdq_switch(struct mmc_card *card, bool enable)
+{
+ int err;
+ u8 reg = 0;
+ u8 *reg_buf = card->ext_reg_buf;
+ ktime_t timeout;
+ /*
+ * SD offers two command queueing modes - sequential (in-order) and
+ * voluntary (out-of-order). Apps Class A2 performance is only
+ * guaranteed for voluntary CQ (bit 1 = 0), so use that in preference
+ * to sequential.
+ */
+ if (enable)
+ reg = BIT(0);
+
+ /* Performance enhancement register byte 262 controls command queueing */
+ err = mmc_sd_write_ext_reg(card, card->ext_perf.fno, card->ext_perf.page,
+ card->ext_perf.offset + 262, reg);
+ if (err)
+ goto out;
+
+ /* Poll the register - cards may have a lazy init/deinit sequence. */
+ timeout = ktime_add_ms(ktime_get(), 10);
+ while (1) {
+ err = mmc_sd_read_ext_reg(card, card->ext_perf.fno, card->ext_perf.page,
+ card->ext_perf.offset + 262, 1, reg_buf);
+ if (err)
+ break;
+ if ((reg_buf[0] & BIT(0)) == reg)
+ break;
+ if (ktime_after(ktime_get(), timeout)) {
+ err = -EBADMSG;
+ break;
+ }
+ usleep_range(100, 200);
+ }
+out:
+ if (!err)
+ card->ext_csd.cmdq_en = enable;
+
+ return err;
+}
+
+int mmc_sd_cmdq_enable(struct mmc_card *card)
+{
+ return mmc_sd_cmdq_switch(card, true);
+}
+EXPORT_SYMBOL_GPL(mmc_sd_cmdq_enable);
+
+int mmc_sd_cmdq_disable(struct mmc_card *card)
+{
+ return mmc_sd_cmdq_switch(card, false);
+}
+EXPORT_SYMBOL_GPL(mmc_sd_cmdq_disable);