Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ded703d6 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "mmc: card: block: Add support for completing cmdq requests with error"

parents ea58c08a 2c7676c7
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -3544,7 +3544,7 @@ void mmc_blk_cmdq_complete_rq(struct request *rq)
	else if (mrq->data && mrq->data->error)
		err = mrq->data->error;

	if (err || cmdq_req->resp_err) {
	if ((err || cmdq_req->resp_err) && !cmdq_req->skip_err_handling) {
		pr_err("%s: %s: txfr error(%d)/resp_err(%d)\n",
				mmc_hostname(mrq->host), __func__, err,
				cmdq_req->resp_err);
@@ -3581,6 +3581,17 @@ void mmc_blk_cmdq_complete_rq(struct request *rq)
		blk_end_request_all(rq, err);
		goto out;
	}
	/*
	 * In case of error, cmdq_req->data.bytes_xfered is set to 0.
	 * If we call blk_end_request() with nr_bytes as 0 then the request
	 * never gets completed. So in case of error, to complete a request
	 * with error we should use blk_end_request_all().
	 */
	if (err && cmdq_req->skip_err_handling) {
		cmdq_req->skip_err_handling = false;
		blk_end_request_all(rq, err);
		goto out;
	}

	blk_end_request(rq, err, cmdq_req->data.bytes_xfered);

+86 −2
Original line number Diff line number Diff line
@@ -145,6 +145,29 @@ static void cmdq_clear_set_irqs(struct cmdq_host *cq_host, u32 clear, u32 set)
	mb();
}

static int cmdq_clear_task_poll(struct cmdq_host *cq_host, unsigned int tag)
{
	int retries = 100;

	cmdq_clear_set_irqs(cq_host, CQIS_TCL, 0);
	cmdq_writel(cq_host, 1<<tag, CQTCLR);
	while (retries) {
		/*
		 * Task Clear register and doorbell,
		 * both should indicate that task is cleared
		 */
		if ((cmdq_readl(cq_host, CQTCLR) & 1<<tag) ||
			(cmdq_readl(cq_host, CQTDBR) & 1<<tag)) {
			udelay(5);
			retries--;
			continue;
		} else
			break;
	}

	cmdq_clear_set_irqs(cq_host, 0, CQIS_TCL);
	return retries ? 0 : -ETIMEDOUT;
}

#define DRV_NAME "cmdq-host"

@@ -857,6 +880,8 @@ irqreturn_t cmdq_irq(struct mmc_host *mmc, int err)
	struct mmc_request *mrq;
	int ret;
	u32 dbr_set = 0;
	u32 dev_pend_set = 0;
	int stat_err = 0;

	status = cmdq_readl(cq_host, CQIS);

@@ -865,7 +890,9 @@ irqreturn_t cmdq_irq(struct mmc_host *mmc, int err)
	MMC_TRACE(mmc, "%s: CQIS: 0x%x err: %d\n",
		__func__, status, err);

	if (err || (status & CQIS_RED)) {
	stat_err = status & (CQIS_RED | CQIS_GCE | CQIS_ICCE);

	if (err || stat_err) {
		err_info = cmdq_readl(cq_host, CQTERRI);
		pr_err("%s: err: %d status: 0x%08x task-err-info (0x%08lx)\n",
		       mmc_hostname(mmc), err, status, err_info);
@@ -968,7 +995,7 @@ skip_cqterri:
		 * CQE detected a reponse error from device
		 * In most cases, this would require a reset.
		 */
		if (status & CQIS_RED) {
		if (stat_err & CQIS_RED) {
			/*
			 * will check if the RED error is due to a bkops
			 * exception once the queue is empty
@@ -987,6 +1014,62 @@ skip_cqterri:
			mrq->cmdq_req->resp_arg = cmdq_readl(cq_host, CQCRA);
		}

		/*
		 * Generic Crypto error detected by CQE.
		 * Its a fatal, would require cmdq reset.
		 */
		if (stat_err & CQIS_GCE) {
			if (mrq->data)
				mrq->data->error = -EIO;
			pr_err("%s: Crypto generic error while processing task %lu!",
				mmc_hostname(mmc), tag);
			MMC_TRACE(mmc, "%s: GCE error detected with tag %lu\n",
					__func__, tag);
		}
		/*
		 * Invalid crypto config error detected by CQE, clear the task.
		 * Task can be cleared only when CQE is halt state.
		 */
		if (stat_err & CQIS_ICCE) {
			/*
			 * Invalid Crypto Config Error is detected at the
			 * beginning of the transfer before the actual execution
			 * started. So just clear the task in CQE. No need to
			 * clear in device. Only the task which caused ICCE has
			 * to be cleared. Other tasks can be continue processing
			 * The first task which is about to be prepared would
			 * cause ICCE Error.
			 */
			dbr_set = cmdq_readl(cq_host, CQTDBR);
			dev_pend_set = cmdq_readl(cq_host, CQDPT);
			if (dbr_set ^ dev_pend_set)
				tag = ffs(dbr_set ^ dev_pend_set) - 1;
			mrq = get_req_by_tag(cq_host, tag);
			pr_err("%s: Crypto config error while processing task %lu!",
				mmc_hostname(mmc), tag);
			MMC_TRACE(mmc, "%s: ICCE error with tag %lu\n",
						__func__, tag);
			if (mrq->data)
				mrq->data->error = -EIO;
			else if (mrq->cmd)
				mrq->cmd->error = -EIO;
			/*
			 * If CQE is halted and tag is valid then clear the task
			 * then un-halt CQE and set flag to skip error recovery.
			 * If any of the condtions is not met thene it will
			 * enter into default error recovery path.
			 */
			if (!ret && (dbr_set ^ dev_pend_set)) {
				ret = cmdq_clear_task_poll(cq_host, tag);
				if (ret) {
					pr_err("%s: %s: task[%lu] clear failed ret=%d\n",
						mmc_hostname(mmc),
						__func__, tag, ret);
				} else if (!cmdq_halt_poll(mmc, false)) {
					mrq->cmdq_req->skip_err_handling = true;
				}
			}
		}
		cmdq_finish_data(mmc, tag);
	} else {
		cmdq_writel(cq_host, status, CQIS);
@@ -1052,6 +1135,7 @@ static int cmdq_halt_poll(struct mmc_host *mmc, bool halt)
			cq_host->ops->clear_set_irqs(mmc, true);
		cmdq_writel(cq_host, cmdq_readl(cq_host, CQCTL) & ~HALT,
			    CQCTL);
		mmc_host_clr_halt(mmc);
		return 0;
	}

+3 −1
Original line number Diff line number Diff line
@@ -37,6 +37,8 @@
#define CQIS_TCC	(1 << 1)
#define CQIS_RED	(1 << 2)
#define CQIS_TCL	(1 << 3)
#define CQIS_GCE	(1 << 4)
#define CQIS_ICCE	(1 << 5)

/* interrupt status enable */
#define CQISTE		0x14
@@ -112,7 +114,7 @@
/* command response argument */
#define CQCRA		0x5C

#define CQ_INT_ALL	0xF
#define CQ_INT_ALL	0x3F
#define CQIC_DEFAULT_ICCTH 31
#define CQIC_DEFAULT_ICTOVAL 1

+1 −0
Original line number Diff line number Diff line
@@ -208,6 +208,7 @@ struct mmc_cmdq_req {
	unsigned int		resp_arg;
	unsigned int		dev_pend_tasks;
	bool			resp_err;
	bool			skip_err_handling;
	int			tag; /* used for command queuing */
	u8			ctx_id;
};