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

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
...@@ -243,10 +243,17 @@ int mmc_add_card(struct mmc_card *card) ...@@ -243,10 +243,17 @@ int mmc_add_card(struct mmc_card *card)
break; break;
} }
printk(KERN_INFO "%s: new %s%s card at address %04x\n", if (mmc_host_is_spi(card->host)) {
mmc_hostname(card->host), printk(KERN_INFO "%s: new %s%s card on SPI\n",
mmc_card_highspeed(card) ? "high speed " : "", mmc_hostname(card->host),
type, card->rca); 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; card->dev.uevent_suppress = 1;
...@@ -278,8 +285,13 @@ int mmc_add_card(struct mmc_card *card) ...@@ -278,8 +285,13 @@ int mmc_add_card(struct mmc_card *card)
void mmc_remove_card(struct mmc_card *card) void mmc_remove_card(struct mmc_card *card)
{ {
if (mmc_card_present(card)) { if (mmc_card_present(card)) {
printk(KERN_INFO "%s: card %04x removed\n", if (mmc_host_is_spi(card->host)) {
mmc_hostname(card->host), card->rca); 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) if (card->host->bus_ops->sysfs_remove)
card->host->bus_ops->sysfs_remove(card->host, card); card->host->bus_ops->sysfs_remove(card->host, card);
......
...@@ -41,6 +41,14 @@ extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr); ...@@ -41,6 +41,14 @@ extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
static struct workqueue_struct *workqueue; 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. * 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) ...@@ -71,6 +79,11 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
struct mmc_command *cmd = mrq->cmd; struct mmc_command *cmd = mrq->cmd;
int err = cmd->error; 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) { if (err && cmd->retries) {
pr_debug("%s: req failed (CMD%u): %d, retrying...\n", pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
mmc_hostname(host), cmd->opcode, err); mmc_hostname(host), cmd->opcode, err);
...@@ -453,8 +466,13 @@ static void mmc_power_up(struct mmc_host *host) ...@@ -453,8 +466,13 @@ static void mmc_power_up(struct mmc_host *host)
int bit = fls(host->ocr_avail) - 1; int bit = fls(host->ocr_avail) - 1;
host->ios.vdd = bit; host->ios.vdd = bit;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; if (mmc_host_is_spi(host)) {
host->ios.chip_select = MMC_CS_DONTCARE; 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.power_mode = MMC_POWER_UP;
host->ios.bus_width = MMC_BUS_WIDTH_1; host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY; host->ios.timing = MMC_TIMING_LEGACY;
...@@ -481,8 +499,10 @@ static void mmc_power_off(struct mmc_host *host) ...@@ -481,8 +499,10 @@ static void mmc_power_off(struct mmc_host *host)
{ {
host->ios.clock = 0; host->ios.clock = 0;
host->ios.vdd = 0; host->ios.vdd = 0;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; if (!mmc_host_is_spi(host)) {
host->ios.chip_select = MMC_CS_DONTCARE; host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
host->ios.chip_select = MMC_CS_DONTCARE;
}
host->ios.power_mode = MMC_POWER_OFF; host->ios.power_mode = MMC_POWER_OFF;
host->ios.bus_width = MMC_BUS_WIDTH_1; host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY; host->ios.timing = MMC_TIMING_LEGACY;
......
...@@ -48,5 +48,7 @@ void mmc_rescan(struct work_struct *work); ...@@ -48,5 +48,7 @@ void mmc_rescan(struct work_struct *work);
void mmc_start_host(struct mmc_host *host); void mmc_start_host(struct mmc_host *host);
void mmc_stop_host(struct mmc_host *host); void mmc_stop_host(struct mmc_host *host);
extern int use_spi_crc;
#endif #endif
...@@ -165,8 +165,6 @@ static int mmc_read_ext_csd(struct mmc_card *card) ...@@ -165,8 +165,6 @@ static int mmc_read_ext_csd(struct mmc_card *card)
BUG_ON(!card); BUG_ON(!card);
err = -EIO;
if (card->csd.mmca_vsn < CSD_SPEC_VER_4) if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
return 0; return 0;
...@@ -279,10 +277,22 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -279,10 +277,22 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
if (err) if (err)
goto 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. * Fetch CID from card.
*/ */
err = mmc_all_send_cid(host, cid); if (mmc_host_is_spi(host))
err = mmc_send_cid(host, cid);
else
err = mmc_all_send_cid(host, cid);
if (err) if (err)
goto err; goto err;
...@@ -309,13 +319,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -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.
*/ */
err = mmc_set_relative_addr(card); if (!mmc_host_is_spi(host)) {
if (err) err = mmc_set_relative_addr(card);
goto free_card; if (err)
goto free_card;
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}
if (!oldcard) { if (!oldcard) {
/* /*
...@@ -336,13 +348,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -336,13 +348,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
/* /*
* Select card, as all following commands rely on that. * Select card, as all following commands rely on that.
*/ */
err = mmc_select_card(card); if (!mmc_host_is_spi(host)) {
if (err) err = mmc_select_card(card);
goto free_card; if (err)
goto free_card;
}
if (!oldcard) { if (!oldcard) {
/* /*
* Fetch and process extened CSD. * Fetch and process extended CSD.
*/ */
err = mmc_read_ext_csd(card); err = mmc_read_ext_csd(card);
if (err) if (err)
...@@ -502,7 +516,8 @@ static void mmc_suspend(struct mmc_host *host) ...@@ -502,7 +516,8 @@ static void mmc_suspend(struct mmc_host *host)
BUG_ON(!host->card); BUG_ON(!host->card);
mmc_claim_host(host); mmc_claim_host(host);
mmc_deselect_cards(host); if (!mmc_host_is_spi(host))
mmc_deselect_cards(host);
host->card->state &= ~MMC_STATE_HIGHSPEED; host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_release_host(host); mmc_release_host(host);
} }
...@@ -562,6 +577,15 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) ...@@ -562,6 +577,15 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
mmc_attach_bus(host, &mmc_ops); 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 * Sanity check the voltages that the card claims to
* support. * support.
......
...@@ -63,23 +63,36 @@ int mmc_go_idle(struct mmc_host *host) ...@@ -63,23 +63,36 @@ int mmc_go_idle(struct mmc_host *host)
int err; int err;
struct mmc_command cmd; struct mmc_command cmd;
mmc_set_chip_select(host, MMC_CS_HIGH); /*
* Non-SPI hosts need to prevent chipselect going active during
mmc_delay(1); * 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)); memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_GO_IDLE_STATE; cmd.opcode = MMC_GO_IDLE_STATE;
cmd.arg = 0; 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); err = mmc_wait_for_cmd(host, &cmd, 0);
mmc_delay(1); 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; return err;
} }
...@@ -94,23 +107,33 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) ...@@ -94,23 +107,33 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
memset(&cmd, 0, sizeof(struct mmc_command)); memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_OP_COND; cmd.opcode = MMC_SEND_OP_COND;
cmd.arg = ocr; cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;
cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
for (i = 100; i; i--) { for (i = 100; i; i--) {
err = mmc_wait_for_cmd(host, &cmd, 0); err = mmc_wait_for_cmd(host, &cmd, 0);
if (err) if (err)
break; break;
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) /* if we're just probing, do a single pass */
if (ocr == 0)
break; 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; err = -ETIMEDOUT;
mmc_delay(10); mmc_delay(10);
} }
if (rocr) if (rocr && !mmc_host_is_spi(host))
*rocr = cmd.resp[0]; *rocr = cmd.resp[0];
return err; return err;
...@@ -160,40 +183,46 @@ int mmc_set_relative_addr(struct mmc_card *card) ...@@ -160,40 +183,46 @@ int mmc_set_relative_addr(struct mmc_card *card)
return 0; 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; int err;
struct mmc_command cmd; struct mmc_command cmd;
BUG_ON(!card); BUG_ON(!host);
BUG_ON(!card->host); BUG_ON(!cxd);
BUG_ON(!csd);
memset(&cmd, 0, sizeof(struct mmc_command)); memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_CSD; cmd.opcode = opcode;
cmd.arg = card->rca << 16; cmd.arg = arg;
cmd.flags = MMC_RSP_R2 | MMC_CMD_AC; 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) if (err)
return err; return err;
memcpy(csd, cmd.resp, sizeof(u32) * 4); memcpy(cxd, cmd.resp, sizeof(u32) * 4);
return 0; 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_request mrq;
struct mmc_command cmd; struct mmc_command cmd;
struct mmc_data data; struct mmc_data data;
struct scatterlist sg; struct scatterlist sg;
void *data_buf;
BUG_ON(!card); /* dma onto stack is unsafe/nonportable, but callers to this
BUG_ON(!card->host); * routine normally provide temporary on-stack buffers ...
BUG_ON(!ext_csd); */
data_buf = kmalloc(len, GFP_KERNEL);
if (data_buf == NULL)
return -ENOMEM;
memset(&mrq, 0, sizeof(struct mmc_request)); memset(&mrq, 0, sizeof(struct mmc_request));
memset(&cmd, 0, sizeof(struct mmc_command)); memset(&cmd, 0, sizeof(struct mmc_command));
...@@ -202,21 +231,31 @@ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd) ...@@ -202,21 +231,31 @@ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
mrq.cmd = &cmd; mrq.cmd = &cmd;
mrq.data = &data; mrq.data = &data;
cmd.opcode = MMC_SEND_EXT_CSD; cmd.opcode = opcode;
cmd.arg = 0; 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.blocks = 1;
data.flags = MMC_DATA_READ; data.flags = MMC_DATA_READ;
data.sg = &sg; data.sg = &sg;
data.sg_len = 1; 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) if (cmd.error)
return cmd.error; return cmd.error;
...@@ -226,6 +265,67 @@ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd) ...@@ -226,6 +265,67 @@ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
return 0; 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 mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
{ {
int err; int err;
...@@ -241,7 +341,7 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value) ...@@ -241,7 +341,7 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
(index << 16) | (index << 16) |
(value << 8) | (value << 8) |
set; 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); err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err) if (err)
...@@ -261,13 +361,17 @@ int mmc_send_status(struct mmc_card *card, u32 *status) ...@@ -261,13 +361,17 @@ int mmc_send_status(struct mmc_card *card, u32 *status)
memset(&cmd, 0, sizeof(struct mmc_command)); memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_STATUS; cmd.opcode = MMC_SEND_STATUS;
cmd.arg = card->rca << 16; if (!mmc_host_is_spi(card->host))
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; 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); err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err) if (err)
return err; return err;
/* NOTE: callers are required to understand the difference
* between "native" and SPI format status words!
*/
if (status) if (status)
*status = cmd.resp[0]; *status = cmd.resp[0];
......
...@@ -22,6 +22,9 @@ int mmc_send_csd(struct mmc_card *card, u32 *csd); ...@@ -22,6 +22,9 @@ int mmc_send_csd(struct mmc_card *card, u32 *csd);
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value); int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value);
int mmc_send_status(struct mmc_card *card, u32 *status); int mmc_send_status(struct mmc_card *card, u32 *status);
int mmc_send_cid(struct mmc_host *host, u32 *cid);
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
#endif #endif
...@@ -322,10 +322,22 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, ...@@ -322,10 +322,22 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
if (err) if (err)
goto 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. * Fetch CID from card.
*/ */
err = mmc_all_send_cid(host, cid); if (mmc_host_is_spi(host))
err = mmc_send_cid(host, cid);
else
err = mmc_all_send_cid(host, cid);
if (err) if (err)
goto err; goto err;
...@@ -351,13 +363,15 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, ...@@ -351,13 +363,15 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
} }
/* /*
* Set card RCA. * For native busses: get card RCA and quit open drain mode.
*/ */
err = mmc_send_relative_addr(host, &card->rca); if (!mmc_host_is_spi(host)) {
if (err) err = mmc_send_relative_addr(host, &card->rca);
goto free_card; if (err)
goto free_card;
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}
if (!oldcard) { if (!oldcard) {
/* /*
...@@ -377,9 +391,11 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, ...@@ -377,9 +391,11 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
/* /*
* Select card, as all following commands rely on that. * Select card, as all following commands rely on that.
*/ */
err = mmc_select_card(card); if (!mmc_host_is_spi(host)) {
if (err) err = mmc_select_card(card);
goto free_card; if (err)
goto free_card;
}
if (!oldcard) { if (!oldcard) {
/* /*
...@@ -562,7 +578,8 @@ static void mmc_sd_suspend(struct mmc_host *host) ...@@ -562,7 +578,8 @@ static void mmc_sd_suspend(struct mmc_host *host)
BUG_ON(!host->card); BUG_ON(!host->card);
mmc_claim_host(host); mmc_claim_host(host);
mmc_deselect_cards(host); if (!mmc_host_is_spi(host))
mmc_deselect_cards(host);
host->card->state &= ~MMC_STATE_HIGHSPEED; host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_release_host(host); mmc_release_host(host);
} }
...@@ -622,6 +639,17 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) ...@@ -622,6 +639,17 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
mmc_attach_bus(host, &mmc_sd_ops); mmc_attach_bus(host, &mmc_sd_ops);
/*
* We need to get OCR a different way for SPI.
*/
if (mmc_host_is_spi(host)) {
mmc_go_idle(host);
err = mmc_spi_read_ocr(host, 0, &ocr);
if (err)
goto err;
}
/* /*
* Sanity check the voltages that the card claims to * Sanity check the voltages that the card claims to
* support. * support.
......
...@@ -33,10 +33,10 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) ...@@ -33,10 +33,10 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
if (card) { if (card) {
cmd.arg = card->rca << 16; cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
} else { } else {
cmd.arg = 0; cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_BCR; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_BCR;
} }
err = mmc_wait_for_cmd(host, &cmd, 0); err = mmc_wait_for_cmd(host, &cmd, 0);
...@@ -44,7 +44,7 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) ...@@ -44,7 +44,7 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
return err; return err;
/* Check that card supported application commands */ /* Check that card supported application commands */
if (!(cmd.resp[0] & R1_APP_CMD)) if (!mmc_host_is_spi(host) && !(cmd.resp[0] & R1_APP_CMD))
return -EOPNOTSUPP; return -EOPNOTSUPP;
return 0; return 0;
...@@ -83,8 +83,14 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, ...@@ -83,8 +83,14 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
memset(&mrq, 0, sizeof(struct mmc_request)); memset(&mrq, 0, sizeof(struct mmc_request));
err = mmc_app_cmd(host, card); err = mmc_app_cmd(host, card);
if (err) if (err) {
/* no point in retrying; no APP commands allowed */
if (mmc_host_is_spi(host)) {
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
break;
}
continue; continue;
}
memset(&mrq, 0, sizeof(struct mmc_request)); memset(&mrq, 0, sizeof(struct mmc_request));
...@@ -99,6 +105,12 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, ...@@ -99,6 +105,12 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
err = cmd->error; err = cmd->error;
if (!cmd->error) if (!cmd->error)
break; break;
/* no point in retrying illegal APP commands */
if (mmc_host_is_spi(host)) {
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
break;
}
} }
return err; return err;
...@@ -147,23 +159,36 @@ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) ...@@ -147,23 +159,36 @@ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
memset(&cmd, 0, sizeof(struct mmc_command)); memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_APP_OP_COND; cmd.opcode = SD_APP_OP_COND;
cmd.arg = ocr; if (mmc_host_is_spi(host))
cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */
else
cmd.arg = ocr;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
for (i = 100; i; i--) { for (i = 100; i; i--) {
err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES); err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
if (err) if (err)
break; break;
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) /* if we're just probing, do a single pass */
if (ocr == 0)
break; 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; err = -ETIMEDOUT;
mmc_delay(10); mmc_delay(10);
} }
if (rocr) if (rocr && !mmc_host_is_spi(host))
*rocr = cmd.resp[0]; *rocr = cmd.resp[0];
return err; return err;
...@@ -174,6 +199,7 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr) ...@@ -174,6 +199,7 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
struct mmc_command cmd; struct mmc_command cmd;
int err; int err;
static const u8 test_pattern = 0xAA; static const u8 test_pattern = 0xAA;
u8 result_pattern;
/* /*
* To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
...@@ -182,13 +208,18 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr) ...@@ -182,13 +208,18 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
*/ */
cmd.opcode = SD_SEND_IF_COND; cmd.opcode = SD_SEND_IF_COND;
cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR; cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR;
err = mmc_wait_for_cmd(host, &cmd, 0); err = mmc_wait_for_cmd(host, &cmd, 0);
if (err) if (err)
return err; return err;
if ((cmd.resp[0] & 0xFF) != test_pattern) if (mmc_host_is_spi(host))
result_pattern = cmd.resp[1] & 0xFF;
else
result_pattern = cmd.resp[0] & 0xFF;
if (result_pattern != test_pattern)
return -EIO; return -EIO;
return 0; return 0;
...@@ -229,6 +260,8 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr) ...@@ -229,6 +260,8 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
BUG_ON(!card->host); BUG_ON(!card->host);
BUG_ON(!scr); BUG_ON(!scr);
/* NOTE: caller guarantees scr is heap-allocated */
err = mmc_app_cmd(card->host, card); err = mmc_app_cmd(card->host, card);
if (err) if (err)
return err; return err;
...@@ -242,7 +275,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr) ...@@ -242,7 +275,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
cmd.opcode = SD_APP_SEND_SCR; cmd.opcode = SD_APP_SEND_SCR;
cmd.arg = 0; cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
data.blksz = 8; data.blksz = 8;
data.blocks = 1; data.blocks = 1;
...@@ -278,6 +311,8 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, ...@@ -278,6 +311,8 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
BUG_ON(!card); BUG_ON(!card);
BUG_ON(!card->host); BUG_ON(!card->host);
/* NOTE: caller guarantees resp is heap-allocated */
mode = !!mode; mode = !!mode;
value &= 0xF; value &= 0xF;
...@@ -292,7 +327,7 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, ...@@ -292,7 +327,7 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
cmd.arg = mode << 31 | 0x00FFFFFF; cmd.arg = mode << 31 | 0x00FFFFFF;
cmd.arg &= ~(0xF << (group * 4)); cmd.arg &= ~(0xF << (group * 4));
cmd.arg |= value << (group * 4); cmd.arg |= value << (group * 4);
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
data.blksz = 64; data.blksz = 64;
data.blocks = 1; data.blocks = 1;
......
...@@ -269,6 +269,15 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) ...@@ -269,6 +269,15 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
if (err) if (err)
goto 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;
}
/* /*
* The number of functions on the card is encoded inside * The number of functions on the card is encoded inside
* the ocr. * the ocr.
...@@ -290,20 +299,24 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) ...@@ -290,20 +299,24 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
host->card = card; host->card = card;
/* /*
* Set card RCA. * For native busses: set card RCA and quit open drain mode.
*/ */
err = mmc_send_relative_addr(host, &card->rca); if (!mmc_host_is_spi(host)) {
if (err) err = mmc_send_relative_addr(host, &card->rca);
goto remove; if (err)
goto remove;
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}
/* /*
* Select card, as all following commands rely on that. * Select card, as all following commands rely on that.
*/ */
err = mmc_select_card(card); if (!mmc_host_is_spi(host)) {
if (err) err = mmc_select_card(card);
goto remove; if (err)
goto remove;
}
/* /*
* Read the common registers. * Read the common registers.
......
...@@ -30,23 +30,39 @@ int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) ...@@ -30,23 +30,39 @@ int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
cmd.opcode = SD_IO_SEND_OP_COND; cmd.opcode = SD_IO_SEND_OP_COND;
cmd.arg = ocr; cmd.arg = ocr;
cmd.flags = MMC_RSP_R4 | MMC_CMD_BCR; cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR;
for (i = 100; i; i--) { for (i = 100; i; i--) {
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err) if (err)
break; break;
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) /* if we're just probing, do a single pass */
if (ocr == 0)
break; break;
/* otherwise wait until reset completes */
if (mmc_host_is_spi(host)) {
/*
* Both R1_SPI_IDLE and MMC_CARD_BUSY indicate
* an initialized card under SPI, but some cards
* (Marvell's) only behave when looking at this
* one.
*/
if (cmd.resp[1] & MMC_CARD_BUSY)
break;
} else {
if (cmd.resp[0] & MMC_CARD_BUSY)
break;
}
err = -ETIMEDOUT; err = -ETIMEDOUT;
mmc_delay(10); mmc_delay(10);
} }
if (rocr) if (rocr)
*rocr = cmd.resp[0]; *rocr = cmd.resp[mmc_host_is_spi(host) ? 1 : 0];
return err; return err;
} }
...@@ -68,21 +84,29 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, ...@@ -68,21 +84,29 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
cmd.arg |= (write && out) ? 0x08000000 : 0x00000000; cmd.arg |= (write && out) ? 0x08000000 : 0x00000000;
cmd.arg |= addr << 9; cmd.arg |= addr << 9;
cmd.arg |= in; cmd.arg |= in;
cmd.flags = MMC_RSP_R5 | MMC_CMD_AC; cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, 0); err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err) if (err)
return err; return err;
if (cmd.resp[0] & R5_ERROR) if (mmc_host_is_spi(card->host)) {
return -EIO; /* host driver already reported errors */
if (cmd.resp[0] & R5_FUNCTION_NUMBER) } else {
return -EINVAL; if (cmd.resp[0] & R5_ERROR)
if (cmd.resp[0] & R5_OUT_OF_RANGE) return -EIO;
return -ERANGE; if (cmd.resp[0] & R5_FUNCTION_NUMBER)
return -EINVAL;
if (cmd.resp[0] & R5_OUT_OF_RANGE)
return -ERANGE;
}
if (out) if (out) {
*out = cmd.resp[0] & 0xFF; if (mmc_host_is_spi(card->host))
*out = (cmd.resp[0] >> 8) & 0xFF;
else
*out = cmd.resp[0] & 0xFF;
}
return 0; return 0;
} }
...@@ -117,7 +141,7 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, ...@@ -117,7 +141,7 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
cmd.arg |= (blksz == 512) ? 0 : blksz; /* byte mode */ cmd.arg |= (blksz == 512) ? 0 : blksz; /* byte mode */
else else
cmd.arg |= 0x08000000 | blocks; /* block mode */ cmd.arg |= 0x08000000 | blocks; /* block mode */
cmd.flags = MMC_RSP_R5 | MMC_CMD_ADTC; cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
data.blksz = blksz; data.blksz = blksz;
data.blocks = blocks; data.blocks = blocks;
...@@ -136,12 +160,16 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, ...@@ -136,12 +160,16 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
if (data.error) if (data.error)
return data.error; return data.error;
if (cmd.resp[0] & R5_ERROR) if (mmc_host_is_spi(card->host)) {
return -EIO; /* host driver already reported errors */
if (cmd.resp[0] & R5_FUNCTION_NUMBER) } else {
return -EINVAL; if (cmd.resp[0] & R5_ERROR)
if (cmd.resp[0] & R5_OUT_OF_RANGE) return -EIO;
return -ERANGE; if (cmd.resp[0] & R5_FUNCTION_NUMBER)
return -EINVAL;
if (cmd.resp[0] & R5_OUT_OF_RANGE)
return -ERANGE;
}
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment