Commit e89d456f authored by Andrei Warkentin's avatar Andrei Warkentin Committed by Chris Ball

mmc: sdhci: Implement MMC_CAP_CMD23 for SDHCI.

Implements support for multiblock transfers bounded
by SET_BLOCK_COUNT (CMD23).
Signed-off-by: default avatarAndrei Warkentin <andreiw@motorola.com>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent d0c97cfb
...@@ -838,9 +838,10 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) ...@@ -838,9 +838,10 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
} }
static void sdhci_set_transfer_mode(struct sdhci_host *host, static void sdhci_set_transfer_mode(struct sdhci_host *host,
struct mmc_data *data) struct mmc_command *cmd)
{ {
u16 mode; u16 mode;
struct mmc_data *data = cmd->data;
if (data == NULL) if (data == NULL)
return; return;
...@@ -848,11 +849,14 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, ...@@ -848,11 +849,14 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
WARN_ON(!host->data); WARN_ON(!host->data);
mode = SDHCI_TRNS_BLK_CNT_EN; mode = SDHCI_TRNS_BLK_CNT_EN;
if (data->blocks > 1) { if (mmc_op_multi(cmd->opcode) || data->blocks > 1) {
if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
else
mode |= SDHCI_TRNS_MULTI; mode |= SDHCI_TRNS_MULTI;
/*
* If we are sending CMD23, CMD12 never gets sent
* on successful completion (so no Auto-CMD12).
*/
if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12))
mode |= SDHCI_TRNS_AUTO_CMD12;
} }
if (data->flags & MMC_DATA_READ) if (data->flags & MMC_DATA_READ)
mode |= SDHCI_TRNS_READ; mode |= SDHCI_TRNS_READ;
...@@ -893,7 +897,15 @@ static void sdhci_finish_data(struct sdhci_host *host) ...@@ -893,7 +897,15 @@ static void sdhci_finish_data(struct sdhci_host *host)
else else
data->bytes_xfered = data->blksz * data->blocks; data->bytes_xfered = data->blksz * data->blocks;
if (data->stop) { /*
* Need to send CMD12 if -
* a) open-ended multiblock transfer (no CMD23)
* b) error in multiblock transfer
*/
if (data->stop &&
(data->error ||
!host->mrq->sbc)) {
/* /*
* The controller needs a reset of internal state machines * The controller needs a reset of internal state machines
* upon error conditions. * upon error conditions.
...@@ -949,7 +961,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) ...@@ -949,7 +961,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT); sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
sdhci_set_transfer_mode(host, cmd->data); sdhci_set_transfer_mode(host, cmd);
if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) { if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
printk(KERN_ERR "%s: Unsupported response type!\n", printk(KERN_ERR "%s: Unsupported response type!\n",
...@@ -1004,6 +1016,13 @@ static void sdhci_finish_command(struct sdhci_host *host) ...@@ -1004,6 +1016,13 @@ static void sdhci_finish_command(struct sdhci_host *host)
host->cmd->error = 0; host->cmd->error = 0;
/* Finished CMD23, now send actual command. */
if (host->cmd == host->mrq->sbc) {
host->cmd = NULL;
sdhci_send_command(host, host->mrq->cmd);
} else {
/* Processed actual command. */
if (host->data && host->data_early) if (host->data && host->data_early)
sdhci_finish_data(host); sdhci_finish_data(host);
...@@ -1011,6 +1030,7 @@ static void sdhci_finish_command(struct sdhci_host *host) ...@@ -1011,6 +1030,7 @@ static void sdhci_finish_command(struct sdhci_host *host)
tasklet_schedule(&host->finish_tasklet); tasklet_schedule(&host->finish_tasklet);
host->cmd = NULL; host->cmd = NULL;
}
} }
static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
...@@ -1189,7 +1209,12 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) ...@@ -1189,7 +1209,12 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
#ifndef SDHCI_USE_LEDS_CLASS #ifndef SDHCI_USE_LEDS_CLASS
sdhci_activate_led(host); sdhci_activate_led(host);
#endif #endif
if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) {
/*
* Ensure we don't send the STOP for non-SET_BLOCK_COUNTED
* requests if Auto-CMD12 is enabled.
*/
if (!mrq->sbc && (host->flags & SDHCI_AUTO_CMD12)) {
if (mrq->stop) { if (mrq->stop) {
mrq->data->stop = NULL; mrq->data->stop = NULL;
mrq->stop = NULL; mrq->stop = NULL;
...@@ -1227,6 +1252,9 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) ...@@ -1227,6 +1252,9 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
host->mrq = mrq; host->mrq = mrq;
} }
if (mrq->sbc)
sdhci_send_command(host, mrq->sbc);
else
sdhci_send_command(host, mrq->cmd); sdhci_send_command(host, mrq->cmd);
} }
...@@ -2455,7 +2483,10 @@ int sdhci_add_host(struct sdhci_host *host) ...@@ -2455,7 +2483,10 @@ int sdhci_add_host(struct sdhci_host *host)
} else } else
mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200; mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200;
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE; mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
host->flags |= SDHCI_AUTO_CMD12;
/* /*
* A controller may support 8-bit width, but the board itself * A controller may support 8-bit width, but the board itself
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
#define SDHCI_TRANSFER_MODE 0x0C #define SDHCI_TRANSFER_MODE 0x0C
#define SDHCI_TRNS_DMA 0x01 #define SDHCI_TRNS_DMA 0x01
#define SDHCI_TRNS_BLK_CNT_EN 0x02 #define SDHCI_TRNS_BLK_CNT_EN 0x02
#define SDHCI_TRNS_ACMD12 0x04 #define SDHCI_TRNS_AUTO_CMD12 0x04
#define SDHCI_TRNS_READ 0x10 #define SDHCI_TRNS_READ 0x10
#define SDHCI_TRNS_MULTI 0x20 #define SDHCI_TRNS_MULTI 0x20
......
...@@ -113,6 +113,7 @@ struct sdhci_host { ...@@ -113,6 +113,7 @@ struct sdhci_host {
#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */ #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */ #define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
#define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */ #define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */
#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */
unsigned int version; /* SDHCI spec. version */ unsigned int version; /* SDHCI spec. version */
......
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