mmc_test.c 24.9 KB
Newer Older
P
Pierre Ossman 已提交
1 2 3
/*
 *  linux/drivers/mmc/card/mmc_test.c
 *
4
 *  Copyright 2007-2008 Pierre Ossman
P
Pierre Ossman 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 */

#include <linux/mmc/core.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>

#include <linux/scatterlist.h>

#define RESULT_OK		0
#define RESULT_FAIL		1
#define RESULT_UNSUP_HOST	2
#define RESULT_UNSUP_CARD	3

P
Pierre Ossman 已提交
24 25
#define BUFFER_ORDER		2
#define BUFFER_SIZE		(PAGE_SIZE << BUFFER_ORDER)
P
Pierre Ossman 已提交
26 27 28 29

struct mmc_test_card {
	struct mmc_card	*card;

P
Pierre Ossman 已提交
30
	u8		scratch[BUFFER_SIZE];
P
Pierre Ossman 已提交
31
	u8		*buffer;
P
Pierre Ossman 已提交
32 33 34
#ifdef CONFIG_HIGHMEM
	struct page	*highmem;
#endif
P
Pierre Ossman 已提交
35 36 37
};

/*******************************************************************/
P
Pierre Ossman 已提交
38
/*  General helper functions                                       */
P
Pierre Ossman 已提交
39 40
/*******************************************************************/

P
Pierre Ossman 已提交
41 42 43
/*
 * Configure correct block size in card
 */
P
Pierre Ossman 已提交
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size)
{
	struct mmc_command cmd;
	int ret;

	cmd.opcode = MMC_SET_BLOCKLEN;
	cmd.arg = size;
	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
	ret = mmc_wait_for_cmd(test->card->host, &cmd, 0);
	if (ret)
		return ret;

	return 0;
}

P
Pierre Ossman 已提交
59 60 61 62 63 64
/*
 * Fill in the mmc_request structure given a set of transfer parameters.
 */
static void mmc_test_prepare_mrq(struct mmc_test_card *test,
	struct mmc_request *mrq, struct scatterlist *sg, unsigned sg_len,
	unsigned dev_addr, unsigned blocks, unsigned blksz, int write)
P
Pierre Ossman 已提交
65
{
P
Pierre Ossman 已提交
66
	BUG_ON(!mrq || !mrq->cmd || !mrq->data || !mrq->stop);
P
Pierre Ossman 已提交
67

P
Pierre Ossman 已提交
68 69 70
	if (blocks > 1) {
		mrq->cmd->opcode = write ?
			MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;
P
Pierre Ossman 已提交
71
	} else {
P
Pierre Ossman 已提交
72 73
		mrq->cmd->opcode = write ?
			MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
P
Pierre Ossman 已提交
74 75
	}

P
Pierre Ossman 已提交
76 77
	mrq->cmd->arg = dev_addr;
	mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC;
P
Pierre Ossman 已提交
78

P
Pierre Ossman 已提交
79 80 81 82 83 84
	if (blocks == 1)
		mrq->stop = NULL;
	else {
		mrq->stop->opcode = MMC_STOP_TRANSMISSION;
		mrq->stop->arg = 0;
		mrq->stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
P
Pierre Ossman 已提交
85 86
	}

P
Pierre Ossman 已提交
87 88 89 90 91
	mrq->data->blksz = blksz;
	mrq->data->blocks = blocks;
	mrq->data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
	mrq->data->sg = sg;
	mrq->data->sg_len = sg_len;
P
Pierre Ossman 已提交
92

P
Pierre Ossman 已提交
93 94
	mmc_set_data_timeout(mrq->data, test->card);
}
P
Pierre Ossman 已提交
95

P
Pierre Ossman 已提交
96 97 98 99 100 101 102
/*
 * Wait for the card to finish the busy state
 */
static int mmc_test_wait_busy(struct mmc_test_card *test)
{
	int ret, busy;
	struct mmc_command cmd;
P
Pierre Ossman 已提交
103 104 105 106 107 108 109 110 111

	busy = 0;
	do {
		memset(&cmd, 0, sizeof(struct mmc_command));

		cmd.opcode = MMC_SEND_STATUS;
		cmd.arg = test->card->rca << 16;
		cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;

P
Pierre Ossman 已提交
112 113
		ret = mmc_wait_for_cmd(test->card->host, &cmd, 0);
		if (ret)
P
Pierre Ossman 已提交
114 115 116 117 118 119 120 121 122 123 124 125 126
			break;

		if (!busy && !(cmd.resp[0] & R1_READY_FOR_DATA)) {
			busy = 1;
			printk(KERN_INFO "%s: Warning: Host did not "
				"wait for busy state to end.\n",
				mmc_hostname(test->card->host));
		}
	} while (!(cmd.resp[0] & R1_READY_FOR_DATA));

	return ret;
}

