Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit af517150 authored by David Brownell's avatar David Brownell Committed by Pierre Ossman
Browse files

MMC core learns about SPI



Teach the MMC/SD/SDIO core about using SPI mode.

 - Use mmc_host_is_spi() so enumeration works through SPI signaling
   and protocols, not just the native versions.

 - Provide the SPI response type flags with each request issued,
   including requests from the new lock/unlock code.

 - Understand that cmd->resp[0] and mmc_get_status() results for SPI
   return different values than for "native" MMC/SD protocol; this
   affects resetting, checking card lock status, and some others.

 - Understand that some commands act a bit differently ... notably:
     * OP_COND command doesn't return the OCR
     * APP_CMD status doesn't have an R1_APP_CMD analogue

Those changes required some new and updated primitives:

 - Provide utilities to access two SPI-only requests, and one
   request that wasn't previously needed:
     * mmc_spi_read_ocr() ... SPI only
     * mmc_spi_set_crc() ... SPI only (override by module parm)
     * mmc_send_cid() ... for use without broadcast mode

 - Updated internal routines:
     * Previous mmc_send_csd() modified into mmc_send_cxd_native();
       it uses native "R2" responses, which include 16 bytes of data.
     * Previous mmc_send_ext_csd() becomes new mmc_send_cxd_data()
       helper for command-and-data access
     * Bugfix to that mmc_send_cxd_data() code:  dma-to-stack is
       unsafe/nonportable, so kmalloc a bounce buffer instead.

 - Modified mmc_send_ext_csd() now uses mmc_send_cxd_data() helper

 - Modified mmc_send_csd(), and new mmc_spi_send_cid(), routines use
   those helper routines based on whether they're native or SPI

The newest categories of cards supported by the MMC stack aren't expected
to work yet with SPI:  MMC or SD cards with over 4GB data, and SDIO.
All those cards support SPI mode, so eventually they should work too.

Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarPierre Ossman <drzeus@drzeus.cx>
parent 7213d175
Loading
Loading
Loading
Loading
+18 −6
Original line number Diff line number Diff line
@@ -243,10 +243,17 @@ int mmc_add_card(struct mmc_card *card)
		break;
	}

	if (mmc_host_is_spi(card->host)) {
		printk(KERN_INFO "%s: new %s%s card on SPI\n",
			mmc_hostname(card->host),
			mmc_card_highspeed(card) ? "high speed " : "",
			type);
	} else {
		printk(KERN_INFO "%s: new %s%s card at address %04x\n",
			mmc_hostname(card->host),
			mmc_card_highspeed(card) ? "high speed " : "",
			type, card->rca);
	}

	card->dev.uevent_suppress = 1;

