[RFC]: mmc bus width testing

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



A long time (2.6.2x days) I tried to submit a patch to do bus width testing on mmc and would appreciate some feedback before I do a formal submission.

Bus width testing is defined in JEDEC Standard No. 84-A441: section A.8.3 Changing the data bus width

The problem is that it does NOT work in all cases.  Some controllers do not handle the CMD19/CMD14 exchange.
(BUSTEST_W/BUSTEST_R)

When this failure happens it is not at all clear what to do....   

We could default to 1 bit mode and just do what we do today and take 4 bit bus width -- assuming CAPS are appropriately enabled.

I was thinking of adding a new MMC_CAP saying controller supports bus width testing.      Any thoughts ?

<snippet of code from today implementation == based on 2.6.32 but with DDR support back ported>

look for mmc_test_bus_width()

My thought was to replace

			if (err) {
				printk(KERN_WARNING "%s: switch to bus width %d ddr %d "
					   "failed\n", mmc_hostname(card->host),
					   1 << bus_width, ddr);
				err = 0;
			} else {
				mmc_set_bus_width_ddr(card->host, bus_width, MMC_SDR_MODE);
				if (mmc_test_bus_width (card, 1<<bus_width))
					break;
			}

with

			if (err) {
				printk(KERN_WARNING "%s: switch to bus width %d ddr %d "
					   "failed\n", mmc_hostname(card->host),
					   1 << bus_width, ddr);
				err = 0;
			} else {
				mmc_set_bus_width_ddr(card->host, bus_width, MMC_SDR_MODE);
				if ((mmc->caps & MMC_CAPS_CONTROLLER_SUPPORTS_BUSTEST) == 0)
					break;
				if (mmc_test_bus_width (card, 1<<bus_width))
					break;
			}

and let the platform code set MMC_CAPS_CONTROLLER_SUPPORTS_BUSTEST



from mmc.c
==========

static int mmc_init_card(struct mmc_host *host, u32 ocr,
	struct mmc_card *oldcard)
{
	struct mmc_card *card;
	int err, ddr = 0;
	u32 cid[4];
	unsigned int max_dtr;
	u32 rocr;

	BUG_ON(!host);
	WARN_ON(!host->claimed);

	/*
	 * Since we're changing the OCR value, we seem to
	 * need to tell some cards to go back to the idle
	 * state.  We wait 1ms to give cards time to
	 * respond.
	 */
	mmc_go_idle(host);

	/* The extra bit indicates that we support high capacity */
	err = mmc_send_op_cond(host, ocr | MMC_CARD_SECTOR_ADDR, &rocr);
	if (err)
		goto err;

	/*
	 * For SPI, enable CRC as appropriate.
	 */
	if (mmc_host_is_spi(host)) {
		err = mmc_spi_set_crc(host, use_spi_crc);
		if (err)
			goto err;
	}

	/*
	 * Fetch CID from card.
	 */
	if (mmc_host_is_spi(host))
		err = mmc_send_cid(host, cid);
	else
		err = mmc_all_send_cid(host, cid);
	if (err)
		goto err;

	if (oldcard) {
		if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
			err = -ENOENT;
			goto err;
		}

		card = oldcard;
	} else {
		/*
		 * Allocate card structure.
		 */
		card = mmc_alloc_card(host, &mmc_type);
		if (IS_ERR(card)) {
			err = PTR_ERR(card);
			goto err;
		}

		card->type = MMC_TYPE_MMC;
		card->rca = 1;
		memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
	}

	/*
	 * For native busses:  set card RCA and quit open drain mode.
	 */
	if (!mmc_host_is_spi(host)) {
		err = mmc_set_relative_addr(card);
		if (err)
			goto free_card;

		mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
	}

	if (!oldcard) {
		/*
		 * Fetch CSD from card.
		 */
		err = mmc_send_csd(card, card->raw_csd);
		if (err)
			goto free_card;

		err = mmc_decode_csd(card);
		if (err)
			goto free_card;
		err = mmc_decode_cid(card);
		if (err)
			goto free_card;
	}

	/*
	 * Select card, as all following commands rely on that.
	 */
	if (!mmc_host_is_spi(host)) {
		err = mmc_select_card(card);
		if (err)
			goto free_card;
	}

