This commit comes from community but not upstreram.
We cannot wait for picking up such a great movement without
upstream admit it. We just take a light way to merge it and
minimize the change.
Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
Signed-off-by: xiaoyao <xiaoyao@rock-chips.com>
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
mmc_active);
struct mmc_blk_request *brq = &mq_mrq->brq;
struct request *req = mq_mrq->req;
+ int need_retune = card->host->need_retune;
int ecc_err = 0, gen_err = 0;
/*
}
if (brq->data.error) {
+ pr_err("need_retune:%d,brq->retune_retry_done:%d.\n",need_retune,brq->retune_retry_done);
+ if (need_retune && !brq->retune_retry_done) {
+ pr_err("%s: retrying because a re-tune was needed\n",
+ req->rq_disk->disk_name);
+ brq->retune_retry_done = 1;
+ return MMC_BLK_RETRY;
+ }
+
pr_err("%s: error %d transferring data, sector %u, nr %u, cmd response %#x, card status %#x\n",
req->rq_disk->disk_name, brq->data.error,
(unsigned)blk_rq_pos(req),
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
struct mmc_blk_request *brq = &mq->mqrq_cur->brq;
- int ret = 1, disable_multi = 0, retry = 0, type;
+ int ret = 1, disable_multi = 0, retry = 0, retune_retry_done = 0, type;
enum mmc_blk_status status;
struct mmc_queue_req *mq_rq;
struct request *req = rqc;
break;
goto cmd_abort;
case MMC_BLK_RETRY:
+ retune_retry_done = brq->retune_retry_done;
if (retry++ < 5)
break;
/* Fall through */
mmc_start_req(card->host,
&mq_rq->mmc_active, NULL);
}
+ mq_rq->brq.retune_retry_done = retune_retry_done;
}
} while (ret);
struct mmc_command cmd;
struct mmc_command stop;
struct mmc_data data;
+ int retune_retry_done;
};
enum mmc_packed_type {
}
EXPORT_SYMBOL(mmc_request_done);
+static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
+{
+ int err;
+
+ /* Assumes host controller has been runtime resumed by mmc_claim_host */
+ err = mmc_retune(host);
+ if (err) {
+ mrq->cmd->error = err;
+ mmc_request_done(host, mrq);
+ return;
+ }
+
+ host->ops->request(host, mrq);
+}
+
static void
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
unsigned int i, sz;
struct scatterlist *sg;
#endif
+ mmc_retune_hold(host);
if (mrq->sbc) {
pr_debug("<%s: starting CMD%u arg %08x flags %08x>\n",
}
mmc_host_clk_hold(host);
led_trigger_event(host->led, LED_FULL);
- host->ops->request(host, mrq);
+ __mmc_start_request(host, mrq);
}
/**
timeout = MMC_BKOPS_MAX_TIMEOUT;
use_busy_signal = true;
} else {
+ /* Hold re-tuning for ongoing bkops */
+ mmc_retune_hold(card->host);
timeout = 0;
use_busy_signal = false;
}
if (err) {
pr_warn("%s: Error %d starting bkops\n",
mmc_hostname(card->host), err);
+ /* bkops not ongoing, so release re-tuning */
+ if (!use_busy_signal)
+ mmc_retune_release(card->host);
goto out;
}
host->areq);
break; /* return err */
} else {
+ mmc_retune_recheck(host);
pr_info("%s: req failed (CMD%u): %d, retrying...\n",
mmc_hostname(host),
cmd->opcode, cmd->error);
cmd->retries--;
cmd->error = 0;
- host->ops->request(host, mrq);
+ __mmc_start_request(host, mrq);
continue; /* wait for done/new event again */
}
} else if (context_info->is_new_req) {
context_info->is_new_req = false;
- if (!next_req) {
- err = MMC_BLK_NEW_REQUEST;
- break; /* return err */
- }
+ if (!next_req)
+ return MMC_BLK_NEW_REQUEST;
}
}
+ mmc_retune_release(host);
return err;
}
mmc_hostname(host), cmd->opcode, cmd->error);
cmd->retries--;
cmd->error = 0;
- host->ops->request(host, mrq);
+ __mmc_start_request(host, mrq);
}
+ mmc_retune_release(host);
}
/**
*/
if (!err || (err == -EINVAL)) {
mmc_card_clr_doing_bkops(card);
+ mmc_retune_release(card->host);
err = 0;
}
}
#endif
+int mmc_execute_tuning(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+ u32 opcode;
+ int err;
+
+ if (!host->ops->execute_tuning)
+ return 0;
+
+ if (mmc_card_mmc(card))
+ opcode = MMC_SEND_TUNING_BLOCK_HS200;
+ else
+ opcode = MMC_SEND_TUNING_BLOCK;
+
+ mmc_host_clk_hold(host);
+ err = host->ops->execute_tuning(host, opcode);
+ mmc_host_clk_release(host);
+
+ if (err)
+ pr_err("%s: tuning execution failed\n", mmc_hostname(host));
+ else
+ mmc_retune_enable(host);
+
+ return err;
+}
+
/*
* Change the bus mode (open drain/push-pull) of a host.
*/
return;
mmc_host_clk_hold(host);
+ mmc_retune_disable(host);
host->ios.vdd = fls(ocr) - 1;
if (mmc_host_is_spi(host))
return;
mmc_host_clk_hold(host);
+ mmc_retune_disable(host);
host->ios.clock = 0;
host->ios.vdd = 0;
unsigned int fr, nr;
int err;
+ mmc_retune_hold(card->host);
+
fr = from;
nr = to - from + 1;
} while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
(R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG));
out:
-
+ mmc_retune_release(card->host);
return err;
}
void mmc_remove_card_debugfs(struct mmc_card *card);
void mmc_init_context_info(struct mmc_host *host);
+int mmc_execute_tuning(struct mmc_card *card);
#endif
#endif
+void mmc_retune_enable(struct mmc_host *host)
+{
+ host->can_retune = 1;
+}
+
+void mmc_retune_disable(struct mmc_host *host)
+{
+ host->can_retune = 0;
+ host->need_retune = 0;
+}
+
+void mmc_retune_hold(struct mmc_host *host)
+{
+ if (!host->hold_retune)
+ host->retune_now = 1;
+ host->hold_retune += 1;
+}
+EXPORT_SYMBOL(mmc_retune_hold);
+
+void mmc_retune_release(struct mmc_host *host)
+{
+ if (host->hold_retune)
+ host->hold_retune -= 1;
+ else
+ WARN_ON(1);
+}
+EXPORT_SYMBOL(mmc_retune_release);
+
+int mmc_retune(struct mmc_host *host)
+{
+ int err;
+
+ if (!host->need_retune || host->doing_retune || !host->card)
+ return 0;
+
+ host->need_retune = 0;
+
+ host->doing_retune = 1;
+
+ err = mmc_execute_tuning(host->card);
+
+ host->doing_retune = 0;
+
+ return err;
+}
+EXPORT_SYMBOL(mmc_retune);
+
/**
* mmc_of_parse() - parse host's device-tree node
* @host: host whose node should be parsed.
* 3. set the clock to > 52Mhz <=200MHz and
* 4. execute tuning for HS200
*/
+ /*
if ((host->caps2 & MMC_CAP2_HS200) &&
card->host->ops->execute_tuning) {
mmc_host_clk_hold(card->host);
mmc_hostname(card->host));
goto err;
}
+ */
+ if (host->caps2 & MMC_CAP2_HS200)
+ mmc_execute_tuning(card);
ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4;
return err;
}
+int mmc_switch_status_error(struct mmc_host *host, u32 status)
+{
+ if (mmc_host_is_spi(host)) {
+ if (status & R1_SPI_ILLEGAL_COMMAND)
+ return -EBADMSG;
+ } else {
+ if (status & 0xFDFFA000)
+ pr_warn("%s: unexpected status %#x after switch\n",
+ mmc_hostname(host), status);
+ if (status & R1_SWITCH_ERROR)
+ return -EBADMSG;
+ }
+ return 0;
+}
+
/**
* __mmc_switch - modify EXT_CSD register
* @card: the MMC card associated with the data transfer
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms, bool use_busy_signal, bool send_status)
{
+ struct mmc_host *host = card->host;
int err;
struct mmc_command cmd = {0};
unsigned long timeout;
BUG_ON(!card);
BUG_ON(!card->host);
+ mmc_retune_hold(host);
+
cmd.opcode = MMC_SWITCH;
cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
(index << 16) |
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err)
- return err;
+ goto out;
/* No need to check card status in case of unblocking command */
if (!use_busy_signal)
- return 0;
+ goto out;
/*
* Must check status to be sure of no errors
if (send_status) {
err = __mmc_send_status(card, &status, ignore_crc);
if (err)
- return err;
+ goto out;
}
if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
break;
*/
if (!send_status) {
mmc_delay(timeout_ms);
- return 0;
+ goto out;
}
/* Timeout if the device never leaves the program state. */
if (time_after(jiffies, timeout)) {
pr_err("%s: Card stuck in programming state! %s\n",
mmc_hostname(card->host), __func__);
- return -ETIMEDOUT;
+ err = -ETIMEDOUT;
+ goto out;
}
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
- if (mmc_host_is_spi(card->host)) {
- if (status & R1_SPI_ILLEGAL_COMMAND)
- return -EBADMSG;
- } else {
- if (status & 0xFDFFA000)
- pr_warning("%s: unexpected status %#x after "
- "switch", mmc_hostname(card->host), status);
- if (status & R1_SWITCH_ERROR)
- return -EBADMSG;
- }
-
- return 0;
+ err = mmc_switch_status_error(host, status);
+out:
+ mmc_retune_release(host);
+ return err;
}
EXPORT_SYMBOL_GPL(__mmc_switch);
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
-
+int mmc_switch_status_error(struct mmc_host *host, u32 status);
#endif
mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
host->data_status = pending;
smp_wmb();
+ printk(KERN_ERR "[%s] Data transmission error !!!! MINTSTS: [0x%08x]\n",
+ mmc_hostname(host->mmc), pending);
+ mmc_retune_needed(host->mmc);
set_bit(EVENT_DATA_ERROR, &host->pending_events);
MMC_DBG_INFO_FUNC(host->mmc,
int rescan_disable; /* disable card detection */
int rescan_entered; /* used with nonremovable devices */
+
+ unsigned int can_retune:1; /* re-tuning can be used */
+ unsigned int doing_retune:1; /* re-tuning in progress */
+ unsigned int retune_now:1; /* do re-tuning at next req */
+ int need_retune; /* re-tuning is needed */
+ int hold_retune; /* hold off re-tuning */
+
struct mmc_card *card; /* device attached to this host */
wait_queue_head_t wq;
return host->ios.clock;
}
#endif
+
+void mmc_retune_enable(struct mmc_host *host);
+void mmc_retune_disable(struct mmc_host *host);
+void mmc_retune_hold(struct mmc_host *host);
+void mmc_retune_release(struct mmc_host *host);
+int mmc_retune(struct mmc_host *host);
+
+static inline void mmc_retune_needed(struct mmc_host *host)
+{
+ if ((host->can_retune) && (host->doing_retune == 0)) {
+ host->need_retune = 1;
+ pr_err("[%s] Data transmission error, need to "
+ "try retuning %d.\n", mmc_hostname(host),
+ host->need_retune);
+ } else {
+ pr_err("[%s] host was already tuning, Don't"
+ " need to retry tune again ignore %d.\n",
+ mmc_hostname(host), host->need_retune);
+ }
+}
+
+static inline void mmc_retune_not_needed(struct mmc_host *host)
+{
+ host->need_retune = 0;
+}
+
+static inline void mmc_retune_recheck(struct mmc_host *host)
+{
+ if (host->hold_retune <= 1)
+ host->retune_now = 1;
+}
+
#endif /* LINUX_MMC_HOST_H */