diff options
author | David Brownell <david-b@pacbell.net> | 2007-08-08 12:11:32 -0400 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2007-09-23 15:51:30 -0400 |
commit | af51715079e7fb6b290e1881d63d815dc4de5011 (patch) | |
tree | 324e81b2346955f130dda8515f2ad4f4ce97c864 /drivers/mmc/core/core.c | |
parent | 7213d175e3b6f6db60f843b72e88857a350e146a (diff) |
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 <dbrownell@users.sourceforge.net>
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers/mmc/core/core.c')
-rw-r--r-- | drivers/mmc/core/core.c | 28 |
1 files changed, 24 insertions, 4 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index bffcaf8df352..bad39442f8fe 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c | |||
@@ -42,6 +42,14 @@ extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr); | |||
42 | static struct workqueue_struct *workqueue; | 42 | static struct workqueue_struct *workqueue; |
43 | 43 | ||
44 | /* | 44 | /* |
45 | * Enabling software CRCs on the data blocks can be a significant (30%) | ||
46 | * performance cost, and for other reasons may not always be desired. | ||
47 | * So we allow it it to be disabled. | ||
48 | */ | ||
49 | int use_spi_crc = 1; | ||
50 | module_param(use_spi_crc, bool, 0); | ||
51 | |||
52 | /* | ||
45 | * Internal function. Schedule delayed work in the MMC work queue. | 53 | * Internal function. Schedule delayed work in the MMC work queue. |
46 | */ | 54 | */ |
47 | static int mmc_schedule_delayed_work(struct delayed_work *work, | 55 | static int mmc_schedule_delayed_work(struct delayed_work *work, |
@@ -71,6 +79,11 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) | |||
71 | struct mmc_command *cmd = mrq->cmd; | 79 | struct mmc_command *cmd = mrq->cmd; |
72 | int err = cmd->error; | 80 | int err = cmd->error; |
73 | 81 | ||
82 | if (err && cmd->retries && mmc_host_is_spi(host)) { | ||
83 | if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND) | ||
84 | cmd->retries = 0; | ||
85 | } | ||
86 | |||
74 | if (err && cmd->retries) { | 87 | if (err && cmd->retries) { |
75 | pr_debug("%s: req failed (CMD%u): %d, retrying...\n", | 88 | pr_debug("%s: req failed (CMD%u): %d, retrying...\n", |
76 | mmc_hostname(host), cmd->opcode, err); | 89 | mmc_hostname(host), cmd->opcode, err); |
@@ -453,8 +466,13 @@ static void mmc_power_up(struct mmc_host *host) | |||
453 | int bit = fls(host->ocr_avail) - 1; | 466 | int bit = fls(host->ocr_avail) - 1; |
454 | 467 | ||
455 | host->ios.vdd = bit; | 468 | host->ios.vdd = bit; |
456 | host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; | 469 | if (mmc_host_is_spi(host)) { |
457 | host->ios.chip_select = MMC_CS_DONTCARE; | 470 | host->ios.chip_select = MMC_CS_HIGH; |
471 | host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; | ||
472 | } else { | ||
473 | host->ios.chip_select = MMC_CS_DONTCARE; | ||
474 | host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; | ||
475 | } | ||
458 | host->ios.power_mode = MMC_POWER_UP; | 476 | host->ios.power_mode = MMC_POWER_UP; |
459 | host->ios.bus_width = MMC_BUS_WIDTH_1; | 477 | host->ios.bus_width = MMC_BUS_WIDTH_1; |
460 | host->ios.timing = MMC_TIMING_LEGACY; | 478 | host->ios.timing = MMC_TIMING_LEGACY; |
@@ -481,8 +499,10 @@ static void mmc_power_off(struct mmc_host *host) | |||
481 | { | 499 | { |
482 | host->ios.clock = 0; | 500 | host->ios.clock = 0; |
483 | host->ios.vdd = 0; | 501 | host->ios.vdd = 0; |
484 | host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; | 502 | if (!mmc_host_is_spi(host)) { |
485 | host->ios.chip_select = MMC_CS_DONTCARE; | 503 | host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; |
504 | host->ios.chip_select = MMC_CS_DONTCARE; | ||
505 | } | ||
486 | host->ios.power_mode = MMC_POWER_OFF; | 506 | host->ios.power_mode = MMC_POWER_OFF; |
487 | host->ios.bus_width = MMC_BUS_WIDTH_1; | 507 | host->ios.bus_width = MMC_BUS_WIDTH_1; |
488 | host->ios.timing = MMC_TIMING_LEGACY; | 508 | host->ios.timing = MMC_TIMING_LEGACY; |