From 18de4e3be05a410815562c23497cf580c3c4d56c 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 number macros Replace magic bit constants (e.g., 1, 2, 4) with BIT(x) expressions. 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 fb5f91becad8f50e18a559e0a25b886df0a0eee7 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 init. to irq_init Initialize `cmd_done` completion struct as part of the irq_init routine (cosmetic). 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 1e220d8e1f4b7e2f0e05abb16b20994f53a1e81a Mon Sep 17 00:00:00 2001 From: Gabriel Somlo Date: Sun, 24 Sep 2023 18:49:45 -0400 Subject: [PATCH 03/10] mmc: litex_mmc: wait for data completion outside command handler Since setting up the (DMA) data transfer is done outside (and before) the call to `send_cmd`, wait for its completion outside (and after) `send_cmd` as well. This is also a cosmetic change. Signed-off-by: Gabriel Somlo --- drivers/mmc/host/litex_mmc.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index aa2386dc4df648..1263bccd7fc2a2 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, @@ -175,15 +173,6 @@ static int litex_mmc_send_cmd(struct litex_mmc_host *host, 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); - return ret; } @@ -390,6 +379,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 c525a3f627014dc2760dac073a34e246d763f736 Mon Sep 17 00:00:00 2001 From: Gabriel Somlo Date: Sun, 24 Sep 2023 19:03:08 -0400 Subject: [PATCH 04/10] mmc: remove redundant return from send_cmd Also, add a clarifying note explaining that waiting for CORE_DATAEVT is part of (and covered by) the command completion condition. Signed-off-by: Gabriel Somlo --- drivers/mmc/host/litex_mmc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index 1263bccd7fc2a2..17f1097a8466f6 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -167,10 +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; } return ret; From 9aeea2900bb0909a6c6fc64fbb3a35b0d345f075 Mon Sep 17 00:00:00 2001 From: Gabriel Somlo Date: Sun, 24 Sep 2023 18:57:00 -0400 Subject: [PATCH 05/10] mmc: litex_mmc: fix irq/completion command sequence Set up (reinit_completion, enable command IRQ) first, send the command next, and wait for completion after that. Signed-off-by: Gabriel Somlo --- drivers/mmc/host/litex_mmc.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index 17f1097a8466f6..1cee1b41f829ca 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -124,23 +124,24 @@ 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) { + 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 (host->irq > 0 && - (transfer != SD_CTL_DATA_XFER_NONE || - response_len == SD_CTL_RESP_SHORT_BUSY)) { - reinit_completion(&host->cmd_done); - litex_write32(host->sdirq + LITEX_IRQ_ENABLE, - SDIRQ_CMD_DONE | SDIRQ_CARD_DETECT); + if (use_irq) { wait_for_completion(&host->cmd_done); } From 7aedadf66195f01a595474a4c60760a23d11c484 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: fix cmd_done irq handling Keep the `cmd_done` irq enabled throughout the driver's operation, and handle it as a proper edge-triggered interrupt. 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 1cee1b41f829ca..79dc4b652a73bc 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 */ @@ -261,9 +259,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; } @@ -502,9 +499,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 e986bbd1d5266594fe2ef1f1899de5eccdfe69df Mon Sep 17 00:00:00 2001 From: Gabriel Somlo Date: Thu, 5 Oct 2023 09:17:03 -0400 Subject: [PATCH 07/10] mmc: litex_mmc: use irq with all commands Do not pick "slow" commands to be handled via irq, leaving "fast" ones to use polling. Instead, apply irq to *all* commands. Signed-off-by: Gabriel Somlo --- drivers/mmc/host/litex_mmc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index 79dc4b652a73bc..e489c240d457b8 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -124,12 +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; - if (use_irq) { + if (host->irq > 0) { reinit_completion(&host->cmd_done); } @@ -139,7 +136,7 @@ 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); } From cfc9662649c7b1f69e3a4043b036d19d3c1556f4 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: disable interrupts when driver removed 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 e489c240d457b8..006489bd22f2e8 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -469,6 +469,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) { @@ -496,6 +501,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 898fde87433d60c7b3a26ad3022762f3d78f53a3 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: acknowledge all handled IRQs at once Collect all handled IRQs and acknowledge them all at once, factoring out the MMIO port write required to do so. 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 006489bd22f2e8..543a8bb622b88a 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -244,25 +244,24 @@ 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; + /* Acknowledge handled interrupts */ + litex_write32(host->sdirq + LITEX_IRQ_PENDING, handled); + + return handled ? IRQ_HANDLED : IRQ_NONE; } static u32 litex_mmc_response_len(struct mmc_command *cmd) From bd05c5d3b8531589d8ccb423ed0ee0858c21ddc7 Mon Sep 17 00:00:00 2001 From: Gabriel Somlo Date: Sun, 24 Sep 2023 19:28:18 -0400 Subject: [PATCH 10/10] mmc: litex_mmc: add IRQ support for DMA data xfers Signed-off-by: Gabriel Somlo --- drivers/mmc/host/litex_mmc.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index 543a8bb622b88a..91f3e4c9aa7d08 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; @@ -258,6 +259,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); + } + /* Acknowledge handled interrupts */ litex_write32(host->sdirq + LITEX_IRQ_PENDING, handled); @@ -369,6 +382,10 @@ 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 +401,9 @@ 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 +527,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;