@@ -278,8 +285,13 @@ int mmc_add_card(struct mmc_card *card)
void mmc_remove_card(struct mmc_card *card)
{
	if (mmc_card_present(card)) {
		if (mmc_host_is_spi(card->host)) {
			printk(KERN_INFO "%s: SPI card removed\n",
				mmc_hostname(card->host));
		} else {
			printk(KERN_INFO "%s: card %04x removed\n",
				mmc_hostname(card->host), card->rca);
		}

		if (card->host->bus_ops->sysfs_remove)
			card->host->bus_ops->sysfs_remove(card->host, card);
+24 −4
Original line number Diff line number Diff line
@@ -41,6 +41,14 @@ extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr);

static struct workqueue_struct *workqueue;

/*
 * Enabling software CRCs on the data blocks can be a significant (30%)
 * performance cost, and for other reasons may not always be desired.
 * So we allow it it to be disabled.
 */
int use_spi_crc = 1;
module_param(use_spi_crc, bool, 0);

/*
 * Internal function. Schedule delayed work in the MMC work queue.
 */
@@ -71,6 +79,11 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
	struct mmc_command *cmd = mrq->cmd;
	int err = cmd->error;

	if (err && cmd->retries && mmc_host_is_spi(host)) {
		if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
			cmd->retries = 0;
	}

	if (err && cmd->retries) {
		pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
			mmc_hostname(host), cmd->opcode, err);
@@ -453,8 +466,13 @@ static void mmc_power_up(struct mmc_host *host)
	int bit = fls(host->ocr_avail) - 1;

	host->ios.vdd = bit;
	host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
	if (mmc_host_is_spi(host)) {
		host->ios.chip_select = MMC_CS_HIGH;
		host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
	} else {
		host->ios.chip_select = MMC_CS_DONTCARE;
		host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
	}
	host->ios.power_mode = MMC_POWER_UP;
	host->ios.bus_width = MMC_BUS_WIDTH_1;
	host->ios.timing = MMC_TIMING_LEGACY;
@@ -481,8 +499,10 @@ static void mmc_power_off(struct mmc_host *host)
{
	host->ios.clock = 0;
	host->ios.vdd = 0;
	if (!mmc_host_is_spi(host)) {
		host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
		host->ios.chip_select = MMC_CS_DONTCARE;
	}
	host->ios.power_mode = MMC_POWER_OFF;
	host->ios.bus_width = MMC_BUS_WIDTH_1;
	host->ios.timing = MMC_TIMING_LEGACY;
+2 −0
Original line number Diff line number Diff line
@@ -48,5 +48,7 @@ void mmc_rescan(struct work_struct *work);
void mmc_start_host(struct mmc_host *host);
void mmc_stop_host(struct mmc_host *host);

extern int use_spi_crc;

#endif
+37 −13
Original line number Diff line number Diff line
@@ -165,8 +165,6 @@ static int mmc_read_ext_csd(struct mmc_card *card)

	BUG_ON(!card);

	err = -EIO;

	if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
		return 0;

@@ -279,9 +277,21 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
	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;
@@ -309,13 +319,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
	}

	/*
	 * Set card RCA.
	 * 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) {
		/*
@@ -336,13 +348,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
	/*
	 * 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 (!oldcard) {
		/*
		 * Fetch and process extened CSD.
		 * Fetch and process extended CSD.
		 */
		err = mmc_read_ext_csd(card);
		if (err)
@@ -502,6 +516,7 @@ static void mmc_suspend(struct mmc_host *host)
	BUG_ON(!host->card);

	mmc_claim_host(host);
	if (!mmc_host_is_spi(host))
		mmc_deselect_cards(host);
	host->card->state &= ~MMC_STATE_HIGHSPEED;
	mmc_release_host(host);
@@ -562,6 +577,15 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)

	mmc_attach_bus(host, &mmc_ops);

	/*
	 * We need to get OCR a different way for SPI.
	 */
	if (mmc_host_is_spi(host)) {
		err = mmc_spi_read_ocr(host, 1, &ocr);
		if (err)
			goto err;
	}

	/*
	 * Sanity check the voltages that the card claims to
	 * support.
+135 −31
Original line number Diff line number Diff line
@@ -63,23 +63,36 @@ int mmc_go_idle(struct mmc_host *host)
	int err;
	struct mmc_command cmd;

	/*
	 * Non-SPI hosts need to prevent chipselect going active during
	 * GO_IDLE; that would put chips into SPI mode.  Remind them of
	 * that in case of hardware that won't pull up DAT3/nCS otherwise.
	 *
	 * SPI hosts ignore ios.chip_select; it's managed according to
	 * rules that must accomodate non-MMC slaves which this layer
	 * won't even know about.
	 */
	if (!mmc_host_is_spi(host)) {
		mmc_set_chip_select(host, MMC_CS_HIGH);

		mmc_delay(1);
	}

	memset(&cmd, 0, sizeof(struct mmc_command));

	cmd.opcode = MMC_GO_IDLE_STATE;
	cmd.arg = 0;
	cmd.flags = MMC_RSP_NONE | MMC_CMD_BC;
	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;

	err = mmc_wait_for_cmd(host, &cmd, 0);

	mmc_delay(1);

	if (!mmc_host_is_spi(host)) {
		mmc_set_chip_select(host, MMC_CS_DONTCARE);

		mmc_delay(1);
	}

	host->use_spi_crc = 0;

	return err;
}
@@ -94,23 +107,33 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
	memset(&cmd, 0, sizeof(struct mmc_command));

	cmd.opcode = MMC_SEND_OP_COND;
	cmd.arg = ocr;
	cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
	cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;
	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;

	for (i = 100; i; i--) {
		err = mmc_wait_for_cmd(host, &cmd, 0);
		if (err)
			break;

		if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
		/* if we're just probing, do a single pass */
		if (ocr == 0)
			break;

		/* otherwise wait until reset completes */
		if (mmc_host_is_spi(host)) {
			if (!(cmd.resp[0] & R1_SPI_IDLE))
				break;
		} else {
			if (cmd.resp[0] & MMC_CARD_BUSY)
				break;
		}

		err = -ETIMEDOUT;

		mmc_delay(10);
	}

	if (rocr)
	if (rocr && !mmc_host_is_spi(host))
		*rocr = cmd.resp[0];

	return err;
@@ -160,40 +183,46 @@ int mmc_set_relative_addr(struct mmc_card *card)
	return 0;
}

