summaryrefslogtreecommitdiffstats
path: root/drivers/mmc/card
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/card')
-rw-r--r--drivers/mmc/card/block.c30
-rw-r--r--drivers/mmc/card/block.h1
-rw-r--r--drivers/mmc/card/mmc_test.c308
-rw-r--r--drivers/mmc/card/queue.c4
-rw-r--r--drivers/mmc/card/queue.h2
5 files changed, 327 insertions, 18 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 2206d4477dbb..c3335112e68c 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -142,8 +142,6 @@ static inline void mmc_blk_clear_packed(struct mmc_queue_req *mqrq)
{
struct mmc_packed *packed = mqrq->packed;
- BUG_ON(!packed);
-
mqrq->cmd_type = MMC_PACKED_NONE;
packed->nr_entries = MMC_PACKED_NR_ZERO;
packed->idx_failure = MMC_PACKED_NR_IDX;
@@ -1443,8 +1441,6 @@ static int mmc_blk_packed_err_check(struct mmc_card *card,
int err, check, status;
u8 *ext_csd;
- BUG_ON(!packed);
-
packed->retries--;
check = mmc_blk_err_check(card, areq);
err = get_card_status(card, &status, 0);
@@ -1673,6 +1669,18 @@ static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
u8 max_packed_rw = 0;
u8 reqs = 0;
+ /*
+ * We don't need to check packed for any further
+ * operation of packed stuff as we set MMC_PACKED_NONE
+ * and return zero for reqs if geting null packed. Also
+ * we clean the flag of MMC_BLK_PACKED_CMD to avoid doing
+ * it again when removing blk req.
+ */
+ if (!mqrq->packed) {
+ md->flags &= (~MMC_BLK_PACKED_CMD);
+ goto no_packed;
+ }
+
if (!(md->flags & MMC_BLK_PACKED_CMD))
goto no_packed;
@@ -1782,8 +1790,6 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq,
u8 hdr_blocks;
u8 i = 1;
- BUG_ON(!packed);
-
mqrq->cmd_type = MMC_PACKED_WRITE;
packed->blocks = 0;
packed->idx_failure = MMC_PACKED_NR_IDX;
@@ -1887,8 +1893,6 @@ static int mmc_blk_end_packed_req(struct mmc_queue_req *mq_rq)
int idx = packed->idx_failure, i = 0;
int ret = 0;
- BUG_ON(!packed);
-
while (!list_empty(&packed->list)) {
prq = list_entry_rq(packed->list.next);
if (idx == i) {
@@ -1917,8 +1921,6 @@ static void mmc_blk_abort_packed_req(struct mmc_queue_req *mq_rq)
struct request *prq;
struct mmc_packed *packed = mq_rq->packed;
- BUG_ON(!packed);
-
while (!list_empty(&packed->list)) {
prq = list_entry_rq(packed->list.next);
list_del_init(&prq->queuelist);
@@ -1935,8 +1937,6 @@ static void mmc_blk_revert_packed_req(struct mmc_queue *mq,
struct request_queue *q = mq->queue;
struct mmc_packed *packed = mq_rq->packed;
- BUG_ON(!packed);
-
while (!list_empty(&packed->list)) {
prq = list_entry_rq(packed->list.prev);
if (prq->queuelist.prev != &packed->list) {
@@ -2144,7 +2144,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
return 0;
}
-static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
+int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
{
int ret;
struct mmc_blk_data *md = mq->data;
@@ -2265,7 +2265,6 @@ again:
if (ret)
goto err_putdisk;
- md->queue.issue_fn = mmc_blk_issue_rq;
md->queue.data = md;
md->disk->major = MMC_BLOCK_MAJOR;
@@ -2303,7 +2302,8 @@ again:
set_capacity(md->disk, size);
if (mmc_host_cmd23(card->host)) {
- if (mmc_card_mmc(card) ||
+ if ((mmc_card_mmc(card) &&
+ card->csd.mmca_vsn >= CSD_SPEC_VER_3) ||
(mmc_card_sd(card) &&
card->scr.cmds & SD_SCR_CMD23_SUPPORT))
md->flags |= MMC_BLK_CMD23;
diff --git a/drivers/mmc/card/block.h b/drivers/mmc/card/block.h
new file mode 100644
index 000000000000..cdabb2ee74be
--- /dev/null
+++ b/drivers/mmc/card/block.h
@@ -0,0 +1 @@
+int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req);
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index c032eef45762..5a8dc5a76e0d 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -184,6 +184,29 @@ static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size)
return mmc_set_blocklen(test->card, size);
}
+static bool mmc_test_card_cmd23(struct mmc_card *card)
+{
+ return mmc_card_mmc(card) ||
+ (mmc_card_sd(card) && card->scr.cmds & SD_SCR_CMD23_SUPPORT);
+}
+
+static void mmc_test_prepare_sbc(struct mmc_test_card *test,
+ struct mmc_request *mrq, unsigned int blocks)
+{
+ struct mmc_card *card = test->card;
+
+ if (!mrq->sbc || !mmc_host_cmd23(card->host) ||
+ !mmc_test_card_cmd23(card) || !mmc_op_multi(mrq->cmd->opcode) ||
+ (card->quirks & MMC_QUIRK_BLK_NO_CMD23)) {
+ mrq->sbc = NULL;
+ return;
+ }
+
+ mrq->sbc->opcode = MMC_SET_BLOCK_COUNT;
+ mrq->sbc->arg = blocks;
+ mrq->sbc->flags = MMC_RSP_R1 | MMC_CMD_AC;
+}
+
/*
* Fill in the mmc_request structure given a set of transfer parameters.
*/
@@ -221,6 +244,8 @@ static void mmc_test_prepare_mrq(struct mmc_test_card *test,
mrq->data->sg = sg;
mrq->data->sg_len = sg_len;
+ mmc_test_prepare_sbc(test, mrq, blocks);
+
mmc_set_data_timeout(mrq->data, test->card);
}
@@ -693,6 +718,8 @@ static int mmc_test_check_result(struct mmc_test_card *test,
ret = 0;
+ if (mrq->sbc && mrq->sbc->error)
+ ret = mrq->sbc->error;
if (!ret && mrq->cmd->error)
ret = mrq->cmd->error;
if (!ret && mrq->data->error)
@@ -2278,6 +2305,245 @@ static int mmc_test_reset(struct mmc_test_card *test)
return RESULT_FAIL;
}
+struct mmc_test_req {
+ struct mmc_request mrq;
+ struct mmc_command sbc;
+ struct mmc_command cmd;
+ struct mmc_command stop;
+ struct mmc_command status;
+ struct mmc_data data;
+};
+
+static struct mmc_test_req *mmc_test_req_alloc(void)
+{
+ struct mmc_test_req *rq = kzalloc(sizeof(*rq), GFP_KERNEL);
+
+ if (rq) {
+ rq->mrq.cmd = &rq->cmd;
+ rq->mrq.data = &rq->data;
+ rq->mrq.stop = &rq->stop;
+ }
+
+ return rq;
+}
+
+static int mmc_test_send_status(struct mmc_test_card *test,
+ struct mmc_command *cmd)
+{
+ memset(cmd, 0, sizeof(*cmd));
+
+ cmd->opcode = MMC_SEND_STATUS;
+ if (!mmc_host_is_spi(test->card->host))
+ cmd->arg = test->card->rca << 16;
+ cmd->flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
+
+ return mmc_wait_for_cmd(test->card->host, cmd, 0);
+}
+
+static int mmc_test_ongoing_transfer(struct mmc_test_card *test,
+ unsigned int dev_addr, int use_sbc,
+ int repeat_cmd, int write, int use_areq)
+{
+ struct mmc_test_req *rq = mmc_test_req_alloc();
+ struct mmc_host *host = test->card->host;
+ struct mmc_test_area *t = &test->area;
+ struct mmc_async_req areq;
+ struct mmc_request *mrq;
+ unsigned long timeout;
+ bool expired = false;
+ int ret = 0, cmd_ret;
+ u32 status = 0;
+ int count = 0;
+
+ if (!rq)
+ return -ENOMEM;
+
+ mrq = &rq->mrq;
+ if (use_sbc)
+ mrq->sbc = &rq->sbc;
+ mrq->cap_cmd_during_tfr = true;
+
+ areq.mrq = mrq;
+ areq.err_check = mmc_test_check_result_async;
+
+ mmc_test_prepare_mrq(test, mrq, t->sg, t->sg_len, dev_addr, t->blocks,
+ 512, write);
+
+ if (use_sbc && t->blocks > 1 && !mrq->sbc) {
+ ret = mmc_host_cmd23(host) ?
+ RESULT_UNSUP_CARD :
+ RESULT_UNSUP_HOST;
+ goto out_free;
+ }
+
+ /* Start ongoing data request */
+ if (use_areq) {
+ mmc_start_req(host, &areq, &ret);
+ if (ret)
+ goto out_free;
+ } else {
+ mmc_wait_for_req(host, mrq);
+ }
+
+ timeout = jiffies + msecs_to_jiffies(3000);
+ do {
+ count += 1;
+
+ /* Send status command while data transfer in progress */
+ cmd_ret = mmc_test_send_status(test, &rq->status);
+ if (cmd_ret)
+ break;
+
+ status = rq->status.resp[0];
+ if (status & R1_ERROR) {
+ cmd_ret = -EIO;
+ break;
+ }
+
+ if (mmc_is_req_done(host, mrq))
+ break;
+
+ expired = time_after(jiffies, timeout);
+ if (expired) {
+ pr_info("%s: timeout waiting for Tran state status %#x\n",
+ mmc_hostname(host), status);
+ cmd_ret = -ETIMEDOUT;
+ break;
+ }
+ } while (repeat_cmd && R1_CURRENT_STATE(status) != R1_STATE_TRAN);
+
+ /* Wait for data request to complete */
+ if (use_areq)
+ mmc_start_req(host, NULL, &ret);
+ else
+ mmc_wait_for_req_done(test->card->host, mrq);
+
+ /*
+ * For cap_cmd_during_tfr request, upper layer must send stop if
+ * required.
+ */
+ if (mrq->data->stop && (mrq->data->error || !mrq->sbc)) {
+ if (ret)
+ mmc_wait_for_cmd(host, mrq->data->stop, 0);
+ else
+ ret = mmc_wait_for_cmd(host, mrq->data->stop, 0);
+ }
+
+ if (ret)
+ goto out_free;
+
+ if (cmd_ret) {
+ pr_info("%s: Send Status failed: status %#x, error %d\n",
+ mmc_hostname(test->card->host), status, cmd_ret);
+ }
+
+ ret = mmc_test_check_result(test, mrq);
+ if (ret)
+ goto out_free;
+
+ ret = mmc_test_wait_busy(test);
+ if (ret)
+ goto out_free;
+
+ if (repeat_cmd && (t->blocks + 1) << 9 > t->max_tfr)
+ pr_info("%s: %d commands completed during transfer of %u blocks\n",
+ mmc_hostname(test->card->host), count, t->blocks);
+
+ if (cmd_ret)
+ ret = cmd_ret;
+out_free:
+ kfree(rq);
+
+ return ret;
+}
+
+static int __mmc_test_cmds_during_tfr(struct mmc_test_card *test,
+ unsigned long sz, int use_sbc, int write,
+ int use_areq)
+{
+ struct mmc_test_area *t = &test->area;
+ int ret;
+
+ if (!(test->card->host->caps & MMC_CAP_CMD_DURING_TFR))
+ return RESULT_UNSUP_HOST;
+
+ ret = mmc_test_area_map(test, sz, 0, 0);
+ if (ret)
+ return ret;
+
+ ret = mmc_test_ongoing_transfer(test, t->dev_addr, use_sbc, 0, write,
+ use_areq);
+ if (ret)
+ return ret;
+
+ return mmc_test_ongoing_transfer(test, t->dev_addr, use_sbc, 1, write,
+ use_areq);
+}
+
+static int mmc_test_cmds_during_tfr(struct mmc_test_card *test, int use_sbc,
+ int write, int use_areq)
+{
+ struct mmc_test_area *t = &test->area;
+ unsigned long sz;
+ int ret;
+
+ for (sz = 512; sz <= t->max_tfr; sz += 512) {
+ ret = __mmc_test_cmds_during_tfr(test, sz, use_sbc, write,
+ use_areq);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * Commands during read - no Set Block Count (CMD23).
+ */
+static int mmc_test_cmds_during_read(struct mmc_test_card *test)
+{
+ return mmc_test_cmds_during_tfr(test, 0, 0, 0);
+}
+
+/*
+ * Commands during write - no Set Block Count (CMD23).
+ */
+static int mmc_test_cmds_during_write(struct mmc_test_card *test)
+{
+ return mmc_test_cmds_during_tfr(test, 0, 1, 0);
+}
+
+/*
+ * Commands during read - use Set Block Count (CMD23).
+ */
+static int mmc_test_cmds_during_read_cmd23(struct mmc_test_card *test)
+{
+ return mmc_test_cmds_during_tfr(test, 1, 0, 0);
+}
+
+/*
+ * Commands during write - use Set Block Count (CMD23).
+ */
+static int mmc_test_cmds_during_write_cmd23(struct mmc_test_card *test)
+{
+ return mmc_test_cmds_during_tfr(test, 1, 1, 0);
+}
+
+/*
+ * Commands during non-blocking read - use Set Block Count (CMD23).
+ */
+static int mmc_test_cmds_during_read_cmd23_nonblock(struct mmc_test_card *test)
+{
+ return mmc_test_cmds_during_tfr(test, 1, 0, 1);
+}
+
+/*
+ * Commands during non-blocking write - use Set Block Count (CMD23).
+ */
+static int mmc_test_cmds_during_write_cmd23_nonblock(struct mmc_test_card *test)
+{
+ return mmc_test_cmds_during_tfr(test, 1, 1, 1);
+}
+
static const struct mmc_test_case mmc_test_cases[] = {
{
.name = "Basic write (no data verification)",
@@ -2605,6 +2871,48 @@ static const struct mmc_test_case mmc_test_cases[] = {
.name = "Reset test",
.run = mmc_test_reset,
},
+
+ {
+ .name = "Commands during read - no Set Block Count (CMD23)",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_cmds_during_read,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Commands during write - no Set Block Count (CMD23)",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_cmds_during_write,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Commands during read - use Set Block Count (CMD23)",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_cmds_during_read_cmd23,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Commands during write - use Set Block Count (CMD23)",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_cmds_during_write_cmd23,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Commands during non-blocking read - use Set Block Count (CMD23)",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_cmds_during_read_cmd23_nonblock,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Commands during non-blocking write - use Set Block Count (CMD23)",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_cmds_during_write_cmd23_nonblock,
+ .cleanup = mmc_test_area_cleanup,
+ },
};
static DEFINE_MUTEX(mmc_test_lock);
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 708057261b38..8037f73a109a 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -19,7 +19,9 @@
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
+
#include "queue.h"
+#include "block.h"
#define MMC_QUEUE_BOUNCESZ 65536
@@ -68,7 +70,7 @@ static int mmc_queue_thread(void *d)
bool req_is_special = mmc_req_is_special(req);
set_current_state(TASK_RUNNING);
- mq->issue_fn(mq, req);
+ mmc_blk_issue_rq(mq, req);
cond_resched();
if (mq->flags & MMC_QUEUE_NEW_REQUEST) {
mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index fee5e1271465..3c15a75bae86 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -57,8 +57,6 @@ struct mmc_queue {
unsigned int flags;
#define MMC_QUEUE_SUSPENDED (1 << 0)
#define MMC_QUEUE_NEW_REQUEST (1 << 1)
-
- int (*issue_fn)(struct mmc_queue *, struct request *);
void *data;
struct request_queue *queue;
struct mmc_queue_req mqrq[2];
OpenPOWER on IntegriCloud