P
Pierre Ossman 已提交
127 128 129 130 131
/*
 * Transfer a single sector of kernel addressable data
 */
static int mmc_test_buffer_transfer(struct mmc_test_card *test,
	u8 *buffer, unsigned addr, unsigned blksz, int write)
P
Pierre Ossman 已提交
132
{
P
Pierre Ossman 已提交
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
	int ret;

	struct mmc_request mrq;
	struct mmc_command cmd;
	struct mmc_command stop;
	struct mmc_data data;

	struct scatterlist sg;

	memset(&mrq, 0, sizeof(struct mmc_request));
	memset(&cmd, 0, sizeof(struct mmc_command));
	memset(&data, 0, sizeof(struct mmc_data));
	memset(&stop, 0, sizeof(struct mmc_command));

	mrq.cmd = &cmd;
	mrq.data = &data;
	mrq.stop = &stop;

	sg_init_one(&sg, buffer, blksz);

	mmc_test_prepare_mrq(test, &mrq, &sg, 1, addr, 1, blksz, write);

	mmc_wait_for_req(test->card->host, &mrq);

	if (cmd.error)
		return cmd.error;
	if (data.error)
		return data.error;

	ret = mmc_test_wait_busy(test);
	if (ret)
		return ret;

	return 0;
P
Pierre Ossman 已提交
167 168
}

P
Pierre Ossman 已提交
169 170 171 172 173 174 175 176 177
/*******************************************************************/
/*  Test preparation and cleanup                                   */
/*******************************************************************/

/*
 * Fill the first couple of sectors of the card with known data
 * so that bad reads/writes can be detected
 */
static int __mmc_test_prepare(struct mmc_test_card *test, int write)
P
Pierre Ossman 已提交
178 179 180 181 182 183 184 185
{
	int ret, i;

	ret = mmc_test_set_blksize(test, 512);
	if (ret)
		return ret;

	if (write)
P
Pierre Ossman 已提交
186
		memset(test->buffer, 0xDF, 512);
P
Pierre Ossman 已提交
187
	else {
P
Pierre Ossman 已提交
188
		for (i = 0;i < 512;i++)
P
Pierre Ossman 已提交
189 190 191 192
			test->buffer[i] = i;
	}

	for (i = 0;i < BUFFER_SIZE / 512;i++) {
P
Pierre Ossman 已提交
193
		ret = mmc_test_buffer_transfer(test, test->buffer, i * 512, 512, 1);
P
Pierre Ossman 已提交
194 195 196 197 198 199 200
		if (ret)
			return ret;
	}

	return 0;
}

P
Pierre Ossman 已提交
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
static int mmc_test_prepare_write(struct mmc_test_card *test)
{
	return __mmc_test_prepare(test, 1);
}

static int mmc_test_prepare_read(struct mmc_test_card *test)
{
	return __mmc_test_prepare(test, 0);
}

static int mmc_test_cleanup(struct mmc_test_card *test)
{
	int ret, i;

	ret = mmc_test_set_blksize(test, 512);
	if (ret)
		return ret;

	memset(test->buffer, 0, 512);

	for (i = 0;i < BUFFER_SIZE / 512;i++) {
		ret = mmc_test_buffer_transfer(test, test->buffer, i * 512, 512, 1);
		if (ret)
			return ret;
	}

	return 0;
}

/*******************************************************************/
/*  Test execution helpers                                         */
/*******************************************************************/

/*
 * Modifies the mmc_request to perform the "short transfer" tests
 */
static void mmc_test_prepare_broken_mrq(struct mmc_test_card *test,
	struct mmc_request *mrq, int write)
{
	BUG_ON(!mrq || !mrq->cmd || !mrq->data);

	if (mrq->data->blocks > 1) {
		mrq->cmd->opcode = write ?
			MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
		mrq->stop = NULL;
	} else {
		mrq->cmd->opcode = MMC_SEND_STATUS;
		mrq->cmd->arg = test->card->rca << 16;
	}
}

/*
 * Checks that a normal transfer didn't have any errors
 */
static int mmc_test_check_result(struct mmc_test_card *test,
	struct mmc_request *mrq)
P
Pierre Ossman 已提交
257
{
P
Pierre Ossman 已提交
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
	int ret;

	BUG_ON(!mrq || !mrq->cmd || !mrq->data);

	ret = 0;

	if (!ret && mrq->cmd->error)
		ret = mrq->cmd->error;
	if (!ret && mrq->data->error)
		ret = mrq->data->error;
	if (!ret && mrq->stop && mrq->stop->error)
		ret = mrq->stop->error;
	if (!ret && mrq->data->bytes_xfered !=
		mrq->data->blocks * mrq->data->blksz)
		ret = RESULT_FAIL;

	if (ret == -EINVAL)
		ret = RESULT_UNSUP_HOST;

	return ret;
P
Pierre Ossman 已提交
278 279
}

