Commit 8d94b54d authored by Ulf Hansson's avatar Ulf Hansson Committed by Chris Ball

mmc: mmci: Enable support for busy detection for ux500 variant

The ux500 variants have HW busy detection support, which is indicated
by the busy_detect flag. For these variants let's enable the
MMC_CAP_WAIT_WHILE_BUSY flag and add the support for it.

The mmc core will provide the RSP_BUSY command flag for those requests
we should care about busy detection. Regarding the max_busy_timeout,
the HW don't support busy detection timeouts so at this initial step
let's make it simple and set it to zero to indicate we are able to
support any timeout.

Cc: Russell King <linux@arm.linux.org.uk>
Cc: Johan Rudholm <jrudholm@gmail.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
Signed-off-by: default avatarChris Ball <chris@printf.net>
parent e7f3d222
...@@ -921,6 +921,29 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, ...@@ -921,6 +921,29 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
{ {
void __iomem *base = host->base; void __iomem *base = host->base;
bool sbc = (cmd == host->mrq->sbc); bool sbc = (cmd == host->mrq->sbc);
bool busy_resp = host->variant->busy_detect &&
(cmd->flags & MMC_RSP_BUSY);
/* Check if we need to wait for busy completion. */
if (host->busy_status && (status & MCI_ST_CARDBUSY))
return;
/* Enable busy completion if needed and supported. */
if (!host->busy_status && busy_resp &&
!(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&
(readl(base + MMCISTATUS) & MCI_ST_CARDBUSY)) {
writel(readl(base + MMCIMASK0) | MCI_ST_BUSYEND,
base + MMCIMASK0);
host->busy_status = status & (MCI_CMDSENT|MCI_CMDRESPEND);
return;
}
/* At busy completion, mask the IRQ and complete the request. */
if (host->busy_status) {
writel(readl(base + MMCIMASK0) & ~MCI_ST_BUSYEND,
base + MMCIMASK0);
host->busy_status = 0;
}
host->cmd = NULL; host->cmd = NULL;
...@@ -1139,14 +1162,19 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) ...@@ -1139,14 +1162,19 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
status &= ~MCI_IRQ1MASK; status &= ~MCI_IRQ1MASK;
} }
/*
* We intentionally clear the MCI_ST_CARDBUSY IRQ here (if it's
* enabled) since the HW seems to be triggering the IRQ on both
* edges while monitoring DAT0 for busy completion.
*/
status &= readl(host->base + MMCIMASK0); status &= readl(host->base + MMCIMASK0);
writel(status, host->base + MMCICLEAR); writel(status, host->base + MMCICLEAR);
dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status); dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status);
cmd = host->cmd; cmd = host->cmd;
if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT| if ((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|
MCI_CMDRESPEND) && cmd) MCI_CMDSENT|MCI_CMDRESPEND) && cmd)
mmci_cmd_irq(host, cmd, status); mmci_cmd_irq(host, cmd, status);
data = host->data; data = host->data;
...@@ -1155,6 +1183,10 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) ...@@ -1155,6 +1183,10 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
MCI_DATABLOCKEND) && data) MCI_DATABLOCKEND) && data)
mmci_data_irq(host, data, status); mmci_data_irq(host, data, status);
/* Don't poll for busy completion in irq context. */
if (host->busy_status)
status &= ~MCI_ST_CARDBUSY;
ret = 1; ret = 1;
} while (status); } while (status);
...@@ -1504,12 +1536,6 @@ static int mmci_probe(struct amba_device *dev, ...@@ -1504,12 +1536,6 @@ static int mmci_probe(struct amba_device *dev,
goto clk_disable; goto clk_disable;
} }
if (variant->busy_detect) {
mmci_ops.card_busy = mmci_card_busy;
mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE);
}
mmc->ops = &mmci_ops;
/* /*
* The ARM and ST versions of the block have slightly different * The ARM and ST versions of the block have slightly different
* clock divider equations which means that the minimum divider * clock divider equations which means that the minimum divider
...@@ -1543,6 +1569,15 @@ static int mmci_probe(struct amba_device *dev, ...@@ -1543,6 +1569,15 @@ static int mmci_probe(struct amba_device *dev,
mmc->caps = plat->capabilities; mmc->caps = plat->capabilities;
mmc->caps2 = plat->capabilities2; mmc->caps2 = plat->capabilities2;
if (variant->busy_detect) {
mmci_ops.card_busy = mmci_card_busy;
mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE);
mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
mmc->max_busy_timeout = 0;
}
mmc->ops = &mmci_ops;
/* We support these PM capabilities. */ /* We support these PM capabilities. */
mmc->pm_caps = MMC_PM_KEEP_POWER; mmc->pm_caps = MMC_PM_KEEP_POWER;
......
...@@ -139,6 +139,7 @@ ...@@ -139,6 +139,7 @@
/* Extended status bits for the ST Micro variants */ /* Extended status bits for the ST Micro variants */
#define MCI_ST_SDIOITMASK (1 << 22) #define MCI_ST_SDIOITMASK (1 << 22)
#define MCI_ST_CEATAENDMASK (1 << 23) #define MCI_ST_CEATAENDMASK (1 << 23)
#define MCI_ST_BUSYEND (1 << 24)
#define MMCIMASK1 0x040 #define MMCIMASK1 0x040
#define MMCIFIFOCNT 0x048 #define MMCIFIFOCNT 0x048
...@@ -186,6 +187,7 @@ struct mmci_host { ...@@ -186,6 +187,7 @@ struct mmci_host {
u32 pwr_reg; u32 pwr_reg;
u32 clk_reg; u32 clk_reg;
u32 datactrl_reg; u32 datactrl_reg;
u32 busy_status;
bool vqmmc_enabled; bool vqmmc_enabled;
struct mmci_platform_data *plat; struct mmci_platform_data *plat;
struct variant_data *variant; struct variant_data *variant;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment