aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAndrei Warkentin <andreiw@motorola.com>2011-05-23 16:06:37 -0400
committerChris Ball <cjb@laptop.org>2011-05-25 16:49:00 -0400
commite89d456fcdde2df008c032bf928e69e628e07a28 (patch)
treeecc1942bea556086e015bdb6a6a548ad6e3a8957 /drivers
parentd0c97cfb81ebc5b416c0f92fa2fc18d2773e3023 (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.c63
-rw-r--r--drivers/mmc/host/sdhci.h2
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
840static void sdhci_set_transfer_mode(struct sdhci_host *host, 840static 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
1016static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) 1036static 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