P
Pierre Ossman 已提交
280 281 282 283 284
/*
 * Checks that a "short transfer" behaved as expected
 */
static int mmc_test_check_broken_result(struct mmc_test_card *test,
	struct mmc_request *mrq)
P
Pierre Ossman 已提交
285
{
P
Pierre Ossman 已提交
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
	int ret;

	BUG_ON(!mrq || !mrq->cmd || !mrq->data);

	ret = 0;

	if (!ret && mrq->cmd->error)
		ret = mrq->cmd->error;
	if (!ret && mrq->data->error == 0)
		ret = RESULT_FAIL;
	if (!ret && mrq->data->error != -ETIMEDOUT)
		ret = mrq->data->error;
	if (!ret && mrq->stop && mrq->stop->error)
		ret = mrq->stop->error;
	if (mrq->data->blocks > 1) {
		if (!ret && mrq->data->bytes_xfered > mrq->data->blksz)
			ret = RESULT_FAIL;
	} else {
		if (!ret && mrq->data->bytes_xfered > 0)
			ret = RESULT_FAIL;
	}

	if (ret == -EINVAL)
		ret = RESULT_UNSUP_HOST;

	return ret;
P
Pierre Ossman 已提交
312 313
}

P
Pierre Ossman 已提交
314 315 316 317 318 319
/*
 * Tests a basic transfer with certain parameters
 */
static int mmc_test_simple_transfer(struct mmc_test_card *test,
	struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
	unsigned blocks, unsigned blksz, int write)
P
Pierre Ossman 已提交
320
{
P
Pierre Ossman 已提交
321 322 323 324
	struct mmc_request mrq;
	struct mmc_command cmd;
	struct mmc_command stop;
	struct mmc_data data;
P
Pierre Ossman 已提交
325

P
Pierre Ossman 已提交
326 327 328 329 330 331 332 333 334 335 336 337 338
	memset(&mrq, 0, sizeof(struct mmc_request));
	memset(&cmd, 0, sizeof(struct mmc_command));
	memset(&data, 0, sizeof(struct mmc_data));
	memset(&stop, 0, sizeof(struct mmc_command));

	mrq.cmd = &cmd;
	mrq.data = &data;
	mrq.stop = &stop;

	mmc_test_prepare_mrq(test, &mrq, sg, sg_len, dev_addr,
		blocks, blksz, write);

	mmc_wait_for_req(test->card->host, &mrq);
P
Pierre Ossman 已提交
339

P
Pierre Ossman 已提交
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
	mmc_test_wait_busy(test);

	return mmc_test_check_result(test, &mrq);
}

/*
 * Tests a transfer where the card will fail completely or partly
 */
static int mmc_test_broken_transfer(struct mmc_test_card *test,
	unsigned blocks, unsigned blksz, int write)
{
	struct mmc_request mrq;
	struct mmc_command cmd;
	struct mmc_command stop;
	struct mmc_data data;

	struct scatterlist sg;

	memset(&mrq, 0, sizeof(struct mmc_request));
	memset(&cmd, 0, sizeof(struct mmc_command));
	memset(&data, 0, sizeof(struct mmc_data));
	memset(&stop, 0, sizeof(struct mmc_command));

	mrq.cmd = &cmd;
	mrq.data = &data;
	mrq.stop = &stop;

	sg_init_one(&sg, test->buffer, blocks * blksz);

	mmc_test_prepare_mrq(test, &mrq, &sg, 1, 0, blocks, blksz, write);
	mmc_test_prepare_broken_mrq(test, &mrq, write);

	mmc_wait_for_req(test->card->host, &mrq);

	mmc_test_wait_busy(test);

	return mmc_test_check_broken_result(test, &mrq);
}

/*
 * Does a complete transfer test where data is also validated
 *
 * Note: mmc_test_prepare() must have been done before this call
 */
