Commit ee157abe authored by Ludovic Barre's avatar Ludovic Barre Committed by Ulf Hansson
Browse files

mmc: mmci: add threaded irq to abort DPSM of non-functional state



The stm32_sdmmc variant has build-in support for datatimeout for R1B
requests. If a corresponding IRQ is raised, this triggers the DPSM to stay
busy and remains in a non-functional state. Only a reset can bring it back
to a functional state.

Because a reset must be issued from non-atomic context, let's defer this to
be managed from a threaded IRQ handler. Besides the reset, the threaded
handler also calls mmc_request_done(), to finally complete the request.

Signed-off-by: default avatarLudovic Barre <ludovic.barre@st.com>
Link: https://lore.kernel.org/r/20191211133934.16932-1-ludovic.Barre@st.com


[Ulf: A few minor updates to the changelog/comments]
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent d0052ad9
Loading
Loading
Loading
Loading
+40 −6
Original line number Diff line number Diff line
@@ -1321,6 +1321,7 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
	} else if (host->variant->busy_timeout && busy_resp &&
		   status & MCI_DATATIMEOUT) {
		cmd->error = -ETIMEDOUT;
		host->irq_action = IRQ_WAKE_THREAD;
	} else {
		cmd->resp[0] = readl(base + MMCIRESPONSE0);
		cmd->resp[1] = readl(base + MMCIRESPONSE1);
@@ -1339,7 +1340,10 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
				return;
			}
		}

		if (host->irq_action != IRQ_WAKE_THREAD)
			mmci_request_end(host, host->mrq);

	} else if (sbc) {
		mmci_start_command(host, host->mrq->cmd, 0);
	} else if (!host->variant->datactrl_first &&
@@ -1532,9 +1536,9 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
{
	struct mmci_host *host = dev_id;
	u32 status;
	int ret = 0;

	spin_lock(&host->lock);
	host->irq_action = IRQ_HANDLED;

	do {
		status = readl(host->base + MMCISTATUS);
@@ -1574,12 +1578,41 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
		if (host->variant->busy_detect_flag)
			status &= ~host->variant->busy_detect_flag;

		ret = 1;
	} while (status);

	spin_unlock(&host->lock);

	return IRQ_RETVAL(ret);
	return host->irq_action;
}

/*
 * mmci_irq_thread() - A threaded IRQ handler that manages a reset of the HW.
 *
 * A reset is needed for some variants, where a datatimeout for a R1B request
 * causes the DPSM to stay busy (non-functional).
 */
static irqreturn_t mmci_irq_thread(int irq, void *dev_id)
{
	struct mmci_host *host = dev_id;
	unsigned long flags;

	if (host->rst) {
		reset_control_assert(host->rst);
		udelay(2);
		reset_control_deassert(host->rst);
	}

	spin_lock_irqsave(&host->lock, flags);
	writel(host->clk_reg, host->base + MMCICLOCK);
	writel(host->pwr_reg, host->base + MMCIPOWER);
	writel(MCI_IRQENABLE | host->variant->start_err,
	       host->base + MMCIMASK0);

	host->irq_action = IRQ_HANDLED;
	mmci_request_end(host, host->mrq);
	spin_unlock_irqrestore(&host->lock, flags);

	return host->irq_action;
}

static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
@@ -2063,7 +2096,8 @@ static int mmci_probe(struct amba_device *dev,
			goto clk_disable;
	}

	ret = devm_request_irq(&dev->dev, dev->irq[0], mmci_irq, IRQF_SHARED,
	ret = devm_request_threaded_irq(&dev->dev, dev->irq[0], mmci_irq,
					mmci_irq_thread, IRQF_SHARED,
					DRIVER_NAME " (cmd)", host);
	if (ret)
		goto clk_disable;
+1 −0
Original line number Diff line number Diff line
@@ -411,6 +411,7 @@ struct mmci_host {

	struct timer_list	timer;
	unsigned int		oldstat;
	u32			irq_action;

	/* pio stuff */
	struct sg_mapping_iter	sg_miter;