diff options
author | Seungwon Jeon <tgih.jun@samsung.com> | 2013-08-30 11:14:05 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2013-09-25 21:35:40 -0400 |
commit | 90c2143a8f6d0cd1dbae1ea32fcd1befb81e4b0d (patch) | |
tree | 719fb73b4dd807f0aa673207ed62cbd170d2628d /drivers/mmc | |
parent | 71abb1339436160b6ab0ae2f816a4046c7328f28 (diff) |
mmc: dw_mmc: guarantee stop-abort cmd in data errors
In error cases, DTO interrupt may or may not be generated depending
on remained data. Stop/Abort command ensures DTO generation for that
situation. Currently if 'stop' field of data is empty, there is no
stop/abort command. So, it could hang waiting DTO. This change
reinforces these cases.
Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
Tested-by: Alim Akhtar <alim.akhtar@samsung.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/host/dw_mmc.c | 84 |
1 files changed, 64 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 | ||