#if 0
	if (mmc_card_mmc(card)) {
		/* work around of Micorn 16G emmc */
		/* set bus_width to 1 bit as init state */
		mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
				EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_1);

	}
#endif

	if (!oldcard) {
		/*
		 * Fetch and process extended CSD.
		 */
		err = mmc_read_ext_csd(card);
		if (err)
			goto free_card;

		if (rocr & MMC_CARD_SECTOR_ADDR)
			mmc_card_set_blockaddr(card);
	}


	/* switch to user partition, emmc may stay in boot partition after boot */
	mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
			EXT_CSD_PART_CONF, 0);

	/*
	 * Activate high speed (if supported)
	 */
	if ((card->ext_csd.hs_max_dtr != 0) &&
		(host->caps & MMC_CAP_MMC_HIGHSPEED)) {
		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
			EXT_CSD_HS_TIMING, 1);
		if (err && err != -EBADMSG)
			goto free_card;

		if (err) {
			printk(KERN_WARNING "%s: switch to highspeed failed\n",
			       mmc_hostname(card->host));
			err = 0;
		} else {
			mmc_card_set_highspeed(card);
			mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
		}
	}

	/*
	 * Compute bus speed.
	 */
	max_dtr = (unsigned int)-1;

	if (mmc_card_highspeed(card)) {
		if (max_dtr > card->ext_csd.hs_max_dtr)
			max_dtr = card->ext_csd.hs_max_dtr;
	} else if (max_dtr > card->csd.max_dtr) {
		max_dtr = card->csd.max_dtr;
	}

	mmc_set_clock(host, max_dtr);

	/*
	 * Indicate DDR mode (if supported).
	 */
	if (mmc_card_highspeed(card)) {
		if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
			&& (host->caps & (MMC_CAP_1_8V_DDR)))
				ddr = MMC_1_8V_DDR_MODE;
		else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
			&& (host->caps & (MMC_CAP_1_2V_DDR)))
				ddr = MMC_1_2V_DDR_MODE;
	}

	/*
	 * Activate wide bus and DDR (if supported).
	 */
	if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
	    (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
		unsigned ext_csd_bit, bus_width;
		int temp_caps = host->caps & (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA);

		do {
			if (temp_caps & MMC_CAP_8_BIT_DATA) {
				ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
				bus_width = MMC_BUS_WIDTH_8;
			} else {
				ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
				bus_width = MMC_BUS_WIDTH_4;
			}

			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
					 EXT_CSD_BUS_WIDTH, ext_csd_bit);
			if (err) {
				printk(KERN_WARNING "%s: switch to bus width %d ddr %d "
					   "failed\n", mmc_hostname(card->host),
					   1 << bus_width, ddr);
				err = 0;
			} else {
				mmc_set_bus_width_ddr(card->host, bus_width, MMC_SDR_MODE);
				if (mmc_test_bus_width (card, 1<<bus_width))
					break;
			}
			
			if (bus_width == MMC_BUS_WIDTH_8)
				temp_caps &= ~MMC_CAP_8_BIT_DATA;
			else
				temp_caps &= ~MMC_CAP_4_BIT_DATA;

			if (temp_caps == 0) {
				ext_csd_bit = EXT_CSD_BUS_WIDTH_1;
				bus_width = MMC_BUS_WIDTH_1;
			}
		} while (temp_caps);

		if (temp_caps == 0) {
			ext_csd_bit = EXT_CSD_BUS_WIDTH_1;
			bus_width = MMC_BUS_WIDTH_1;
		}
		else if (temp_caps & MMC_CAP_8_BIT_DATA) {
			if (ddr)
				ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_8;
			else
				ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
			bus_width = MMC_BUS_WIDTH_8;
		} else {
			if (ddr)
				ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_4;
			else
				ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
			bus_width = MMC_BUS_WIDTH_4;
		}

		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
				 EXT_CSD_BUS_WIDTH, ext_csd_bit);

		if (ddr) {
			mmc_card_set_ddr_mode(card);
			mmc_set_bus_width_ddr(card->host, bus_width, ddr);
		} else
			mmc_set_bus_width_ddr(card->host, bus_width, MMC_SDR_MODE);

		if (err && err != -EBADMSG)
			goto free_card;
	}

	if (!oldcard)
		host->card = card;

	return 0;

