diff options
| author | Andrei Warkentin <andreiw@motorola.com> | 2011-05-23 16:06:37 -0400 |
|---|---|---|
| committer | Chris Ball <cjb@laptop.org> | 2011-05-25 16:49:00 -0400 |
| commit | e89d456fcdde2df008c032bf928e69e628e07a28 (patch) | |
| tree | ecc1942bea556086e015bdb6a6a548ad6e3a8957 /drivers/mmc | |
| parent | d0c97cfb81ebc5b416c0f92fa2fc18d2773e3023 (diff) | |
mmc: sdhci: Implement MMC_CAP_CMD23 for SDHCI.
Implements support for multiblock transfers bounded
by SET_BLOCK_COUNT (CMD23).
Signed-off-by: Andrei Warkentin <andreiw@motorola.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
| -rw-r--r-- | drivers/mmc/host/sdhci.c | 63 | ||||
| -rw-r--r-- | drivers/mmc/host/sdhci.h | 2 |
2 files changed, 48 insertions, 17 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index cc63f5ed2310..b9ed66307ac3 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c | |||
| @@ -838,9 +838,10 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) | |||
| 838 | } | 838 | } |
| 839 | 839 | ||
| 840 | static void sdhci_set_transfer_mode(struct sdhci_host *host, | 840 | static void sdhci_set_transfer_mode(struct sdhci_host *host, |
| 841 | struct mmc_data *data) | 841 | struct mmc_command *cmd) |
| 842 | { | 842 | { |
| 843 | u16 mode; | 843 | u16 mode; |
| 844 | struct mmc_data *data = cmd->data; | ||
| 844 | 845 | ||
| 845 | if (data == NULL) | 846 | if (data == NULL) |
| 846 | return; | 847 | return; |
| @@ -848,11 +849,14 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, | |||
| 848 | WARN_ON(!host->data); | 849 | WARN_ON(!host->data); |
| 849 | 850 | ||
| 850 | mode = SDHCI_TRNS_BLK_CNT_EN; | 851 | mode = SDHCI_TRNS_BLK_CNT_EN; |
| 851 | if (data->blocks > 1) { | 852 | if (mmc_op_multi(cmd->opcode) || data->blocks > 1) { |
| 852 | if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) | 853 | mode |= SDHCI_TRNS_MULTI; |
| 853 | mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12; | 854 | /* |
| 854 | else | 855 | * If we are sending CMD23, CMD12 never gets sent |
| 855 | mode |= SDHCI_TRNS_MULTI; | 856 | * on successful completion (so no Auto-CMD12). |
| 857 | */ | ||
| 858 | if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12)) | ||
| 859 | mode |= SDHCI_TRNS_AUTO_CMD12; | ||
| 856 | } | 860 | } |
| 857 | if (data->flags & MMC_DATA_READ) | 861 | if (data->flags & MMC_DATA_READ) |
| 858 | mode |= SDHCI_TRNS_READ; | 862 | mode |= SDHCI_TRNS_READ; |
| @@ -893,7 +897,15 @@ static void sdhci_finish_data(struct sdhci_host *host) | |||
| 893 | else | 897 | else |
| 894 | data->bytes_xfered = data->blksz * data->blocks; | 898 | data->bytes_xfered = data->blksz * data->blocks; |
| 895 | 899 | ||
| 896 | if (data->stop) { | 900 | /* |
| 901 | * Need to send CMD12 if - | ||
| 902 | * a) open-ended multiblock transfer (no CMD23) | ||
| 903 | * b) error in multiblock transfer | ||
| 904 | */ | ||
| 905 | if (data->stop && | ||
| 906 | (data->error || | ||
| 907 | !host->mrq->sbc)) { | ||
| 908 | |||
| 897 | /* | 909 | /* |
| 898 | * The controller needs a reset of internal state machines | 910 | * The controller needs a reset of internal state machines |
| 899 | * upon error conditions. | 911 | * upon error conditions. |
| @@ -949,7 +961,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) | |||
| 949 | 961 | ||
| 950 | sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT); | 962 | sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT); |
| 951 | 963 | ||
| 952 | sdhci_set_transfer_mode(host, cmd->data); | 964 | sdhci_set_transfer_mode(host, cmd); |
| 953 | 965 | ||
| 954 | if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) { | 966 | if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) { |
| 955 | printk(KERN_ERR "%s: Unsupported response type!\n", | 967 | printk(KERN_ERR "%s: Unsupported response type!\n", |
| @@ -1004,13 +1016,21 @@ static void sdhci_finish_command(struct sdhci_host *host) | |||
| 1004 | 1016 | ||
| 1005 | host->cmd->error = 0; | 1017 | host->cmd->error = 0; |
| 1006 | 1018 | ||
| 1007 | if (host->data && host->data_early) | 1019 | /* Finished CMD23, now send actual command. */ |
| 1008 | sdhci_finish_data(host); | 1020 | if (host->cmd == host->mrq->sbc) { |
| 1021 | host->cmd = NULL; | ||
| 1022 | sdhci_send_command(host, host->mrq->cmd); | ||
| 1023 | } else { | ||
| 1009 | 1024 | ||
| 1010 | if (!host->cmd->data) | 1025 | /* Processed actual command. */ |
| 1011 | tasklet_schedule(&host->finish_tasklet); | 1026 | if (host->data && host->data_early) |
| 1027 | sdhci_finish_data(host); | ||
| 1012 | 1028 | ||
| 1013 | host->cmd = NULL; | 1029 | if (!host->cmd->data) |
| 1030 | tasklet_schedule(&host->finish_tasklet); | ||
| 1031 | |||
| 1032 | host->cmd = NULL; | ||
| 1033 | } | ||
| 1014 | } | 1034 | } |
| 1015 | 1035 | ||
| 1016 | static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) | 1036 | 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 | #ifndef SDHCI_USE_LEDS_CLASS | 1209 | #ifndef SDHCI_USE_LEDS_CLASS |
| 1190 | sdhci_activate_led(host); | 1210 | sdhci_activate_led(host); |
| 1191 | #endif | 1211 | #endif |
| 1192 | if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) { | 1212 | |
| 1213 | /* | ||
| 1214 | * Ensure we don't send the STOP for non-SET_BLOCK_COUNTED | ||
| 1215 | * requests if Auto-CMD12 is enabled. | ||
| 1216 | */ | ||
| 1217 | if (!mrq->sbc && (host->flags & SDHCI_AUTO_CMD12)) { | ||
| 1193 | if (mrq->stop) { | 1218 | if (mrq->stop) { |
| 1194 | mrq->data->stop = NULL; | 1219 | mrq->data->stop = NULL; |
| 1195 | mrq->stop = NULL; | 1220 | mrq->stop = NULL; |
| @@ -1227,7 +1252,10 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) | |||
| 1227 | host->mrq = mrq; | 1252 | host->mrq = mrq; |
| 1228 | } | 1253 | } |
| 1229 | 1254 | ||
| 1230 | sdhci_send_command(host, mrq->cmd); | 1255 | if (mrq->sbc) |
| 1256 | sdhci_send_command(host, mrq->sbc); | ||
| 1257 | else | ||
| 1258 | sdhci_send_command(host, mrq->cmd); | ||
| 1231 | } | 1259 | } |
| 1232 | 1260 | ||
| 1233 | mmiowb(); | 1261 | mmiowb(); |
| @@ -2455,7 +2483,10 @@ int sdhci_add_host(struct sdhci_host *host) | |||
| 2455 | } else | 2483 | } else |
| 2456 | mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200; | 2484 | mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200; |
| 2457 | 2485 | ||
| 2458 | mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE; | 2486 | mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; |
| 2487 | |||
| 2488 | if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) | ||
| 2489 | host->flags |= SDHCI_AUTO_CMD12; | ||
| 2459 | 2490 | ||
| 2460 | /* | 2491 | /* |
| 2461 | * A controller may support 8-bit width, but the board itself | 2492 | * A controller may support 8-bit width, but the board itself |
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 7e28eec97f04..2c3fbc5a4c07 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h | |||
| @@ -36,7 +36,7 @@ | |||
| 36 | #define SDHCI_TRANSFER_MODE 0x0C | 36 | #define SDHCI_TRANSFER_MODE 0x0C |
| 37 | #define SDHCI_TRNS_DMA 0x01 | 37 | #define SDHCI_TRNS_DMA 0x01 |
| 38 | #define SDHCI_TRNS_BLK_CNT_EN 0x02 | 38 | #define SDHCI_TRNS_BLK_CNT_EN 0x02 |
| 39 | #define SDHCI_TRNS_ACMD12 0x04 | 39 | #define SDHCI_TRNS_AUTO_CMD12 0x04 |
| 40 | #define SDHCI_TRNS_READ 0x10 | 40 | #define SDHCI_TRNS_READ 0x10 |
| 41 | #define SDHCI_TRNS_MULTI 0x20 | 41 | #define SDHCI_TRNS_MULTI 0x20 |
| 42 | 42 | ||