static int mmc_test_transfer(struct mmc_test_card *test,
	struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
	unsigned blocks, unsigned blksz, int write)
{
	int ret, i;
	unsigned long flags;
P
Pierre Ossman 已提交
390 391 392

	if (write) {
		for (i = 0;i < blocks * blksz;i++)
P
Pierre Ossman 已提交
393 394
			test->scratch[i] = i;
	} else {
395
		memset(test->scratch, 0, BUFFER_SIZE);
P
Pierre Ossman 已提交
396
	}
P
Pierre Ossman 已提交
397
	local_irq_save(flags);
398
	sg_copy_from_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
P
Pierre Ossman 已提交
399
	local_irq_restore(flags);
P
Pierre Ossman 已提交
400 401 402 403 404

	ret = mmc_test_set_blksize(test, blksz);
	if (ret)
		return ret;

P
Pierre Ossman 已提交
405 406
	ret = mmc_test_simple_transfer(test, sg, sg_len, dev_addr,
		blocks, blksz, write);
P
Pierre Ossman 已提交
407 408 409 410
	if (ret)
		return ret;

	if (write) {
P
Pierre Ossman 已提交
411 412
		int sectors;

P
Pierre Ossman 已提交
413 414 415 416 417 418 419 420 421 422 423 424 425 426
		ret = mmc_test_set_blksize(test, 512);
		if (ret)
			return ret;

		sectors = (blocks * blksz + 511) / 512;
		if ((sectors * 512) == (blocks * blksz))
			sectors++;

		if ((sectors * 512) > BUFFER_SIZE)
			return -EINVAL;

		memset(test->buffer, 0, sectors * 512);

		for (i = 0;i < sectors;i++) {
P
Pierre Ossman 已提交
427
			ret = mmc_test_buffer_transfer(test,
P
Pierre Ossman 已提交
428
				test->buffer + i * 512,
P
Pierre Ossman 已提交
429
				dev_addr + i * 512, 512, 0);
P
Pierre Ossman 已提交
430 431 432 433 434 435 436 437 438 439 440 441 442 443
			if (ret)
				return ret;
		}

		for (i = 0;i < blocks * blksz;i++) {
			if (test->buffer[i] != (u8)i)
				return RESULT_FAIL;
		}

		for (;i < sectors * 512;i++) {
			if (test->buffer[i] != 0xDF)
				return RESULT_FAIL;
		}
	} else {
P
Pierre Ossman 已提交
444
		local_irq_save(flags);
445
		sg_copy_to_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
P
Pierre Ossman 已提交
446
		local_irq_restore(flags);
P
Pierre Ossman 已提交
447
		for (i = 0;i < blocks * blksz;i++) {
P
Pierre Ossman 已提交
448
			if (test->scratch[i] != (u8)i)
P
Pierre Ossman 已提交
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
				return RESULT_FAIL;
		}
	}

	return 0;
}

/*******************************************************************/
/*  Tests                                                          */
/*******************************************************************/

struct mmc_test_case {
	const char *name;

	int (*prepare)(struct mmc_test_card *);
	int (*run)(struct mmc_test_card *);
	int (*cleanup)(struct mmc_test_card *);
};

static int mmc_test_basic_write(struct mmc_test_card *test)
{
	int ret;
P
Pierre Ossman 已提交
471
	struct scatterlist sg;
P
Pierre Ossman 已提交
472 473 474 475 476

	ret = mmc_test_set_blksize(test, 512);
	if (ret)
		return ret;

P
Pierre Ossman 已提交
477 478 479
	sg_init_one(&sg, test->buffer, 512);

	ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1);
P
Pierre Ossman 已提交
480 481 482 483 484 485 486 487 488
	if (ret)
		return ret;

	return 0;
}

static int mmc_test_basic_read(struct mmc_test_card *test)
{
	int ret;
P
Pierre Ossman 已提交
489
	struct scatterlist sg;
P
Pierre Ossman 已提交
490 491 492 493 494

	ret = mmc_test_set_blksize(test, 512);
	if (ret)
		return ret;

P
Pierre Ossman 已提交
495 496 497
	sg_init_one(&sg, test->buffer, 512);

	ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1);
P
Pierre Ossman 已提交
498 499 500 501 502 503 504 505 506
	if (ret)
		return ret;

	return 0;
}

static int mmc_test_verify_write(struct mmc_test_card *test)
{
	int ret;
P
Pierre Ossman 已提交
507 508 509
	struct scatterlist sg;

	sg_init_one(&sg, test->buffer, 512);
P
Pierre Ossman 已提交
510

P
Pierre Ossman 已提交
511
	ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
P
Pierre Ossman 已提交
512 513 514 515 516 517 518 519 520
	if (ret)
		return ret;

	return 0;
}

static int mmc_test_verify_read(struct mmc_test_card *test)
{
	int ret;
P
Pierre Ossman 已提交
521 522 523
	struct scatterlist sg;

	sg_init_one(&sg, test->buffer, 512);
P
Pierre Ossman 已提交
524

P
Pierre Ossman 已提交
525
	ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
P
Pierre Ossman 已提交
526 527 528 529 530 531 532 533 534 535
	if (ret)
		return ret;

	return 0;
}