free_card:
	if (!oldcard)
		mmc_remove_card(card);
err:

	return err;
}


from mmc_ops.c
=============
int mmc_test_bus_width(struct mmc_card *card, int bits)
{
	struct mmc_request mrq;
	struct mmc_command cmd;
	struct mmc_data data;
	struct scatterlist sg;
	int len;
	u8 test_data_write[8];
	u8 test_data_read[64];

	switch (bits) {
	case 8:
		test_data_write[0] = 0x55;
		test_data_write[1] = 0xaa;
		test_data_write[2] = 0x00;
		test_data_write[3] = 0x00;
		test_data_write[4] = 0x00;
		test_data_write[5] = 0x00;
		test_data_write[6] = 0x00;
		test_data_write[7] = 0x00;
		len = 8;
		break;
	case 4:
		test_data_write[0] = 0x5a;
		test_data_write[1] = 0x00;
		test_data_write[2] = 0x00;
		test_data_write[3] = 0x00;
		len = 4;
		break;
	default:
		return 0;
	}

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

	cmd.opcode = MMC_BUSTEST_W;
	cmd.arg = 0;
	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;

	data.flags = MMC_DATA_WRITE;
	data.blksz = 64;
	data.blocks = 1;
	data.sg = &sg;
	data.sg_len = 1;

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

	sg_init_one(&sg, &test_data_write, 64);

	/*
	 * The spec states that MMC_BUSTEST_W and BUSTEST_R accesses have a timeout
	 * of 64 clock cycles.
	 */
	data.timeout_ns = 0;
	data.timeout_clks = 64;

	mmc_wait_for_req(card->host, &mrq);

	if (cmd.error || data.error ) {
		printk(KERN_INFO "Failed to send CMD19: %d %d\n", cmd.error, data.error);
		return 0;
	}

	/* Now read back */
	memset(&mrq, 0, sizeof(struct mmc_request));
	memset(&cmd, 0, sizeof(struct mmc_command));
	memset(&data, 0, sizeof(struct mmc_data));
	memset (&test_data_read, 0, sizeof(test_data_read));

	cmd.opcode = MMC_BUSTEST_R;
	cmd.arg = 0;
	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;

	data.flags = MMC_DATA_READ;
	data.blksz = sizeof(test_data_read);
	data.blocks = 1;
	data.sg = &sg;
	data.sg_len = 1;

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

	sg_init_one(&sg, &test_data_read, sizeof(test_data_read));

	/*
	 * The spec states that MMC_BUSTEST_W and BUSTEST_R accesses have a timeout
	 * of 64 clock cycles.
	 */
	data.timeout_ns = 0;
	data.timeout_clks = 64;

	mmc_wait_for_req(card->host, &mrq);

	if (cmd.error) {
		printk(KERN_INFO "Failed to send CMD14: %d %d\n", cmd.error, data.error);
		return 0;
	}

#if 0
#warning PRINT RESULTS FROM CMD14
	printk (KERN_INFO "%s: Got %02X %02X %02X %02X\n", __FUNCTION__,
		test_data_read[0],
		test_data_read[1],
		test_data_read[2],
		test_data_read[3]);
#endif

	switch (bits) {
	case 8:
		return (test_data_read[0] == 0xaa && test_data_read[1] == 0x55);
	case 4:
		return (test_data_read[0] == 0xa5);
	}
	return 0;
}

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux