From f3fdb534afa72688aba2f6b09f4e431628d86d1d Mon Sep 17 00:00:00 2001 From: Gabriel Somlo Date: Sun, 24 Sep 2023 18:30:25 -0400 Subject: [PATCH 01/10] mmc: litex_mmc: use BIT() macros for IRQ registers Signed-off-by: Gabriel Somlo --- drivers/mmc/host/litex_mmc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index 4ec8072dc60b3d..d04071ffd4fa95 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -68,10 +68,10 @@ #define SD_SLEEP_US 5 #define SD_TIMEOUT_US 20000 -#define SDIRQ_CARD_DETECT 1 -#define SDIRQ_SD_TO_MEM_DONE 2 -#define SDIRQ_MEM_TO_SD_DONE 4 -#define SDIRQ_CMD_DONE 8 +#define SDIRQ_CARD_DETECT BIT(0) +#define SDIRQ_SD_TO_MEM_DONE BIT(1) +#define SDIRQ_MEM_TO_SD_DONE BIT(2) +#define SDIRQ_CMD_DONE BIT(3) struct litex_mmc_host { struct mmc_host *mmc; From c040c28b7809d5153aea3c89df4078c2dbce7264 Mon Sep 17 00:00:00 2001 From: Gabriel Somlo Date: Sun, 24 Sep 2023 18:34:26 -0400 Subject: [PATCH 02/10] mmc: litex_mmc: move completion setup to irq_init Initializing completion(s) belongs with the rest of the IRQ support setup, in `irq_init()`. Signed-off-by: Gabriel Somlo --- drivers/mmc/host/litex_mmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index d04071ffd4fa95..aa2386dc4df648 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -498,6 +498,8 @@ static int litex_mmc_irq_init(struct platform_device *pdev, litex_write32(host->sdirq + LITEX_IRQ_PENDING, SDIRQ_CARD_DETECT); litex_write32(host->sdirq + LITEX_IRQ_ENABLE, SDIRQ_CARD_DETECT); + init_completion(&host->cmd_done); + return 0; use_polling: @@ -583,7 +585,6 @@ static int litex_mmc_probe(struct platform_device *pdev) litex_write8(host->sdreader + LITEX_BLK2MEM_ENA, 0); litex_write8(host->sdwriter + LITEX_MEM2BLK_ENA, 0); - init_completion(&host->cmd_done); ret = litex_mmc_irq_init(pdev, host); if (ret) return ret; From 1af3e8ad90923c946b7729bc1b13759b3fc8d479 Mon Sep 17 00:00:00 2001 From: Gabriel Somlo Date: Fri, 13 Oct 2023 16:24:31 -0400 Subject: [PATCH 03/10] mmc: litex_mmc: wait for data completion outside cmd handler Setting up DMA data transfers happens outside (and before) the call to `send_cmd()`; therefore, wait for their completion also outside (and after) the `send_cmd()` call. Signed-off-by: Gabriel Somlo --- drivers/mmc/host/litex_mmc.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index aa2386dc4df648..495a681431274b 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -124,9 +124,7 @@ static int litex_mmc_send_cmd(struct litex_mmc_host *host, u8 cmd, u32 arg, u8 response_len, u8 transfer) { struct device *dev = mmc_dev(host->mmc); - void __iomem *reg; int ret; - u8 evt; litex_write32(host->sdcore + LITEX_CORE_CMDARG, arg); litex_write32(host->sdcore + LITEX_CORE_CMDCMD, @@ -169,20 +167,14 @@ static int litex_mmc_send_cmd(struct litex_mmc_host *host, if (transfer == SD_CTL_DATA_XFER_NONE) return ret; /* OK from prior litex_mmc_sdcard_wait_done() */ + /* + * NOTE: this information becomes available at the same time + * as LITEX_CORE_CMDEVT, and is therefore also covered by the + * SDIRQ_CMD_DONE interrupt and cmd_done completion + */ ret = litex_mmc_sdcard_wait_done(host->sdcore + LITEX_CORE_DATEVT, dev); - if (ret) { - dev_err(dev, "Data xfer (cmd %d) error, status %d\n", cmd, ret); - return ret; - } - - /* Wait for completion of (read or write) DMA transfer */ - reg = (transfer == SD_CTL_DATA_XFER_READ) ? - host->sdreader + LITEX_BLK2MEM_DONE : - host->sdwriter + LITEX_MEM2BLK_DONE; - ret = readx_poll_timeout(litex_read8, reg, evt, evt & SD_BIT_DONE, - SD_SLEEP_US, SD_TIMEOUT_US); if (ret) - dev_err(dev, "DMA timeout (cmd %d)\n", cmd); + dev_err(dev, "Data xfer (cmd %d) error, status %d\n", cmd, ret); return ret; } @@ -390,6 +382,20 @@ static void litex_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) response_len, transfer); } while (cmd->error && retries-- > 0); + if (!cmd->error && transfer != SD_CTL_DATA_XFER_NONE) { + void __iomem *reg = (transfer == SD_CTL_DATA_XFER_READ) ? + host->sdreader + LITEX_BLK2MEM_DONE : + host->sdwriter + LITEX_MEM2BLK_DONE; + u8 evt; + + /* Wait for completion of (read or write) DMA transfer */ + cmd->error = readx_poll_timeout(litex_read8, reg, + evt, evt & SD_BIT_DONE, + SD_SLEEP_US, SD_TIMEOUT_US); + if (cmd->error) + dev_err(dev, "DMA timeout (cmd %d)\n", cmd->opcode); + } + if (cmd->error) { /* Card may be gone; don't assume bus width is still set */ host->is_bus_width_set = false; From 1f7136d1a8fefd1f86522c29308a3b2200951bcd Mon Sep 17 00:00:00 2001 From: Gabriel Somlo Date: Fri, 13 Oct 2023 11:47:47 -0400 Subject: [PATCH 04/10] mmc: litex_mmc: isolate cmd irq heuristic Separate the criteria for selecting commands that should use IRQ into an explicit Boolean flag. Signed-off-by: Gabriel Somlo --- drivers/mmc/host/litex_mmc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index 495a681431274b..5db4540028935a 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -124,6 +124,9 @@ static int litex_mmc_send_cmd(struct litex_mmc_host *host, u8 cmd, u32 arg, u8 response_len, u8 transfer) { struct device *dev = mmc_dev(host->mmc); + bool use_irq = host->irq > 0 && + (transfer != SD_CTL_DATA_XFER_NONE || + response_len == SD_CTL_RESP_SHORT_BUSY); int ret; litex_write32(host->sdcore + LITEX_CORE_CMDARG, arg); @@ -135,9 +138,7 @@ static int litex_mmc_send_cmd(struct litex_mmc_host *host, * Wait for an interrupt if we have an interrupt and either there is * data to be transferred, or if the card can report busy via DAT0. */ - if (host->irq > 0 && - (transfer != SD_CTL_DATA_XFER_NONE || - response_len == SD_CTL_RESP_SHORT_BUSY)) { + if (use_irq) { reinit_completion(&host->cmd_done); litex_write32(host->sdirq + LITEX_IRQ_ENABLE, SDIRQ_CMD_DONE | SDIRQ_CARD_DETECT); From 5a7c0537ce557bb2673b6e85164b5e90ffa42846 Mon Sep 17 00:00:00 2001 From: Gabriel Somlo Date: Fri, 13 Oct 2023 11:49:44 -0400 Subject: [PATCH 05/10] mmc: litex_mmc: fix cmd irq/completion sequencing To ensure interrupts cannot be missed, we must (re)initialize completions *before* setting up any transaction that will result in the assertion of an interrupt. Waiting for completion should follow *after* the hardware transaction setup, and before proceeding with e.g. a polling loop acting as fall-back for when IRQ support is unavailable. Signed-off-by: Gabriel Somlo --- drivers/mmc/host/litex_mmc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index 5db4540028935a..95adb056a8dd66 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -129,19 +129,19 @@ static int litex_mmc_send_cmd(struct litex_mmc_host *host, response_len == SD_CTL_RESP_SHORT_BUSY); int ret; + if (use_irq) { + reinit_completion(&host->cmd_done); + litex_write32(host->sdirq + LITEX_IRQ_ENABLE, + SDIRQ_CMD_DONE | SDIRQ_CARD_DETECT); + } + + /* Send command */ litex_write32(host->sdcore + LITEX_CORE_CMDARG, arg); litex_write32(host->sdcore + LITEX_CORE_CMDCMD, cmd << 8 | transfer << 5 | response_len); litex_write8(host->sdcore + LITEX_CORE_CMDSND, 1); - /* - * Wait for an interrupt if we have an interrupt and either there is - * data to be transferred, or if the card can report busy via DAT0. - */ if (use_irq) { - reinit_completion(&host->cmd_done); - litex_write32(host->sdirq + LITEX_IRQ_ENABLE, - SDIRQ_CMD_DONE | SDIRQ_CARD_DETECT); wait_for_completion(&host->cmd_done); } From f1557fa2e5630835447392ecc177cc26bbf8c2aa Mon Sep 17 00:00:00 2001 From: Gabriel Somlo Date: Sun, 24 Sep 2023 19:13:34 -0400 Subject: [PATCH 06/10] mmc: litex_mmc: switch cmd_done irq to edge-triggered Switch from *level* to *edge* triggered IRQ mode for the command completion interrupt. The SDIRQ_CMD_DONE interrupt will no longer have to be toggled by the interrupt handler, laying the groundwork for supporting (edge-triggered) data/dma completion interrupts in a subsequent patch. NOTE: this requires the underlying LiteX gateware to be newer than commit #XXXXXXXX ("integration/soc/add_sdcard: switch to edge-triggered cmd-done irq") Signed-off-by: Gabriel Somlo --- drivers/mmc/host/litex_mmc.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index 95adb056a8dd66..c5e4cdc570f0e2 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -131,8 +131,6 @@ static int litex_mmc_send_cmd(struct litex_mmc_host *host, if (use_irq) { reinit_completion(&host->cmd_done); - litex_write32(host->sdirq + LITEX_IRQ_ENABLE, - SDIRQ_CMD_DONE | SDIRQ_CARD_DETECT); } /* Send command */ @@ -260,9 +258,8 @@ static irqreturn_t litex_mmc_interrupt(int irq, void *arg) /* Check for command completed */ if (pending & SDIRQ_CMD_DONE) { - /* Disable it so it doesn't keep interrupting */ - litex_write32(host->sdirq + LITEX_IRQ_ENABLE, - SDIRQ_CARD_DETECT); + litex_write32(host->sdirq + LITEX_IRQ_PENDING, + SDIRQ_CMD_DONE); complete(&host->cmd_done); ret = IRQ_HANDLED; } @@ -501,9 +498,11 @@ static int litex_mmc_irq_init(struct platform_device *pdev, goto use_polling; } - /* Clear & enable card-change interrupts */ - litex_write32(host->sdirq + LITEX_IRQ_PENDING, SDIRQ_CARD_DETECT); - litex_write32(host->sdirq + LITEX_IRQ_ENABLE, SDIRQ_CARD_DETECT); + /* Clear & enable interrupts */ + litex_write32(host->sdirq + LITEX_IRQ_PENDING, + SDIRQ_CARD_DETECT | SDIRQ_CMD_DONE); + litex_write32(host->sdirq + LITEX_IRQ_ENABLE, + SDIRQ_CARD_DETECT | SDIRQ_CMD_DONE); init_completion(&host->cmd_done); From 66074a0c2eee84495587e3428beb190f76c96b64 Mon Sep 17 00:00:00 2001 From: Gabriel Somlo Date: Fri, 13 Oct 2023 16:16:57 -0400 Subject: [PATCH 07/10] mmc: litex_mmc: use irq for all commands Instead of heuristically selecting "slow" commands to use interrupts, leaving "fast" ones to use polling, apply IRQ handling equally to *all* commands. The measured transfer times decreased slightly for some LiteX designs (e.g., using VexRiscv/NaxRiscv), and did not increase measurably for others (e.g., using RocketChip). Signed-off-by: Gabriel Somlo --- drivers/mmc/host/litex_mmc.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index c5e4cdc570f0e2..955be973a9ba65 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -124,14 +124,10 @@ static int litex_mmc_send_cmd(struct litex_mmc_host *host, u8 cmd, u32 arg, u8 response_len, u8 transfer) { struct device *dev = mmc_dev(host->mmc); - bool use_irq = host->irq > 0 && - (transfer != SD_CTL_DATA_XFER_NONE || - response_len == SD_CTL_RESP_SHORT_BUSY); int ret; - if (use_irq) { + if (host->irq > 0) reinit_completion(&host->cmd_done); - } /* Send command */ litex_write32(host->sdcore + LITEX_CORE_CMDARG, arg); @@ -139,9 +135,8 @@ static int litex_mmc_send_cmd(struct litex_mmc_host *host, cmd << 8 | transfer << 5 | response_len); litex_write8(host->sdcore + LITEX_CORE_CMDSND, 1); - if (use_irq) { + if (host->irq > 0) wait_for_completion(&host->cmd_done); - } ret = litex_mmc_sdcard_wait_done(host->sdcore + LITEX_CORE_CMDEVT, dev); if (ret) { From 10240a96da4c282bd94671d425552fa3d22ba25c Mon Sep 17 00:00:00 2001 From: Gabriel Somlo Date: Sun, 24 Sep 2023 19:18:33 -0400 Subject: [PATCH 08/10] mmc: litex_mmc: turn off IRQs on driver removal Ensure that interrupts are turned off as part of removing the driver. Signed-off-by: Gabriel Somlo --- drivers/mmc/host/litex_mmc.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index 955be973a9ba65..a797e8372b49f8 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -466,6 +466,11 @@ static const struct mmc_host_ops litex_mmc_ops = { .set_ios = litex_mmc_set_ios, }; +static void litex_mmc_irq_off(void *sdirq) +{ + litex_write32((void __iomem *)sdirq + LITEX_IRQ_ENABLE, 0); +} + static int litex_mmc_irq_init(struct platform_device *pdev, struct litex_mmc_host *host) { @@ -493,6 +498,11 @@ static int litex_mmc_irq_init(struct platform_device *pdev, goto use_polling; } + ret = devm_add_action_or_reset(dev, litex_mmc_irq_off, host->sdirq); + if (ret) + return dev_err_probe(dev, ret, + "Can't register irq_off action\n"); + /* Clear & enable interrupts */ litex_write32(host->sdirq + LITEX_IRQ_PENDING, SDIRQ_CARD_DETECT | SDIRQ_CMD_DONE); From 14916705b5e4263a3d1f7a36da15fec4f5243ae1 Mon Sep 17 00:00:00 2001 From: Gabriel Somlo Date: Sun, 24 Sep 2023 19:21:53 -0400 Subject: [PATCH 09/10] mmc: litex_mmc: ack all handled IRQs together Collect handled IRQs and ack them all together with a single MMIO port write. Signed-off-by: Gabriel Somlo --- drivers/mmc/host/litex_mmc.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index a797e8372b49f8..c04b4c4e019c92 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -241,25 +241,27 @@ static irqreturn_t litex_mmc_interrupt(int irq, void *arg) struct mmc_host *mmc = arg; struct litex_mmc_host *host = mmc_priv(mmc); u32 pending = litex_read32(host->sdirq + LITEX_IRQ_PENDING); - irqreturn_t ret = IRQ_NONE; + u32 handled = 0; /* Check for card change interrupt */ if (pending & SDIRQ_CARD_DETECT) { - litex_write32(host->sdirq + LITEX_IRQ_PENDING, - SDIRQ_CARD_DETECT); + handled |= SDIRQ_CARD_DETECT; mmc_detect_change(mmc, msecs_to_jiffies(10)); - ret = IRQ_HANDLED; } /* Check for command completed */ if (pending & SDIRQ_CMD_DONE) { - litex_write32(host->sdirq + LITEX_IRQ_PENDING, - SDIRQ_CMD_DONE); + handled |= SDIRQ_CMD_DONE; complete(&host->cmd_done); - ret = IRQ_HANDLED; } - return ret; + if (handled) { + /* Acknowledge handled interrupts */ + litex_write32(host->sdirq + LITEX_IRQ_PENDING, handled); + return IRQ_HANDLED; + } + + return IRQ_NONE; } static u32 litex_mmc_response_len(struct mmc_command *cmd) From b695ceef171f388711104a5481ce9e79870df32f Mon Sep 17 00:00:00 2001 From: Gabriel Somlo Date: Fri, 13 Oct 2023 16:21:47 -0400 Subject: [PATCH 10/10] mmc: litex_mmc: add IRQ support for DMA data xfers Add support for utilizing interrupts generated by LiteSDCard when DMA data transfers complete. Signed-off-by: Gabriel Somlo --- drivers/mmc/host/litex_mmc.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index c04b4c4e019c92..44cd7f4d033229 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -87,6 +87,7 @@ struct litex_mmc_host { dma_addr_t dma; struct completion cmd_done; + struct completion data_done; int irq; unsigned int ref_clk; @@ -255,6 +256,18 @@ static irqreturn_t litex_mmc_interrupt(int irq, void *arg) complete(&host->cmd_done); } + /* Check for DMA read completed */ + if (pending & SDIRQ_SD_TO_MEM_DONE) { + handled |= SDIRQ_SD_TO_MEM_DONE; + complete(&host->data_done); + } + + /* Check for DMA write completed */ + if (pending & SDIRQ_MEM_TO_SD_DONE) { + handled |= SDIRQ_MEM_TO_SD_DONE; + complete(&host->data_done); + } + if (handled) { /* Acknowledge handled interrupts */ litex_write32(host->sdirq + LITEX_IRQ_PENDING, handled); @@ -369,6 +382,9 @@ static void litex_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) return; } + if (host->irq > 0) + reinit_completion(&host->data_done); + litex_mmc_do_dma(host, data, &len, &direct, &transfer); } @@ -384,6 +400,8 @@ static void litex_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) u8 evt; /* Wait for completion of (read or write) DMA transfer */ + if (host->irq > 0) + wait_for_completion(&host->data_done); cmd->error = readx_poll_timeout(litex_read8, reg, evt, evt & SD_BIT_DONE, SD_SLEEP_US, SD_TIMEOUT_US); @@ -507,11 +525,14 @@ static int litex_mmc_irq_init(struct platform_device *pdev, /* Clear & enable interrupts */ litex_write32(host->sdirq + LITEX_IRQ_PENDING, - SDIRQ_CARD_DETECT | SDIRQ_CMD_DONE); + SDIRQ_CARD_DETECT | SDIRQ_CMD_DONE | + SDIRQ_SD_TO_MEM_DONE | SDIRQ_MEM_TO_SD_DONE); litex_write32(host->sdirq + LITEX_IRQ_ENABLE, - SDIRQ_CARD_DETECT | SDIRQ_CMD_DONE); + SDIRQ_CARD_DETECT | SDIRQ_CMD_DONE | + SDIRQ_SD_TO_MEM_DONE | SDIRQ_MEM_TO_SD_DONE); init_completion(&host->cmd_done); + init_completion(&host->data_done); return 0;