static int mmc_test_multi_write(struct mmc_test_card *test)
{
	int ret;
	unsigned int size;
P
Pierre Ossman 已提交
536
	struct scatterlist sg;
P
Pierre Ossman 已提交
537 538 539 540 541 542 543 544 545 546 547 548

	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;

P
Pierre Ossman 已提交
549 550 551
	sg_init_one(&sg, test->buffer, size);

	ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
P
Pierre Ossman 已提交
552 553 554 555 556 557 558 559 560 561
	if (ret)
		return ret;

	return 0;
}

static int mmc_test_multi_read(struct mmc_test_card *test)
{
	int ret;
	unsigned int size;
P
Pierre Ossman 已提交
562
	struct scatterlist sg;
P
Pierre Ossman 已提交
563 564 565 566 567 568 569 570 571 572 573 574

	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;

P
Pierre Ossman 已提交
575 576 577
	sg_init_one(&sg, test->buffer, size);

	ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
P
Pierre Ossman 已提交
578 579 580 581 582 583 584 585 586
	if (ret)
		return ret;

	return 0;
}

static int mmc_test_pow2_write(struct mmc_test_card *test)
{
	int ret, i;
P
Pierre Ossman 已提交
587
	struct scatterlist sg;
P
Pierre Ossman 已提交
588 589 590 591 592

	if (!test->card->csd.write_partial)
		return RESULT_UNSUP_CARD;

	for (i = 1; i < 512;i <<= 1) {
P
Pierre Ossman 已提交
593 594
		sg_init_one(&sg, test->buffer, i);
		ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 1);
P
Pierre Ossman 已提交
595 596 597 598 599 600 601 602 603 604
		if (ret)
			return ret;
	}

	return 0;
}

static int mmc_test_pow2_read(struct mmc_test_card *test)
{
	int ret, i;
P
Pierre Ossman 已提交
605
	struct scatterlist sg;
P
Pierre Ossman 已提交
606 607 608 609 610

	if (!test->card->csd.read_partial)
		return RESULT_UNSUP_CARD;

	for (i = 1; i < 512;i <<= 1) {
P
Pierre Ossman 已提交
611 612
		sg_init_one(&sg, test->buffer, i);
		ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 0);
P
Pierre Ossman 已提交
613 614 615 616 617 618 619 620 621 622
		if (ret)
			return ret;
	}

	return 0;
}

static int mmc_test_weird_write(struct mmc_test_card *test)
{
	int ret, i;
P
Pierre Ossman 已提交
623
	struct scatterlist sg;
P
Pierre Ossman 已提交
624 625 626 627 628

	if (!test->card->csd.write_partial)
		return RESULT_UNSUP_CARD;

	for (i = 3; i < 512;i += 7) {
P
Pierre Ossman 已提交
629 630
		sg_init_one(&sg, test->buffer, i);
		ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 1);
P
Pierre Ossman 已提交
631 632 633 634 635 636 637 638 639 640
		if (ret)
			return ret;
	}

	return 0;
}

static int mmc_test_weird_read(struct mmc_test_card *test)
{
	int ret, i;
P
Pierre Ossman 已提交
641
	struct scatterlist sg;
P
Pierre Ossman 已提交
642 643 644 645 646

	if (!test->card->csd.read_partial)
		return RESULT_UNSUP_CARD;

	for (i = 3; i < 512;i += 7) {
P
Pierre Ossman 已提交
647 648
		sg_init_one(&sg, test->buffer, i);
		ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 0);
P
Pierre Ossman 已提交
649 650 651 652 653 654 655 656 657 658
		if (ret)
			return ret;
	}

	return 0;
}

static int mmc_test_align_write(struct mmc_test_card *test)
{
	int ret, i;
P
Pierre Ossman 已提交
659
	struct scatterlist sg;
P
Pierre Ossman 已提交
660 661

	for (i = 1;i < 4;i++) {
P
Pierre Ossman 已提交
662 663
		sg_init_one(&sg, test->buffer + i, 512);
		ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
P
Pierre Ossman 已提交
664 665 666 667 668 669 670 671 672 673
		if (ret)
			return ret;
	}

	return 0;
}

static int mmc_test_align_read(struct mmc_test_card *test)
{
	int ret, i;
P
Pierre Ossman 已提交
674
	struct scatterlist sg;
P
Pierre Ossman 已提交
675 676

	for (i = 1;i < 4;i++) {
P
Pierre Ossman 已提交
677 678
		sg_init_one(&sg, test->buffer + i, 512);
		ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
P
Pierre Ossman 已提交
679 680 681 682 683 684 685 686 687 688 689
		if (ret)
			return ret;
	}

	return 0;
}