int mmc_send_csd(struct mmc_card *card, u32 *csd)
static int
mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
{
	int err;
	struct mmc_command cmd;

	BUG_ON(!card);
	BUG_ON(!card->host);
	BUG_ON(!csd);
	BUG_ON(!host);
	BUG_ON(!cxd);

	memset(&cmd, 0, sizeof(struct mmc_command));

	cmd.opcode = MMC_SEND_CSD;
	cmd.arg = card->rca << 16;
	cmd.opcode = opcode;
	cmd.arg = arg;
	cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;

	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
	if (err)
		return err;

	memcpy(csd, cmd.resp, sizeof(u32) * 4);
	memcpy(cxd, cmd.resp, sizeof(u32) * 4);

	return 0;
}

int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
static int
mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
		u32 opcode, void *buf, unsigned len)
{
	struct mmc_request mrq;
	struct mmc_command cmd;
	struct mmc_data data;
	struct scatterlist sg;
	void *data_buf;

	BUG_ON(!card);
	BUG_ON(!card->host);
	BUG_ON(!ext_csd);
	/* dma onto stack is unsafe/nonportable, but callers to this
	 * routine normally provide temporary on-stack buffers ...
	 */
	data_buf = kmalloc(len, GFP_KERNEL);
	if (data_buf == NULL)
		return -ENOMEM;

	memset(&mrq, 0, sizeof(struct mmc_request));
	memset(&cmd, 0, sizeof(struct mmc_command));
@@ -202,21 +231,31 @@ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
	mrq.cmd = &cmd;
	mrq.data = &data;

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

	data.blksz = 512;
	/* NOTE HACK:  the MMC_RSP_SPI_R1 is always correct here, but we
	 * rely on callers to never use this with "native" calls for reading
	 * CSD or CID.  Native versions of those commands use the R2 type,
	 * not R1 plus a data block.
	 */
	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;

	data.blksz = len;
	data.blocks = 1;
	data.flags = MMC_DATA_READ;
	data.sg = &sg;
	data.sg_len = 1;

	sg_init_one(&sg, ext_csd, 512);
	sg_init_one(&sg, data_buf, len);

	if (card)
		mmc_set_data_timeout(&data, card);

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

	memcpy(buf, data_buf, len);
	kfree(data_buf);

	if (cmd.error)
		return cmd.error;
@@ -226,6 +265,67 @@ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
	return 0;
}

int mmc_send_csd(struct mmc_card *card, u32 *csd)
{
	if (!mmc_host_is_spi(card->host))
		return mmc_send_cxd_native(card->host, card->rca << 16,
				csd, MMC_SEND_CSD);

	return mmc_send_cxd_data(card, card->host, MMC_SEND_CSD, csd, 16);
}

int mmc_send_cid(struct mmc_host *host, u32 *cid)
{
	if (!mmc_host_is_spi(host)) {
		if (!host->card)
			return -EINVAL;
		return mmc_send_cxd_native(host, host->card->rca << 16,
				cid, MMC_SEND_CID);
	}

	return mmc_send_cxd_data(NULL, host, MMC_SEND_CID, cid, 16);
}

int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
{
	return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD,
			ext_csd, 512);
}

int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
{
	struct mmc_command cmd;
	int err;

	memset(&cmd, 0, sizeof(struct mmc_command));

	cmd.opcode = MMC_SPI_READ_OCR;
	cmd.arg = highcap ? (1 << 30) : 0;
	cmd.flags = MMC_RSP_SPI_R3;

	err = mmc_wait_for_cmd(host, &cmd, 0);

	*ocrp = cmd.resp[1];
	return err;
}

int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
{
	struct mmc_command cmd;
	int err;

	memset(&cmd, 0, sizeof(struct mmc_command));

	cmd.opcode = MMC_SPI_CRC_ON_OFF;
	cmd.flags = MMC_RSP_SPI_R1;
	cmd.arg = use_crc;

	err = mmc_wait_for_cmd(host, &cmd, 0);
	if (!err)
		host->use_spi_crc = use_crc;
	return err;
}

int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
{
	int err;
@@ -241,7 +341,7 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
		  (index << 16) |
		  (value << 8) |
		  set;
	cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
	cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;

	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
	if (err)
@@ -261,13 +361,17 @@ int mmc_send_status(struct mmc_card *card, u32 *status)
	memset(&cmd, 0, sizeof(struct mmc_command));

	cmd.opcode = MMC_SEND_STATUS;
	if (!mmc_host_is_spi(card->host))
		cmd.arg = card->rca << 16;
	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
	cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;

	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
	if (err)
		return err;

	/* NOTE: callers are required to understand the difference
	 * between "native" and SPI format status words!
	 */
	if (status)
		*status = cmd.resp[0];

Loading