Skip to content

Commit

Permalink
mmc: meson-gx: make sure the descriptor is stopped on errors
Browse files Browse the repository at this point in the history
On errors, if we don't stop the descriptor chain, it may continue to
run and raise IRQ after we have called mmc_request_done(). This is bad
because we won't be able to get cmd anymore and properly deal with the
IRQ.

This patch makes sure the descriptor chain is stopped before
calling mmc_request_done()

Fixes: 79ed05e ("mmc: meson-gx: add support for descriptor chain mode")
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
  • Loading branch information
Jerome Brunet authored and Ulf Hansson committed Dec 17, 2018
1 parent 41fd4ca commit 18f92bc
Showing 1 changed file with 63 additions and 10 deletions.
73 changes: 63 additions & 10 deletions drivers/mmc/host/meson-gx-mmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
Expand Down Expand Up @@ -90,9 +91,11 @@
#define CFG_CLK_ALWAYS_ON BIT(18)
#define CFG_CHK_DS BIT(20)
#define CFG_AUTO_CLK BIT(23)
#define CFG_ERR_ABORT BIT(27)

#define SD_EMMC_STATUS 0x48
#define STATUS_BUSY BIT(31)
#define STATUS_DESC_BUSY BIT(30)
#define STATUS_DATI GENMASK(23, 16)

#define SD_EMMC_IRQ_EN 0x4c
Expand Down Expand Up @@ -928,6 +931,7 @@ static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd)

cmd_cfg |= FIELD_PREP(CMD_CFG_CMD_INDEX_MASK, cmd->opcode);
cmd_cfg |= CMD_CFG_OWNER; /* owned by CPU */
cmd_cfg |= CMD_CFG_ERROR; /* stop in case of error */

meson_mmc_set_response_bits(cmd, &cmd_cfg);

Expand Down Expand Up @@ -1022,29 +1026,36 @@ static irqreturn_t meson_mmc_irq(int irq, void *dev_id)
u32 irq_en, status, raw_status;
irqreturn_t ret = IRQ_NONE;

irq_en = readl(host->regs + SD_EMMC_IRQ_EN);
raw_status = readl(host->regs + SD_EMMC_STATUS);
status = raw_status & irq_en;

if (!status) {
dev_dbg(host->dev,
"Unexpected IRQ! irq_en 0x%08x - status 0x%08x\n",
irq_en, raw_status);
return IRQ_NONE;
}

if (WARN_ON(!host) || WARN_ON(!host->cmd))
return IRQ_NONE;

spin_lock(&host->lock);

cmd = host->cmd;
data = cmd->data;
irq_en = readl(host->regs + SD_EMMC_IRQ_EN);
raw_status = readl(host->regs + SD_EMMC_STATUS);
status = raw_status & irq_en;

cmd->error = 0;
if (status & IRQ_CRC_ERR) {
dev_dbg(host->dev, "CRC Error - status 0x%08x\n", status);
cmd->error = -EILSEQ;
ret = IRQ_HANDLED;
ret = IRQ_WAKE_THREAD;
goto out;
}

if (status & IRQ_TIMEOUTS) {
dev_dbg(host->dev, "Timeout - status 0x%08x\n", status);
cmd->error = -ETIMEDOUT;
ret = IRQ_HANDLED;
ret = IRQ_WAKE_THREAD;
goto out;
}

Expand All @@ -1069,17 +1080,49 @@ static irqreturn_t meson_mmc_irq(int irq, void *dev_id)
/* ack all enabled interrupts */
writel(irq_en, host->regs + SD_EMMC_STATUS);

if (cmd->error) {
/* Stop desc in case of errors */
u32 start = readl(host->regs + SD_EMMC_START);

start &= ~START_DESC_BUSY;
writel(start, host->regs + SD_EMMC_START);
}

if (ret == IRQ_HANDLED)
meson_mmc_request_done(host->mmc, cmd->mrq);
else if (ret == IRQ_NONE)
dev_warn(host->dev,
"Unexpected IRQ! status=0x%08x, irq_en=0x%08x\n",
raw_status, irq_en);

spin_unlock(&host->lock);
return ret;
}

static int meson_mmc_wait_desc_stop(struct meson_host *host)
{
int loop;
u32 status;

/*
* It may sometimes take a while for it to actually halt. Here, we
* are giving it 5ms to comply
*
* If we don't confirm the descriptor is stopped, it might raise new
* IRQs after we have called mmc_request_done() which is bad.
*/
for (loop = 50; loop; loop--) {
status = readl(host->regs + SD_EMMC_STATUS);
if (status & (STATUS_BUSY | STATUS_DESC_BUSY))
udelay(100);
else
break;
}

if (status & (STATUS_BUSY | STATUS_DESC_BUSY)) {
dev_err(host->dev, "Timed out waiting for host to stop\n");
return -ETIMEDOUT;
}

return 0;
}

static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
{
struct meson_host *host = dev_id;
Expand All @@ -1090,6 +1133,13 @@ static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
if (WARN_ON(!cmd))
return IRQ_NONE;

if (cmd->error) {
meson_mmc_wait_desc_stop(host);
meson_mmc_request_done(host->mmc, cmd->mrq);

return IRQ_HANDLED;
}

data = cmd->data;
if (meson_mmc_bounce_buf_read(data)) {
xfer_bytes = data->blksz * data->blocks;
Expand Down Expand Up @@ -1130,6 +1180,9 @@ static void meson_mmc_cfg_init(struct meson_host *host)
cfg |= FIELD_PREP(CFG_RC_CC_MASK, ilog2(SD_EMMC_CFG_CMD_GAP));
cfg |= FIELD_PREP(CFG_BLK_LEN_MASK, ilog2(SD_EMMC_CFG_BLK_SIZE));

/* abort chain on R/W errors */
cfg |= CFG_ERR_ABORT;

writel(cfg, host->regs + SD_EMMC_CFG);
}

Expand Down

0 comments on commit 18f92bc

Please sign in to comment.