static int mmc_test_align_multi_write(struct mmc_test_card *test)
{
	int ret, i;
	unsigned int size;
P
Pierre Ossman 已提交
690
	struct scatterlist sg;
P
Pierre Ossman 已提交
691 692 693 694 695 696 697 698 699 700 701 702 703

	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;

	for (i = 1;i < 4;i++) {
P
Pierre Ossman 已提交
704 705
		sg_init_one(&sg, test->buffer + i, size);
		ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
P
Pierre Ossman 已提交
706 707 708 709 710 711 712 713 714 715 716
		if (ret)
			return ret;
	}

	return 0;
}

static int mmc_test_align_multi_read(struct mmc_test_card *test)
{
	int ret, i;
	unsigned int size;
P
Pierre Ossman 已提交
717
	struct scatterlist sg;
P
Pierre Ossman 已提交
718 719 720 721 722 723 724 725 726 727 728 729 730

	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;

	for (i = 1;i < 4;i++) {
P
Pierre Ossman 已提交
731 732
		sg_init_one(&sg, test->buffer + i, size);
		ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
P
Pierre Ossman 已提交
733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
		if (ret)
			return ret;
	}

	return 0;
}

static int mmc_test_xfersize_write(struct mmc_test_card *test)
{
	int ret;

	ret = mmc_test_set_blksize(test, 512);
	if (ret)
		return ret;

P
Pierre Ossman 已提交
748
	ret = mmc_test_broken_transfer(test, 1, 512, 1);
P
Pierre Ossman 已提交
749 750 751 752 753 754 755 756 757 758 759 760 761 762
	if (ret)
		return ret;

	return 0;
}

static int mmc_test_xfersize_read(struct mmc_test_card *test)
{
	int ret;

	ret = mmc_test_set_blksize(test, 512);
	if (ret)
		return ret;

P
Pierre Ossman 已提交
763
	ret = mmc_test_broken_transfer(test, 1, 512, 0);
P
Pierre Ossman 已提交
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
	if (ret)
		return ret;

	return 0;
}

static int mmc_test_multi_xfersize_write(struct mmc_test_card *test)
{
	int ret;

	if (test->card->host->max_blk_count == 1)
		return RESULT_UNSUP_HOST;

	ret = mmc_test_set_blksize(test, 512);
	if (ret)
		return ret;

P
Pierre Ossman 已提交
781
	ret = mmc_test_broken_transfer(test, 2, 512, 1);
P
Pierre Ossman 已提交
782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
	if (ret)
		return ret;

	return 0;
}

static int mmc_test_multi_xfersize_read(struct mmc_test_card *test)
{
	int ret;

	if (test->card->host->max_blk_count == 1)
		return RESULT_UNSUP_HOST;

	ret = mmc_test_set_blksize(test, 512);
	if (ret)
		return ret;

P
Pierre Ossman 已提交
799
	ret = mmc_test_broken_transfer(test, 2, 512, 0);
P
Pierre Ossman 已提交
800 801 802 803 804 805
	if (ret)
		return ret;

	return 0;
}

P
Pierre Ossman 已提交
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893
#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 */

