|
@@ -1761,6 +1761,33 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
|
|
}
|
|
|
|
|
|
if (cmd->data && err) {
|
|
|
+ /*
|
|
|
+ * During UHS tuning sequence, sending the stop
|
|
|
+ * command after the response CRC error would
|
|
|
+ * throw the system into a confused state
|
|
|
+ * causing all future tuning phases to report
|
|
|
+ * failure.
|
|
|
+ *
|
|
|
+ * In such case controller will move into a data
|
|
|
+ * transfer state after a response error or
|
|
|
+ * response CRC error. Let's let that finish
|
|
|
+ * before trying to send a stop, so we'll go to
|
|
|
+ * STATE_SENDING_DATA.
|
|
|
+ *
|
|
|
+ * Although letting the data transfer take place
|
|
|
+ * will waste a bit of time (we already know
|
|
|
+ * the command was bad), it can't cause any
|
|
|
+ * errors since it's possible it would have
|
|
|
+ * taken place anyway if this tasklet got
|
|
|
+ * delayed. Allowing the transfer to take place
|
|
|
+ * avoids races and keeps things simple.
|
|
|
+ */
|
|
|
+ if ((err != -ETIMEDOUT) &&
|
|
|
+ (cmd->opcode == MMC_SEND_TUNING_BLOCK)) {
|
|
|
+ state = STATE_SENDING_DATA;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
dw_mci_stop_dma(host);
|
|
|
send_stop_abort(host, data);
|
|
|
state = STATE_SENDING_STOP;
|