From af51715079e7fb6b290e1881d63d815dc4de5011 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 8 Aug 2007 09:11:32 -0700 Subject: 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: David Brownell Signed-off-by: Pierre Ossman --- drivers/mmc/core/mmc_ops.c | 166 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 135 insertions(+), 31 deletions(-) (limited to 'drivers/mmc/core/mmc_ops.c') diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 39567f91a4b..bf4bc6adcfe 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -63,23 +63,36 @@ int mmc_go_idle(struct mmc_host *host) int err; struct mmc_command cmd; - mmc_set_chip_select(host, MMC_CS_HIGH); - - mmc_delay(1); + /* + * 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); - mmc_set_chip_select(host, MMC_CS_DONTCARE); + if (!mmc_host_is_spi(host)) { + mmc_set_chip_select(host, MMC_CS_DONTCARE); + mmc_delay(1); + } - 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); - mmc_set_data_timeout(&data, card); + 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; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + if (!mmc_host_is_spi(card->host)) + cmd.arg = card->rca << 16; + 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]; -- cgit v1.2.3