P
Pierre Ossman 已提交
894 895 896 897 898 899 900 901 902 903 904 905 906
static const struct mmc_test_case mmc_test_cases[] = {
	{
		.name = "Basic write (no data verification)",
		.run = mmc_test_basic_write,
	},

	{
		.name = "Basic read (no data verification)",
		.run = mmc_test_basic_read,
	},

	{
		.name = "Basic write (with data verification)",
P
Pierre Ossman 已提交
907
		.prepare = mmc_test_prepare_write,
P
Pierre Ossman 已提交
908
		.run = mmc_test_verify_write,
P
Pierre Ossman 已提交
909
		.cleanup = mmc_test_cleanup,
P
Pierre Ossman 已提交
910 911 912 913
	},

	{
		.name = "Basic read (with data verification)",
P
Pierre Ossman 已提交
914
		.prepare = mmc_test_prepare_read,
P
Pierre Ossman 已提交
915
		.run = mmc_test_verify_read,
P
Pierre Ossman 已提交
916
		.cleanup = mmc_test_cleanup,
P
Pierre Ossman 已提交
917 918 919 920
	},

	{
		.name = "Multi-block write",
P
Pierre Ossman 已提交
921
		.prepare = mmc_test_prepare_write,
P
Pierre Ossman 已提交
922
		.run = mmc_test_multi_write,
P
Pierre Ossman 已提交
923
		.cleanup = mmc_test_cleanup,
P
Pierre Ossman 已提交
924 925 926 927
	},

	{
		.name = "Multi-block read",
P
Pierre Ossman 已提交
928
		.prepare = mmc_test_prepare_read,
P
Pierre Ossman 已提交
929
		.run = mmc_test_multi_read,
P
Pierre Ossman 已提交
930
		.cleanup = mmc_test_cleanup,
P
Pierre Ossman 已提交
931 932 933 934
	},

	{
		.name = "Power of two block writes",
P
Pierre Ossman 已提交
935
		.prepare = mmc_test_prepare_write,
P
Pierre Ossman 已提交
936
		.run = mmc_test_pow2_write,
P
Pierre Ossman 已提交
937
		.cleanup = mmc_test_cleanup,
P
Pierre Ossman 已提交
938 939 940 941
	},

	{
		.name = "Power of two block reads",
P
Pierre Ossman 已提交
942
		.prepare = mmc_test_prepare_read,
P
Pierre Ossman 已提交
943
		.run = mmc_test_pow2_read,
P
Pierre Ossman 已提交
944
		.cleanup = mmc_test_cleanup,
P
Pierre Ossman 已提交
945 946 947 948
	},

	{
		.name = "Weird sized block writes",
P
Pierre Ossman 已提交
949
		.prepare = mmc_test_prepare_write,
P
Pierre Ossman 已提交
950
		.run = mmc_test_weird_write,
P
Pierre Ossman 已提交
951
		.cleanup = mmc_test_cleanup,
P
Pierre Ossman 已提交
952 953 954 955
	},

	{
		.name = "Weird sized block reads",
P
Pierre Ossman 已提交
956
		.prepare = mmc_test_prepare_read,
P
Pierre Ossman 已提交
957
		.run = mmc_test_weird_read,
P
Pierre Ossman 已提交
958
		.cleanup = mmc_test_cleanup,
P
Pierre Ossman 已提交
959 960 961 962
	},

	{
		.name = "Badly aligned write",
P
Pierre Ossman 已提交
963
		.prepare = mmc_test_prepare_write,
P
Pierre Ossman 已提交
964
		.run = mmc_test_align_write,
P
Pierre Ossman 已提交
965
		.cleanup = mmc_test_cleanup,
P
Pierre Ossman 已提交
966 967 968 969
	},

	{
		.name = "Badly aligned read",
P
Pierre Ossman 已提交
970
		.prepare = mmc_test_prepare_read,
P
Pierre Ossman 已提交
971
		.run = mmc_test_align_read,
P
Pierre Ossman 已提交
972
		.cleanup = mmc_test_cleanup,
P
Pierre Ossman 已提交
973 974 975 976
	},

	{
		.name = "Badly aligned multi-block write",
P
Pierre Ossman 已提交
977
		.prepare = mmc_test_prepare_write,
P
Pierre Ossman 已提交
978
		.run = mmc_test_align_multi_write,
P
Pierre Ossman 已提交
979
		.cleanup = mmc_test_cleanup,
P
Pierre Ossman 已提交
980 981 982 983
	},

	{
		.name = "Badly aligned multi-block read",
P
Pierre Ossman 已提交
984
		.prepare = mmc_test_prepare_read,
P
Pierre Ossman 已提交
985
		.run = mmc_test_align_multi_read,
P
Pierre Ossman 已提交
986
		.cleanup = mmc_test_cleanup,
P
Pierre Ossman 已提交
987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
	},

	{
		.name = "Correct xfer_size at write (start failure)",
		.run = mmc_test_xfersize_write,
	},

	{
		.name = "Correct xfer_size at read (start failure)",
		.run = mmc_test_xfersize_read,
	},

	{
		.name = "Correct xfer_size at write (midway failure)",
		.run = mmc_test_multi_xfersize_write,
	},

	{
		.name = "Correct xfer_size at read (midway failure)",
		.run = mmc_test_multi_xfersize_read,
	},
P
Pierre Ossman 已提交
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040

#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 */

P
Pierre Ossman 已提交
1041 1042
};

1043
static DEFINE_MUTEX(mmc_test_lock);
P
Pierre Ossman 已提交
1044

