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: Ludovic 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: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
parent
d0052ad90e
commit
ee157abebc
@ -1321,6 +1321,7 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
|
|||||||
} else if (host->variant->busy_timeout && busy_resp &&
|
} else if (host->variant->busy_timeout && busy_resp &&
|
||||||
status & MCI_DATATIMEOUT) {
|
status & MCI_DATATIMEOUT) {
|
||||||
cmd->error = -ETIMEDOUT;
|
cmd->error = -ETIMEDOUT;
|
||||||
|
host->irq_action = IRQ_WAKE_THREAD;
|
||||||
} else {
|
} else {
|
||||||
cmd->resp[0] = readl(base + MMCIRESPONSE0);
|
cmd->resp[0] = readl(base + MMCIRESPONSE0);
|
||||||
cmd->resp[1] = readl(base + MMCIRESPONSE1);
|
cmd->resp[1] = readl(base + MMCIRESPONSE1);
|
||||||
@ -1339,7 +1340,10 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mmci_request_end(host, host->mrq);
|
|
||||||
|
if (host->irq_action != IRQ_WAKE_THREAD)
|
||||||
|
mmci_request_end(host, host->mrq);
|
||||||
|
|
||||||
} else if (sbc) {
|
} else if (sbc) {
|
||||||
mmci_start_command(host, host->mrq->cmd, 0);
|
mmci_start_command(host, host->mrq->cmd, 0);
|
||||||
} else if (!host->variant->datactrl_first &&
|
} 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;
|
struct mmci_host *host = dev_id;
|
||||||
u32 status;
|
u32 status;
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
spin_lock(&host->lock);
|
spin_lock(&host->lock);
|
||||||
|
host->irq_action = IRQ_HANDLED;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
status = readl(host->base + MMCISTATUS);
|
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)
|
if (host->variant->busy_detect_flag)
|
||||||
status &= ~host->variant->busy_detect_flag;
|
status &= ~host->variant->busy_detect_flag;
|
||||||
|
|
||||||
ret = 1;
|
|
||||||
} while (status);
|
} while (status);
|
||||||
|
|
||||||
spin_unlock(&host->lock);
|
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)
|
static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
@ -2063,8 +2096,9 @@ static int mmci_probe(struct amba_device *dev,
|
|||||||
goto clk_disable;
|
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,
|
||||||
DRIVER_NAME " (cmd)", host);
|
mmci_irq_thread, IRQF_SHARED,
|
||||||
|
DRIVER_NAME " (cmd)", host);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto clk_disable;
|
goto clk_disable;
|
||||||
|
|
||||||
|
@ -411,6 +411,7 @@ struct mmci_host {
|
|||||||
|
|
||||||
struct timer_list timer;
|
struct timer_list timer;
|
||||||
unsigned int oldstat;
|
unsigned int oldstat;
|
||||||
|
u32 irq_action;
|
||||||
|
|
||||||
/* pio stuff */
|
/* pio stuff */
|
||||||
struct sg_mapping_iter sg_miter;
|
struct sg_mapping_iter sg_miter;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user