|
@@ -21,13 +21,17 @@
|
|
#define RESULT_UNSUP_HOST 2
|
|
#define RESULT_UNSUP_HOST 2
|
|
#define RESULT_UNSUP_CARD 3
|
|
#define RESULT_UNSUP_CARD 3
|
|
|
|
|
|
-#define BUFFER_SIZE (PAGE_SIZE * 4)
|
|
|
|
|
|
+#define BUFFER_ORDER 2
|
|
|
|
+#define BUFFER_SIZE (PAGE_SIZE << BUFFER_ORDER)
|
|
|
|
|
|
struct mmc_test_card {
|
|
struct mmc_test_card {
|
|
struct mmc_card *card;
|
|
struct mmc_card *card;
|
|
|
|
|
|
u8 scratch[BUFFER_SIZE];
|
|
u8 scratch[BUFFER_SIZE];
|
|
u8 *buffer;
|
|
u8 *buffer;
|
|
|
|
+#ifdef CONFIG_HIGHMEM
|
|
|
|
+ struct page *highmem;
|
|
|
|
+#endif
|
|
};
|
|
};
|
|
|
|
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
@@ -384,14 +388,16 @@ static int mmc_test_transfer(struct mmc_test_card *test,
|
|
int ret, i;
|
|
int ret, i;
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
+ BUG_ON(blocks * blksz > BUFFER_SIZE);
|
|
|
|
+
|
|
if (write) {
|
|
if (write) {
|
|
for (i = 0;i < blocks * blksz;i++)
|
|
for (i = 0;i < blocks * blksz;i++)
|
|
test->scratch[i] = i;
|
|
test->scratch[i] = i;
|
|
} else {
|
|
} else {
|
|
- memset(test->scratch, 0, BUFFER_SIZE);
|
|
|
|
|
|
+ memset(test->scratch, 0, blocks * blksz);
|
|
}
|
|
}
|
|
local_irq_save(flags);
|
|
local_irq_save(flags);
|
|
- sg_copy_from_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
|
|
|
|
|
|
+ sg_copy_from_buffer(sg, sg_len, test->scratch, blocks * blksz);
|
|
local_irq_restore(flags);
|
|
local_irq_restore(flags);
|
|
|
|
|
|
ret = mmc_test_set_blksize(test, blksz);
|
|
ret = mmc_test_set_blksize(test, blksz);
|
|
@@ -438,7 +444,7 @@ static int mmc_test_transfer(struct mmc_test_card *test,
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
local_irq_save(flags);
|
|
local_irq_save(flags);
|
|
- sg_copy_to_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
|
|
|
|
|
|
+ sg_copy_to_buffer(sg, sg_len, test->scratch, blocks * blksz);
|
|
local_irq_restore(flags);
|
|
local_irq_restore(flags);
|
|
for (i = 0;i < blocks * blksz;i++) {
|
|
for (i = 0;i < blocks * blksz;i++) {
|
|
if (test->scratch[i] != (u8)i)
|
|
if (test->scratch[i] != (u8)i)
|
|
@@ -799,6 +805,157 @@ static int mmc_test_multi_xfersize_read(struct mmc_test_card *test)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int mmc_test_bigsg_write(struct mmc_test_card *test)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+ unsigned int size;
|
|
|
|
+ struct scatterlist sg;
|
|
|
|
+
|
|
|
|
+ if (test->card->host->max_blk_count == 1)
|
|
|
|
+ return RESULT_UNSUP_HOST;
|
|
|
|
+
|
|
|
|
+ size = PAGE_SIZE * 2;
|
|
|
|
+ size = min(size, test->card->host->max_req_size);
|
|
|
|
+ size = min(size, test->card->host->max_seg_size);
|
|
|
|
+ size = min(size, test->card->host->max_blk_count * 512);
|
|
|
|
+
|
|
|
|
+ memset(test->buffer, 0, BUFFER_SIZE);
|
|
|
|
+
|
|
|
|
+ if (size < 1024)
|
|
|
|
+ return RESULT_UNSUP_HOST;
|
|
|
|
+
|
|
|
|
+ sg_init_table(&sg, 1);
|
|
|
|
+ sg_init_one(&sg, test->buffer, BUFFER_SIZE);
|
|
|
|
+
|
|
|
|
+ ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int mmc_test_bigsg_read(struct mmc_test_card *test)
|
|
|
|
+{
|
|
|
|
+ int ret, i;
|
|
|
|
+ unsigned int size;
|
|
|
|
+ struct scatterlist sg;
|
|
|
|
+
|
|
|
|
+ if (test->card->host->max_blk_count == 1)
|
|
|
|
+ return RESULT_UNSUP_HOST;
|
|
|
|
+
|
|
|
|
+ size = PAGE_SIZE * 2;
|
|
|
|
+ size = min(size, test->card->host->max_req_size);
|
|
|
|
+ size = min(size, test->card->host->max_seg_size);
|
|
|
|
+ size = min(size, test->card->host->max_blk_count * 512);
|
|
|
|
+
|
|
|
|
+ if (size < 1024)
|
|
|
|
+ return RESULT_UNSUP_HOST;
|
|
|
|
+
|
|
|
|
+ memset(test->buffer, 0xCD, BUFFER_SIZE);
|
|
|
|
+
|
|
|
|
+ sg_init_table(&sg, 1);
|
|
|
|
+ sg_init_one(&sg, test->buffer, BUFFER_SIZE);
|
|
|
|
+ ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ /* mmc_test_transfer() doesn't check for read overflows */
|
|
|
|
+ for (i = size;i < BUFFER_SIZE;i++) {
|
|
|
|
+ if (test->buffer[i] != 0xCD)
|
|
|
|
+ return RESULT_FAIL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_HIGHMEM
|
|
|
|
+
|
|
|
|
+static int mmc_test_write_high(struct mmc_test_card *test)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+ struct scatterlist sg;
|
|
|
|
+
|
|
|
|
+ sg_init_table(&sg, 1);
|
|
|
|
+ sg_set_page(&sg, test->highmem, 512, 0);
|
|
|
|
+
|
|
|
|
+ ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int mmc_test_read_high(struct mmc_test_card *test)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+ struct scatterlist sg;
|
|
|
|
+
|
|
|
|
+ sg_init_table(&sg, 1);
|
|
|
|
+ sg_set_page(&sg, test->highmem, 512, 0);
|
|
|
|
+
|
|
|
|
+ ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int mmc_test_multi_write_high(struct mmc_test_card *test)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+ unsigned int size;
|
|
|
|
+ struct scatterlist sg;
|
|
|
|
+
|
|
|
|
+ if (test->card->host->max_blk_count == 1)
|
|
|
|
+ return RESULT_UNSUP_HOST;
|
|
|
|
+
|
|
|
|
+ size = PAGE_SIZE * 2;
|
|
|
|
+ size = min(size, test->card->host->max_req_size);
|
|
|
|
+ size = min(size, test->card->host->max_seg_size);
|
|
|
|
+ size = min(size, test->card->host->max_blk_count * 512);
|
|
|
|
+
|
|
|
|
+ if (size < 1024)
|
|
|
|
+ return RESULT_UNSUP_HOST;
|
|
|
|
+
|
|
|
|
+ sg_init_table(&sg, 1);
|
|
|
|
+ sg_set_page(&sg, test->highmem, size, 0);
|
|
|
|
+
|
|
|
|
+ ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int mmc_test_multi_read_high(struct mmc_test_card *test)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+ unsigned int size;
|
|
|
|
+ struct scatterlist sg;
|
|
|
|
+
|
|
|
|
+ if (test->card->host->max_blk_count == 1)
|
|
|
|
+ return RESULT_UNSUP_HOST;
|
|
|
|
+
|
|
|
|
+ size = PAGE_SIZE * 2;
|
|
|
|
+ size = min(size, test->card->host->max_req_size);
|
|
|
|
+ size = min(size, test->card->host->max_seg_size);
|
|
|
|
+ size = min(size, test->card->host->max_blk_count * 512);
|
|
|
|
+
|
|
|
|
+ if (size < 1024)
|
|
|
|
+ return RESULT_UNSUP_HOST;
|
|
|
|
+
|
|
|
|
+ sg_init_table(&sg, 1);
|
|
|
|
+ sg_set_page(&sg, test->highmem, size, 0);
|
|
|
|
+
|
|
|
|
+ ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#endif /* CONFIG_HIGHMEM */
|
|
|
|
+
|
|
static const struct mmc_test_case mmc_test_cases[] = {
|
|
static const struct mmc_test_case mmc_test_cases[] = {
|
|
{
|
|
{
|
|
.name = "Basic write (no data verification)",
|
|
.name = "Basic write (no data verification)",
|
|
@@ -913,6 +1070,53 @@ static const struct mmc_test_case mmc_test_cases[] = {
|
|
.name = "Correct xfer_size at read (midway failure)",
|
|
.name = "Correct xfer_size at read (midway failure)",
|
|
.run = mmc_test_multi_xfersize_read,
|
|
.run = mmc_test_multi_xfersize_read,
|
|
},
|
|
},
|
|
|
|
+
|
|
|
|
+ {
|
|
|
|
+ .name = "Over-sized SG list write",
|
|
|
|
+ .prepare = mmc_test_prepare_write,
|
|
|
|
+ .run = mmc_test_bigsg_write,
|
|
|
|
+ .cleanup = mmc_test_cleanup,
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ {
|
|
|
|
+ .name = "Over-sized SG list read",
|
|
|
|
+ .prepare = mmc_test_prepare_read,
|
|
|
|
+ .run = mmc_test_bigsg_read,
|
|
|
|
+ .cleanup = mmc_test_cleanup,
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_HIGHMEM
|
|
|
|
+
|
|
|
|
+ {
|
|
|
|
+ .name = "Highmem write",
|
|
|
|
+ .prepare = mmc_test_prepare_write,
|
|
|
|
+ .run = mmc_test_write_high,
|
|
|
|
+ .cleanup = mmc_test_cleanup,
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ {
|
|
|
|
+ .name = "Highmem read",
|
|
|
|
+ .prepare = mmc_test_prepare_read,
|
|
|
|
+ .run = mmc_test_read_high,
|
|
|
|
+ .cleanup = mmc_test_cleanup,
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ {
|
|
|
|
+ .name = "Multi-block highmem write",
|
|
|
|
+ .prepare = mmc_test_prepare_write,
|
|
|
|
+ .run = mmc_test_multi_write_high,
|
|
|
|
+ .cleanup = mmc_test_cleanup,
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ {
|
|
|
|
+ .name = "Multi-block highmem read",
|
|
|
|
+ .prepare = mmc_test_prepare_read,
|
|
|
|
+ .run = mmc_test_multi_read_high,
|
|
|
|
+ .cleanup = mmc_test_cleanup,
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+#endif /* CONFIG_HIGHMEM */
|
|
|
|
+
|
|
};
|
|
};
|
|
|
|
|
|
static struct mutex mmc_test_lock;
|
|
static struct mutex mmc_test_lock;
|
|
@@ -1014,12 +1218,23 @@ static ssize_t mmc_test_store(struct device *dev,
|
|
test->card = card;
|
|
test->card = card;
|
|
|
|
|
|
test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL);
|
|
test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL);
|
|
|
|
+#ifdef CONFIG_HIGHMEM
|
|
|
|
+ test->highmem = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, BUFFER_ORDER);
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_HIGHMEM
|
|
|
|
+ if (test->buffer && test->highmem) {
|
|
|
|
+#else
|
|
if (test->buffer) {
|
|
if (test->buffer) {
|
|
|
|
+#endif
|
|
mutex_lock(&mmc_test_lock);
|
|
mutex_lock(&mmc_test_lock);
|
|
mmc_test_run(test, testcase);
|
|
mmc_test_run(test, testcase);
|
|
mutex_unlock(&mmc_test_lock);
|
|
mutex_unlock(&mmc_test_lock);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#ifdef CONFIG_HIGHMEM
|
|
|
|
+ __free_pages(test->highmem, BUFFER_ORDER);
|
|
|
|
+#endif
|
|
kfree(test->buffer);
|
|
kfree(test->buffer);
|
|
kfree(test);
|
|
kfree(test);
|
|
|
|
|
|
@@ -1041,6 +1256,8 @@ static int mmc_test_probe(struct mmc_card *card)
|
|
if (ret)
|
|
if (ret)
|
|
return ret;
|
|
return ret;
|
|
|
|
|
|
|
|
+ dev_info(&card->dev, "Card claimed for testing.\n");
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|