P
Pierre Ossman 已提交
1045
static void mmc_test_run(struct mmc_test_card *test, int testcase)
P
Pierre Ossman 已提交
1046 1047 1048 1049 1050 1051 1052 1053 1054
{
	int i, ret;

	printk(KERN_INFO "%s: Starting tests of card %s...\n",
		mmc_hostname(test->card->host), mmc_card_id(test->card));

	mmc_claim_host(test->card->host);

	for (i = 0;i < ARRAY_SIZE(mmc_test_cases);i++) {
P
Pierre Ossman 已提交
1055 1056 1057
		if (testcase && ((i + 1) != testcase))
			continue;

P
Pierre Ossman 已提交
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
		printk(KERN_INFO "%s: Test case %d. %s...\n",
			mmc_hostname(test->card->host), i + 1,
			mmc_test_cases[i].name);

		if (mmc_test_cases[i].prepare) {
			ret = mmc_test_cases[i].prepare(test);
			if (ret) {
				printk(KERN_INFO "%s: Result: Prepare "
					"stage failed! (%d)\n",
					mmc_hostname(test->card->host),
					ret);
				continue;
			}
		}

		ret = mmc_test_cases[i].run(test);
		switch (ret) {
		case RESULT_OK:
			printk(KERN_INFO "%s: Result: OK\n",
				mmc_hostname(test->card->host));
			break;
		case RESULT_FAIL:
			printk(KERN_INFO "%s: Result: FAILED\n",
				mmc_hostname(test->card->host));
			break;
		case RESULT_UNSUP_HOST:
			printk(KERN_INFO "%s: Result: UNSUPPORTED "
				"(by host)\n",
				mmc_hostname(test->card->host));
			break;
		case RESULT_UNSUP_CARD:
			printk(KERN_INFO "%s: Result: UNSUPPORTED "
				"(by card)\n",
				mmc_hostname(test->card->host));
			break;
		default:
			printk(KERN_INFO "%s: Result: ERROR (%d)\n",
				mmc_hostname(test->card->host), ret);
		}

		if (mmc_test_cases[i].cleanup) {
			ret = mmc_test_cases[i].cleanup(test);
			if (ret) {
				printk(KERN_INFO "%s: Warning: Cleanup "
					"stage failed! (%d)\n",
					mmc_hostname(test->card->host),
					ret);
			}
		}
	}

	mmc_release_host(test->card->host);

	printk(KERN_INFO "%s: Tests completed.\n",
		mmc_hostname(test->card->host));
}

static ssize_t mmc_test_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	mutex_lock(&mmc_test_lock);
	mutex_unlock(&mmc_test_lock);

	return 0;
}

static ssize_t mmc_test_store(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	struct mmc_card *card;
	struct mmc_test_card *test;
P
Pierre Ossman 已提交
1129
	int testcase;
P
Pierre Ossman 已提交
1130 1131 1132

	card = container_of(dev, struct mmc_card, dev);

P
Pierre Ossman 已提交
1133 1134
	testcase = simple_strtol(buf, NULL, 10);

P
Pierre Ossman 已提交
1135 1136 1137 1138 1139 1140 1141
	test = kzalloc(sizeof(struct mmc_test_card), GFP_KERNEL);
	if (!test)
		return -ENOMEM;

	test->card = card;

	test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL);
P
Pierre Ossman 已提交
1142 1143 1144 1145 1146 1147 1148
#ifdef CONFIG_HIGHMEM
	test->highmem = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, BUFFER_ORDER);
#endif

#ifdef CONFIG_HIGHMEM
	if (test->buffer && test->highmem) {
#else
P
Pierre Ossman 已提交
1149
	if (test->buffer) {
P
Pierre Ossman 已提交
1150
#endif
P
Pierre Ossman 已提交
1151
		mutex_lock(&mmc_test_lock);
P
Pierre Ossman 已提交
1152
		mmc_test_run(test, testcase);
P
Pierre Ossman 已提交
1153 1154 1155
		mutex_unlock(&mmc_test_lock);
	}

P
Pierre Ossman 已提交
1156 1157 1158
#ifdef CONFIG_HIGHMEM
	__free_pages(test->highmem, BUFFER_ORDER);
#endif
P
Pierre Ossman 已提交
1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
	kfree(test->buffer);
	kfree(test);

	return count;
}

static DEVICE_ATTR(test, S_IWUSR | S_IRUGO, mmc_test_show, mmc_test_store);

static int mmc_test_probe(struct mmc_card *card)
{
	int ret;

1171 1172 1173
	if ((card->type != MMC_TYPE_MMC) && (card->type != MMC_TYPE_SD))
		return -ENODEV;

P
Pierre Ossman 已提交
1174 1175 1176 1177
	ret = device_create_file(&card->dev, &dev_attr_test);
	if (ret)
		return ret;

1178 1179
	dev_info(&card->dev, "Card claimed for testing.\n");

P
Pierre Ossman 已提交
1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211
	return 0;
}

static void mmc_test_remove(struct mmc_card *card)
{
	device_remove_file(&card->dev, &dev_attr_test);
}

static struct mmc_driver mmc_driver = {
	.drv		= {
		.name	= "mmc_test",
	},
	.probe		= mmc_test_probe,
	.remove		= mmc_test_remove,
};

static int __init mmc_test_init(void)
{
	return mmc_register_driver(&mmc_driver);
}

static void __exit mmc_test_exit(void)
{
	mmc_unregister_driver(&mmc_driver);
}

module_init(mmc_test_init);
module_exit(mmc_test_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Multimedia Card (MMC) host test driver");
MODULE_AUTHOR("Pierre Ossman");