| From 266bb56464cce262a9e5d441e75e6686ad53ea92 Mon Sep 17 00:00:00 2001 |
| From: Douglas Anderson <dianders@chromium.org> |
| Date: Mon, 13 Apr 2020 16:27:27 -0700 |
| Subject: [PATCH] mmc: cqhci: Avoid false "cqhci: CQE stuck on" by not |
| open-coding timeout loop |
| |
| commit b1ac62a7ac386d76968af5f374a4a7a82a35fe31 upstream. |
| |
| Open-coding a timeout loop invariably leads to errors with handling |
| the timeout properly in one corner case or another. In the case of |
| cqhci we might report "CQE stuck on" even if it wasn't stuck on. |
| You'd just need this sequence of events to happen in cqhci_off(): |
| |
| 1. Call ktime_get(). |
| 2. Something happens to interrupt the CPU for > 100 us (context switch |
| or interrupt). |
| 3. Check time and; set "timed_out" to true since > 100 us. |
| 4. Read CQHCI_CTL. |
| 5. Both "reg & CQHCI_HALT" and "timed_out" are true, so break. |
| 6. Since "timed_out" is true, falsely print the error message. |
| |
| Rather than fixing the polling loop, use readx_poll_timeout() like |
| many people do. This has been time tested to handle the corner cases. |
| |
| Fixes: a4080225f51d ("mmc: cqhci: support for command queue enabled host") |
| Signed-off-by: Douglas Anderson <dianders@chromium.org> |
| Acked-by: Adrian Hunter <adrian.hunter@intel.com> |
| Cc: stable@vger.kernel.org |
| Link: https://lore.kernel.org/r/20200413162717.1.Idece266f5c8793193b57a1ddb1066d030c6af8e0@changeid |
| Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/mmc/host/cqhci.c b/drivers/mmc/host/cqhci.c |
| index 5047f7343ffc..c19f4c3f115a 100644 |
| --- a/drivers/mmc/host/cqhci.c |
| +++ b/drivers/mmc/host/cqhci.c |
| @@ -5,6 +5,7 @@ |
| #include <linux/delay.h> |
| #include <linux/highmem.h> |
| #include <linux/io.h> |
| +#include <linux/iopoll.h> |
| #include <linux/module.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/slab.h> |
| @@ -343,12 +344,16 @@ static int cqhci_enable(struct mmc_host *mmc, struct mmc_card *card) |
| /* CQHCI is idle and should halt immediately, so set a small timeout */ |
| #define CQHCI_OFF_TIMEOUT 100 |
| |
| +static u32 cqhci_read_ctl(struct cqhci_host *cq_host) |
| +{ |
| + return cqhci_readl(cq_host, CQHCI_CTL); |
| +} |
| + |
| static void cqhci_off(struct mmc_host *mmc) |
| { |
| struct cqhci_host *cq_host = mmc->cqe_private; |
| - ktime_t timeout; |
| - bool timed_out; |
| u32 reg; |
| + int err; |
| |
| if (!cq_host->enabled || !mmc->cqe_on || cq_host->recovery_halt) |
| return; |
| @@ -358,15 +363,9 @@ static void cqhci_off(struct mmc_host *mmc) |
| |
| cqhci_writel(cq_host, CQHCI_HALT, CQHCI_CTL); |
| |
| - timeout = ktime_add_us(ktime_get(), CQHCI_OFF_TIMEOUT); |
| - while (1) { |
| - timed_out = ktime_compare(ktime_get(), timeout) > 0; |
| - reg = cqhci_readl(cq_host, CQHCI_CTL); |
| - if ((reg & CQHCI_HALT) || timed_out) |
| - break; |
| - } |
| - |
| - if (timed_out) |
| + err = readx_poll_timeout(cqhci_read_ctl, cq_host, reg, |
| + reg & CQHCI_HALT, 0, CQHCI_OFF_TIMEOUT); |
| + if (err < 0) |
| pr_err("%s: cqhci: CQE stuck on\n", mmc_hostname(mmc)); |
| else |
| pr_debug("%s: cqhci: CQE off\n", mmc_hostname(mmc)); |
| -- |
| 2.7.4 |
| |