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 | |
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')
-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 | ||