diff options
| -rw-r--r-- | drivers/mmc/host/dw_mmc.c | 84 | ||||
| -rw-r--r-- | include/linux/mmc/dw_mmc.h | 2 |
2 files changed, 66 insertions, 20 deletions
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 649127e71894..b4328ad59cf0 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c | |||
| @@ -29,6 +29,7 @@ | |||
| 29 | #include <linux/irq.h> | 29 | #include <linux/irq.h> |
| 30 | #include <linux/mmc/host.h> | 30 | #include <linux/mmc/host.h> |
| 31 | #include <linux/mmc/mmc.h> | 31 | #include <linux/mmc/mmc.h> |
| 32 | #include <linux/mmc/sdio.h> | ||
| 32 | #include <linux/mmc/dw_mmc.h> | 33 | #include <linux/mmc/dw_mmc.h> |
| 33 | #include <linux/bitops.h> | 34 | #include <linux/bitops.h> |
| 34 | #include <linux/regulator/consumer.h> | 35 | #include <linux/regulator/consumer.h> |
| @@ -246,10 +247,15 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) | |||
| 246 | 247 | ||
| 247 | cmdr = cmd->opcode; | 248 | cmdr = cmd->opcode; |
| 248 | 249 | ||
| 249 | if (cmdr == MMC_STOP_TRANSMISSION) | 250 | if (cmd->opcode == MMC_STOP_TRANSMISSION || |
| 251 | cmd->opcode == MMC_GO_IDLE_STATE || | ||
| 252 | cmd->opcode == MMC_GO_INACTIVE_STATE || | ||
| 253 | (cmd->opcode == SD_IO_RW_DIRECT && | ||
| 254 | ((cmd->arg >> 9) & 0x1FFFF) == SDIO_CCCR_ABORT)) | ||
| 250 | cmdr |= SDMMC_CMD_STOP; | 255 | cmdr |= SDMMC_CMD_STOP; |
| 251 | else | 256 | else |
| 252 | cmdr |= SDMMC_CMD_PRV_DAT_WAIT; | 257 | if (cmd->opcode != MMC_SEND_STATUS && cmd->data) |
| 258 | cmdr |= SDMMC_CMD_PRV_DAT_WAIT; | ||
| 253 | 259 | ||
| 254 | if (cmd->flags & MMC_RSP_PRESENT) { | 260 | if (cmd->flags & MMC_RSP_PRESENT) { |
| 255 | /* We expect a response, so set this bit */ | 261 | /* We expect a response, so set this bit */ |
| @@ -276,6 +282,40 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) | |||
| 276 | return cmdr; | 282 | return cmdr; |
| 277 | } | 283 | } |
| 278 | 284 | ||
| 285 | static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd) | ||
| 286 | { | ||
| 287 | struct mmc_command *stop; | ||
| 288 | u32 cmdr; | ||
| 289 | |||
| 290 | if (!cmd->data) | ||
| 291 | return 0; | ||
| 292 | |||
| 293 | stop = &host->stop_abort; | ||
| 294 | cmdr = cmd->opcode; | ||
| 295 | memset(stop, 0, sizeof(struct mmc_command)); | ||
| 296 | |||
| 297 | if (cmdr == MMC_READ_SINGLE_BLOCK || | ||
| 298 | cmdr == MMC_READ_MULTIPLE_BLOCK || | ||
| 299 | cmdr == MMC_WRITE_BLOCK || | ||
| 300 | cmdr == MMC_WRITE_MULTIPLE_BLOCK) { | ||
| 301 | stop->opcode = MMC_STOP_TRANSMISSION; | ||
| 302 | stop->arg = 0; | ||
| 303 | stop->flags = MMC_RSP_R1B | MMC_CMD_AC; | ||
| 304 | } else if (cmdr == SD_IO_RW_EXTENDED) { | ||
| 305 | stop->opcode = SD_IO_RW_DIRECT; | ||
| 306 | stop->arg |= (1 << 31) | (0 << 28) | (SDIO_CCCR_ABORT << 9) | | ||
| 307 | ((cmd->arg >> 28) & 0x7); | ||
| 308 | stop->flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC; | ||
| 309 | } else { | ||
| 310 | return 0; | ||
| 311 | } | ||
| 312 | |||
| 313 | cmdr = stop->opcode | SDMMC_CMD_STOP | | ||
| 314 | SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_EXP; | ||
| 315 | |||
| 316 | return cmdr; | ||
| 317 | } | ||
| 318 | |||
| 279 | static void dw_mci_start_command(struct dw_mci *host, | 319 | static void dw_mci_start_command(struct dw_mci *host, |
| 280 | struct mmc_command *cmd, u32 cmd_flags) | 320 | struct mmc_command *cmd, u32 cmd_flags) |
| 281 | { | 321 | { |
| @@ -290,9 +330,10 @@ static void dw_mci_start_command(struct dw_mci *host, | |||
| 290 | mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START); | 330 | mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START); |
| 291 | } | 331 | } |
| 292 | 332 | ||
| 293 | static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data) | 333 | static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data) |
| 294 | { | 334 | { |
| 295 | dw_mci_start_command(host, data->stop, host->stop_cmdr); | 335 | struct mmc_command *stop = data->stop ? data->stop : &host->stop_abort; |
| 336 | dw_mci_start_command(host, stop, host->stop_cmdr); | ||
| 296 | } | 337 | } |
| 297 | 338 | ||
| 298 | /* DMA interface functions */ | 339 | /* DMA interface functions */ |
| @@ -828,6 +869,8 @@ static void __dw_mci_start_request(struct dw_mci *host, | |||
| 828 | 869 | ||
| 829 | if (mrq->stop) | 870 | if (mrq->stop) |
| 830 | host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop); | 871 | host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop); |
| 872 | else | ||
| 873 | host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd); | ||
| 831 | } | 874 | } |
| 832 | 875 | ||
| 833 | static void dw_mci_start_request(struct dw_mci *host, | 876 | static void dw_mci_start_request(struct dw_mci *host, |
| @@ -1190,13 +1233,9 @@ static void dw_mci_tasklet_func(unsigned long priv) | |||
| 1190 | 1233 | ||
| 1191 | if (cmd->data && cmd->error) { | 1234 | if (cmd->data && cmd->error) { |
| 1192 | dw_mci_stop_dma(host); | 1235 | dw_mci_stop_dma(host); |
| 1193 | if (data->stop) { | 1236 | send_stop_abort(host, data); |
| 1194 | send_stop_cmd(host, data); | 1237 | state = STATE_SENDING_STOP; |
| 1195 | state = STATE_SENDING_STOP; | 1238 | break; |
| 1196 | break; | ||
| 1197 | } else { | ||
| 1198 | host->data = NULL; | ||
| 1199 | } | ||
| 1200 | } | 1239 | } |
| 1201 | 1240 | ||
| 1202 | if (!host->mrq->data || cmd->error) { | 1241 | if (!host->mrq->data || cmd->error) { |
| @@ -1211,8 +1250,7 @@ static void dw_mci_tasklet_func(unsigned long priv) | |||
| 1211 | if (test_and_clear_bit(EVENT_DATA_ERROR, | 1250 | if (test_and_clear_bit(EVENT_DATA_ERROR, |
| 1212 | &host->pending_events)) { | 1251 | &host->pending_events)) { |
| 1213 | dw_mci_stop_dma(host); | 1252 | dw_mci_stop_dma(host); |
| 1214 | if (data->stop) | 1253 | send_stop_abort(host, data); |
| 1215 | send_stop_cmd(host, data); | ||
| 1216 | state = STATE_DATA_ERROR; | 1254 | state = STATE_DATA_ERROR; |
| 1217 | break; | 1255 | break; |
| 1218 | } | 1256 | } |
| @@ -1272,7 +1310,7 @@ static void dw_mci_tasklet_func(unsigned long priv) | |||
| 1272 | data->error = 0; | 1310 | data->error = 0; |
| 1273 | } | 1311 | } |
| 1274 | 1312 | ||
| 1275 | if (!data->stop) { | 1313 | if (!data->stop && !data->error) { |
| 1276 | dw_mci_request_end(host, host->mrq); | 1314 | dw_mci_request_end(host, host->mrq); |
| 1277 | goto unlock; | 1315 | goto unlock; |
| 1278 | } | 1316 | } |
| @@ -1284,8 +1322,10 @@ static void dw_mci_tasklet_func(unsigned long priv) | |||
| 1284 | } | 1322 | } |
| 1285 | 1323 | ||
| 1286 | prev_state = state = STATE_SENDING_STOP; | 1324 | prev_state = state = STATE_SENDING_STOP; |
| 1287 | if (!data->error) | 1325 | if (data->stop && !data->error) { |
| 1288 | send_stop_cmd(host, data); | 1326 | /* stop command for open-ended transfer*/ |
| 1327 | send_stop_abort(host, data); | ||
| 1328 | } | ||
| 1289 | /* fall through */ | 1329 | /* fall through */ |
| 1290 | 1330 | ||
| 1291 | case STATE_SENDING_STOP: | 1331 | case STATE_SENDING_STOP: |
| @@ -1304,7 +1344,12 @@ static void dw_mci_tasklet_func(unsigned long priv) | |||
| 1304 | 1344 | ||
| 1305 | host->cmd = NULL; | 1345 | host->cmd = NULL; |
| 1306 | host->data = NULL; | 1346 | host->data = NULL; |
| 1307 | dw_mci_command_complete(host, host->mrq->stop); | 1347 | |
| 1348 | if (host->mrq->stop) | ||
| 1349 | dw_mci_command_complete(host, host->mrq->stop); | ||
| 1350 | else | ||
| 1351 | host->cmd_status = 0; | ||
| 1352 | |||
| 1308 | dw_mci_request_end(host, host->mrq); | 1353 | dw_mci_request_end(host, host->mrq); |
| 1309 | goto unlock; | 1354 | goto unlock; |
| 1310 | 1355 | ||
| @@ -1888,11 +1933,10 @@ static void dw_mci_work_routine_card(struct work_struct *work) | |||
| 1888 | case STATE_DATA_ERROR: | 1933 | case STATE_DATA_ERROR: |
| 1889 | if (mrq->data->error == -EINPROGRESS) | 1934 | if (mrq->data->error == -EINPROGRESS) |
| 1890 | mrq->data->error = -ENOMEDIUM; | 1935 | mrq->data->error = -ENOMEDIUM; |
| 1891 | if (!mrq->stop) | ||
| 1892 | break; | ||
| 1893 | /* fall through */ | 1936 | /* fall through */ |
| 1894 | case STATE_SENDING_STOP: | 1937 | case STATE_SENDING_STOP: |
| 1895 | mrq->stop->error = -ENOMEDIUM; | 1938 | if (mrq->stop) |
| 1939 | mrq->stop->error = -ENOMEDIUM; | ||
| 1896 | break; | 1940 | break; |
| 1897 | } | 1941 | } |
| 1898 | 1942 | ||
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index a829f7ee28c8..6ce7d2cd3c7a 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #define LINUX_MMC_DW_MMC_H | 15 | #define LINUX_MMC_DW_MMC_H |
| 16 | 16 | ||
| 17 | #include <linux/scatterlist.h> | 17 | #include <linux/scatterlist.h> |
| 18 | #include <linux/mmc/core.h> | ||
| 18 | 19 | ||
| 19 | #define MAX_MCI_SLOTS 2 | 20 | #define MAX_MCI_SLOTS 2 |
| 20 | 21 | ||
| @@ -129,6 +130,7 @@ struct dw_mci { | |||
| 129 | struct mmc_request *mrq; | 130 | struct mmc_request *mrq; |
| 130 | struct mmc_command *cmd; | 131 | struct mmc_command *cmd; |
| 131 | struct mmc_data *data; | 132 | struct mmc_data *data; |
| 133 | struct mmc_command stop_abort; | ||
| 132 | unsigned int prev_blksz; | 134 | unsigned int prev_blksz; |
| 133 | unsigned char timing; | 135 | unsigned char timing; |
| 134 | struct workqueue_struct *card_workqueue; | 136 | struct workqueue_struct *card_workqueue; |
