From 18cbe81639f2c8999f9ad0505a08ddfc01cd05f7 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 14 Jan 2024 16:01:57 +0100 Subject: [PATCH 01/44] mmc: core: Remove usage of the deprecated ida_simple_xx() API ida_alloc() and ida_free() should be preferred to the deprecated ida_simple_get() and ida_simple_remove(). Note that the upper limit of ida_simple_get() is exclusive, but the one of ida_alloc_range()/ida_alloc_max() is inclusive. So a -1 has been added when needed. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/583c57d0ae09f9d3a1e1a7b80c1e39ada17954b7.1705244502.git.christophe.jaillet@wanadoo.fr Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 12 ++++++------ drivers/mmc/core/host.c | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 32d49100dff5..a9b60b91e32f 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -206,7 +206,7 @@ static void mmc_blk_kref_release(struct kref *ref) int devidx; devidx = mmc_get_devidx(md->disk); - ida_simple_remove(&mmc_blk_ida, devidx); + ida_free(&mmc_blk_ida, devidx); mutex_lock(&open_lock); md->disk->private_data = NULL; @@ -2467,7 +2467,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, bool cache_enabled = false; bool fua_enabled = false; - devidx = ida_simple_get(&mmc_blk_ida, 0, max_devices, GFP_KERNEL); + devidx = ida_alloc_max(&mmc_blk_ida, max_devices - 1, GFP_KERNEL); if (devidx < 0) { /* * We get -ENOSPC because there are no more any available @@ -2577,7 +2577,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, err_kfree: kfree(md); out: - ida_simple_remove(&mmc_blk_ida, devidx); + ida_free(&mmc_blk_ida, devidx); return ERR_PTR(ret); } @@ -2703,7 +2703,7 @@ static void mmc_blk_rpmb_device_release(struct device *dev) { struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev); - ida_simple_remove(&mmc_rpmb_ida, rpmb->id); + ida_free(&mmc_rpmb_ida, rpmb->id); kfree(rpmb); } @@ -2719,13 +2719,13 @@ static int mmc_blk_alloc_rpmb_part(struct mmc_card *card, struct mmc_rpmb_data *rpmb; /* This creates the minor number for the RPMB char device */ - devidx = ida_simple_get(&mmc_rpmb_ida, 0, max_devices, GFP_KERNEL); + devidx = ida_alloc_max(&mmc_rpmb_ida, max_devices - 1, GFP_KERNEL); if (devidx < 0) return devidx; rpmb = kzalloc(sizeof(*rpmb), GFP_KERNEL); if (!rpmb) { - ida_simple_remove(&mmc_rpmb_ida, devidx); + ida_free(&mmc_rpmb_ida, devidx); return -ENOMEM; } diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index cf396e8f34e9..7cc9a33d28ca 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -76,7 +76,7 @@ static void mmc_host_classdev_release(struct device *dev) struct mmc_host *host = cls_dev_to_mmc_host(dev); wakeup_source_unregister(host->ws); if (of_alias_get_id(host->parent->of_node, "mmc") < 0) - ida_simple_remove(&mmc_host_ida, host->index); + ida_free(&mmc_host_ida, host->index); kfree(host); } @@ -538,7 +538,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) min_idx = mmc_first_nonreserved_index(); max_idx = 0; - index = ida_simple_get(&mmc_host_ida, min_idx, max_idx, GFP_KERNEL); + index = ida_alloc_range(&mmc_host_ida, min_idx, max_idx - 1, + GFP_KERNEL); if (index < 0) { kfree(host); return NULL; From c64ec3a7d9e93e2e0e06bdc4b1ce760b54676c97 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 22 Jan 2024 17:16:23 +0800 Subject: [PATCH 02/44] dt-bindings: mmc: fsl-imx-esdhc: add i.MX95 compatible string Same as i.MX93, add i.MX95 SDHC which is compatible with i.MX8MM USDHC. Signed-off-by: Peng Fan Link: https://lore.kernel.org/r/20240122091623.2078089-1-peng.fan@oss.nxp.com Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml index 82eb7a24c857..f3c5aa64affc 100644 --- a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml +++ b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml @@ -55,8 +55,9 @@ properties: - enum: - fsl,imx8mn-usdhc - fsl,imx8mp-usdhc - - fsl,imx93-usdhc - fsl,imx8ulp-usdhc + - fsl,imx93-usdhc + - fsl,imx95-usdhc - const: fsl,imx8mm-usdhc - items: - enum: From 069279d6fef57b06d5489d6aa8775630903c0da2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 25 Jan 2024 09:50:23 +0100 Subject: [PATCH 03/44] mmc: core Drop BLK_BOUNCE_HIGH The MMC core sets BLK_BOUNCE_HIGH for devices where dma_mask is unassigned. For the majority of MMC hosts this path is never taken: the OF core will unconditionally assign a 32-bit mask to any OF device, and most MMC hosts are probed from device tree, see drivers/of/platform.c: of_platform_device_create_pdata() dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); if (!dev->dev.dma_mask) dev->dev.dma_mask = &dev->dev.coherent_dma_mask; of_amba_device_create() dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); dev->dev.dma_mask = &dev->dev.coherent_dma_mask; MMC devices that are probed from ACPI or PCI will likewise have a proper dma_mask assigned. The only remaining devices that could have a blank dma_mask are platform devices instantiated from board files. These are mostly used on systems without CONFIG_HIGHMEM enabled which means the block layer will not bounce, and in the few cases where it is enabled it is not used anyway: for example some OMAP2 systems such as Nokia n800/n810 will create a platform_device and not assign a dma_mask, however they do not have any highmem, so no bouncing will happen anyway: the block core checks if max_low_pfn >= max_pfn and this will always be false. Should it turn out there is a platform_device with blank DMA mask actually using CONFIG_HIGHMEM somewhere out there we should set dma_mask for it, not do this trickery. Signed-off-by: Linus Walleij Acked-by: Arnd Bergmann Acked-by: Christoph Hellwig Link: https://lore.kernel.org/r/20240125-mmc-no-blk-bounce-high-v1-1-d0f92a30e085@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/core/queue.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c index a0a2412f62a7..316415588a77 100644 --- a/drivers/mmc/core/queue.c +++ b/drivers/mmc/core/queue.c @@ -351,8 +351,6 @@ static void mmc_setup_queue(struct mmc_queue *mq, struct mmc_card *card) if (mmc_can_erase(card)) mmc_queue_setup_discard(mq->queue, card); - if (!mmc_dev(host)->dma_mask || !*mmc_dev(host)->dma_mask) - blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_HIGH); blk_queue_max_hw_sectors(mq->queue, min(host->max_blk_count, host->max_req_size / 512)); if (host->can_dma_map_merge) From 6bf26a0e5e9b11f7f4a614e539b9ed511e2fc0cd Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Sat, 3 Feb 2024 16:02:00 -0300 Subject: [PATCH 04/44] mmc: core: make mmc_rpmb_bus_type const Now that the driver core can properly handle constant struct bus_type, move the mmc_rpmb_bus_type variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Suggested-by: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Reviewed-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20240203-bus_cleanup-mmc-v1-1-ad054dce8dc3@marliere.net Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index a9b60b91e32f..bd165cef2bbc 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -144,7 +144,7 @@ struct mmc_blk_data { static dev_t mmc_rpmb_devt; /* Bus type for RPMB character devices */ -static struct bus_type mmc_rpmb_bus_type = { +static const struct bus_type mmc_rpmb_bus_type = { .name = "mmc_rpmb", }; From 1ba28dee4b88f14887d0a6981bce80c5885f34c2 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Sat, 3 Feb 2024 16:02:01 -0300 Subject: [PATCH 05/44] mmc: core: make mmc_bus_type const Now that the driver core can properly handle constant struct bus_type, move the mmc_bus_type variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Suggested-by: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Reviewed-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20240203-bus_cleanup-mmc-v1-2-ad054dce8dc3@marliere.net Signed-off-by: Ulf Hansson --- drivers/mmc/core/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 0af96548e7da..48daeafdff7a 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -214,7 +214,7 @@ static const struct dev_pm_ops mmc_bus_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(mmc_bus_suspend, mmc_bus_resume) }; -static struct bus_type mmc_bus_type = { +static const struct bus_type mmc_bus_type = { .name = "mmc", .dev_groups = mmc_dev_groups, .uevent = mmc_bus_uevent, From 13dbc21bf80218acc51ab257d1976e3695b9d01a Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Sat, 3 Feb 2024 16:02:02 -0300 Subject: [PATCH 06/44] mmc: core: make sdio_bus_type const Now that the driver core can properly handle constant struct bus_type, move the sdio_bus_type variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Suggested-by: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Reviewed-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20240203-bus_cleanup-mmc-v1-3-ad054dce8dc3@marliere.net Signed-off-by: Ulf Hansson --- drivers/mmc/core/sdio_bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 47a48e902a24..71d885fbc228 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -244,7 +244,7 @@ static const struct dev_pm_ops sdio_bus_pm_ops = { ) }; -static struct bus_type sdio_bus_type = { +static const struct bus_type sdio_bus_type = { .name = "sdio", .dev_groups = sdio_dev_groups, .match = sdio_bus_match, From cb8e8570d5c99e99715466d5616ad1bc3a4ad2f8 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Sun, 4 Feb 2024 17:05:58 -0300 Subject: [PATCH 07/44] memstick: core: make memstick_bus_type const Now that the driver core can properly handle constant struct bus_type, move the memstick_bus_type variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Suggested-by: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Reviewed-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20240204-bus_cleanup-memstick-v1-1-14809d4405d8@marliere.net Signed-off-by: Ulf Hansson --- drivers/memstick/core/memstick.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c index bbfaf6536903..23fea51ecbdd 100644 --- a/drivers/memstick/core/memstick.c +++ b/drivers/memstick/core/memstick.c @@ -164,7 +164,7 @@ static struct attribute *memstick_dev_attrs[] = { }; ATTRIBUTE_GROUPS(memstick_dev); -static struct bus_type memstick_bus_type = { +static const struct bus_type memstick_bus_type = { .name = "memstick", .dev_groups = memstick_dev_groups, .match = memstick_bus_match, From 3a999e57cf4bebe46070378013a3c3e7ac00e4d3 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Thu, 1 Feb 2024 15:22:41 -0500 Subject: [PATCH 08/44] dt-bindings: mmc: fsl-imx-esdhc: add iommus property iMX95 and iMX8QM have smmu. Add property "iommus". Signed-off-by: Frank Li Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240201-8qm_smmu-v2-1-3d12a80201a3@nxp.com Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml index f3c5aa64affc..3f6dcc773092 100644 --- a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml +++ b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml @@ -163,6 +163,9 @@ properties: - const: ahb - const: per + iommus: + maxItems: 1 + power-domains: maxItems: 1 From ed01d210fd910f7fa7933638df14ffb8d4aac2a9 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 27 Jan 2024 01:19:48 +0100 Subject: [PATCH 09/44] mmc: davinci_mmc: Use sg_miter for PIO Use the scatterlist memory iterator instead of just dereferencing virtual memory using sg_virt(). This make highmem references work properly. Suggested-by: Christoph Hellwig Link: https://lore.kernel.org/linux-mmc/20240122073423.GA25859@lst.de/ Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20240127-mmc-proper-kmap-v2-1-d8e732aa97d1@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/davinci_mmc.c | 61 ++++++++++++++++------------------ 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index ee3b1a4e0848..c46577305138 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -180,12 +180,6 @@ struct mmc_davinci_host { #define DAVINCI_MMC_DATADIR_WRITE 2 unsigned char data_dir; - /* buffer is used during PIO of one scatterlist segment, and - * is updated along with buffer_bytes_left. bytes_left applies - * to all N blocks of the PIO transfer. - */ - u8 *buffer; - u32 buffer_bytes_left; u32 bytes_left; struct dma_chan *dma_tx; @@ -196,8 +190,8 @@ struct mmc_davinci_host { bool active_request; /* For PIO we walk scatterlists one segment at a time. */ + struct sg_mapping_iter sg_miter; unsigned int sg_len; - struct scatterlist *sg; /* Version of the MMC/SD controller */ u8 version; @@ -213,30 +207,24 @@ struct mmc_davinci_host { static irqreturn_t mmc_davinci_irq(int irq, void *dev_id); /* PIO only */ -static void mmc_davinci_sg_to_buf(struct mmc_davinci_host *host) -{ - host->buffer_bytes_left = sg_dma_len(host->sg); - host->buffer = sg_virt(host->sg); - if (host->buffer_bytes_left > host->bytes_left) - host->buffer_bytes_left = host->bytes_left; -} - static void davinci_fifo_data_trans(struct mmc_davinci_host *host, unsigned int n) { + struct sg_mapping_iter *sgm = &host->sg_miter; + size_t sglen; u8 *p; unsigned int i; - if (host->buffer_bytes_left == 0) { - host->sg = sg_next(host->data->sg); - mmc_davinci_sg_to_buf(host); + /* + * By adjusting sgm->consumed this will give a pointer to the + * current index into the sgm. + */ + if (!sg_miter_next(sgm)) { + dev_err(mmc_dev(host->mmc), "ran out of sglist prematurely\n"); + return; } - - p = host->buffer; - if (n > host->buffer_bytes_left) - n = host->buffer_bytes_left; - host->buffer_bytes_left -= n; - host->bytes_left -= n; + p = sgm->addr; + sglen = sgm->length; /* NOTE: we never transfer more than rw_threshold bytes * to/from the fifo here; there's no I/O overlap. @@ -261,7 +249,9 @@ static void davinci_fifo_data_trans(struct mmc_davinci_host *host, p = p + (n & 3); } } - host->buffer = p; + + sgm->consumed = n; + host->bytes_left -= n; } static void mmc_davinci_start_command(struct mmc_davinci_host *host, @@ -517,6 +507,7 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req) int fifo_lev = (rw_threshold == 32) ? MMCFIFOCTL_FIFOLEV : 0; int timeout; struct mmc_data *data = req->data; + unsigned int flags = SG_MITER_ATOMIC; /* Used from IRQ */ if (host->version == MMC_CTLR_VERSION_2) fifo_lev = (rw_threshold == 64) ? MMCFIFOCTL_FIFOLEV : 0; @@ -545,12 +536,14 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req) /* Configure the FIFO */ if (data->flags & MMC_DATA_WRITE) { + flags |= SG_MITER_FROM_SG; host->data_dir = DAVINCI_MMC_DATADIR_WRITE; writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR | MMCFIFOCTL_FIFORST, host->base + DAVINCI_MMCFIFOCTL); writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR, host->base + DAVINCI_MMCFIFOCTL); } else { + flags |= SG_MITER_TO_SG; host->data_dir = DAVINCI_MMC_DATADIR_READ; writel(fifo_lev | MMCFIFOCTL_FIFODIR_RD | MMCFIFOCTL_FIFORST, host->base + DAVINCI_MMCFIFOCTL); @@ -558,7 +551,6 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req) host->base + DAVINCI_MMCFIFOCTL); } - host->buffer = NULL; host->bytes_left = data->blocks * data->blksz; /* For now we try to use DMA whenever we won't need partial FIFO @@ -576,8 +568,7 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req) } else { /* Revert to CPU Copy */ host->sg_len = data->sg_len; - host->sg = host->data->sg; - mmc_davinci_sg_to_buf(host); + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); } } @@ -843,6 +834,8 @@ davinci_abort_data(struct mmc_davinci_host *host, struct mmc_data *data) { mmc_davinci_reset_ctrl(host, 1); mmc_davinci_reset_ctrl(host, 0); + if (!host->do_dma) + sg_miter_stop(&host->sg_miter); } static irqreturn_t mmc_davinci_sdio_irq(int irq, void *dev_id) @@ -919,11 +912,13 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id) if (qstatus & MMCST0_DATDNE) { /* All blocks sent/received, and CRC checks passed */ if (data != NULL) { - if ((host->do_dma == 0) && (host->bytes_left > 0)) { - /* if datasize < rw_threshold - * no RX ints are generated - */ - davinci_fifo_data_trans(host, host->bytes_left); + if (!host->do_dma) { + if (host->bytes_left > 0) + /* if datasize < rw_threshold + * no RX ints are generated + */ + davinci_fifo_data_trans(host, host->bytes_left); + sg_miter_stop(&host->sg_miter); } end_transfer = 1; data->bytes_xfered = data->blocks * data->blksz; From bc169ad24308ff3304966af10bea2df9219a0189 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 27 Jan 2024 01:19:49 +0100 Subject: [PATCH 10/44] mmc: moxart-mmc: Factor out moxart_use_dma() helper The same code is in two places and we will add a third place. Break this out into its own function. Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20240127-mmc-proper-kmap-v2-2-d8e732aa97d1@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/moxart-mmc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index 5cfdd3a86e54..d12d7d79b19c 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -254,6 +254,11 @@ static void moxart_dma_complete(void *param) complete(&host->dma_complete); } +static bool moxart_use_dma(struct moxart_host *host) +{ + return (host->data_len > host->fifo_width) && host->have_dma; +} + static void moxart_transfer_dma(struct mmc_data *data, struct moxart_host *host) { u32 len, dir_slave; @@ -375,7 +380,7 @@ static void moxart_prepare_data(struct moxart_host *host) if (data->flags & MMC_DATA_WRITE) datactrl |= DCR_DATA_WRITE; - if ((host->data_len > host->fifo_width) && host->have_dma) + if (moxart_use_dma(host)) datactrl |= DCR_DMA_EN; writel(DCR_DATA_FIFO_RESET, host->base + REG_DATA_CONTROL); @@ -407,7 +412,7 @@ static void moxart_request(struct mmc_host *mmc, struct mmc_request *mrq) moxart_send_command(host, host->mrq->cmd); if (mrq->cmd->data) { - if ((host->data_len > host->fifo_width) && host->have_dma) { + if (moxart_use_dma(host)) { writel(CARD_CHANGE, host->base + REG_INTERRUPT_MASK); From 54fd8cd61799673622a0c683e870ee584c713aa7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 27 Jan 2024 01:19:50 +0100 Subject: [PATCH 11/44] mmc: moxart-mmc: Fix accounting in DMA transfer The whole scatterlist chain is submitted to the DMA engine, but the code is written to just account for the length of the first sg entry. When the DMA transfer is finished, all the data in the request has been transferred, account for this instead. This only works because the moxart_request() function isn't checking that all data was transferred and will unconditionally issue mmc_request_done() after returning successfully from moxart_transfer_dma(). Keep the assignment of accounted bytes in .bytes_xfered but move it after the completion where we know it has actually happened. Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20240127-mmc-proper-kmap-v2-3-d8e732aa97d1@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/moxart-mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index d12d7d79b19c..8ede4ce93271 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -296,11 +296,11 @@ static void moxart_transfer_dma(struct mmc_data *data, struct moxart_host *host) dma_async_issue_pending(dma_chan); } - data->bytes_xfered += host->data_remain; - wait_for_completion_interruptible_timeout(&host->dma_complete, host->timeout); + data->bytes_xfered = host->data_len; + dma_unmap_sg(dma_chan->device->dev, data->sg, data->sg_len, mmc_get_dma_dir(data)); From 3ee0e7c3e67cab83ffbbe7707b43df8d41c9fe47 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 27 Jan 2024 01:19:51 +0100 Subject: [PATCH 12/44] mmc: moxart-mmc: Use sg_miter for PIO Use the scatterlist memory iterator instead of just dereferencing virtual memory using sg_virt(). This make highmem references work properly. Suggested-by: Christoph Hellwig Link: https://lore.kernel.org/linux-mmc/20240122073423.GA25859@lst.de/ Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20240127-mmc-proper-kmap-v2-4-d8e732aa97d1@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/moxart-mmc.c | 77 ++++++++++++++++------------------- 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index 8ede4ce93271..b88d6dec209f 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -131,12 +131,10 @@ struct moxart_host { struct dma_async_tx_descriptor *tx_desc; struct mmc_host *mmc; struct mmc_request *mrq; - struct scatterlist *cur_sg; struct completion dma_complete; struct completion pio_complete; - u32 num_sg; - u32 data_remain; + struct sg_mapping_iter sg_miter; u32 data_len; u32 fifo_width; u32 timeout; @@ -148,35 +146,6 @@ struct moxart_host { bool is_removed; }; -static inline void moxart_init_sg(struct moxart_host *host, - struct mmc_data *data) -{ - host->cur_sg = data->sg; - host->num_sg = data->sg_len; - host->data_remain = host->cur_sg->length; - - if (host->data_remain > host->data_len) - host->data_remain = host->data_len; -} - -static inline int moxart_next_sg(struct moxart_host *host) -{ - int remain; - struct mmc_data *data = host->mrq->cmd->data; - - host->cur_sg++; - host->num_sg--; - - if (host->num_sg > 0) { - host->data_remain = host->cur_sg->length; - remain = host->data_len - data->bytes_xfered; - if (remain > 0 && remain < host->data_remain) - host->data_remain = remain; - } - - return host->num_sg; -} - static int moxart_wait_for_status(struct moxart_host *host, u32 mask, u32 *status) { @@ -309,14 +278,28 @@ static void moxart_transfer_dma(struct mmc_data *data, struct moxart_host *host) static void moxart_transfer_pio(struct moxart_host *host) { + struct sg_mapping_iter *sgm = &host->sg_miter; struct mmc_data *data = host->mrq->cmd->data; u32 *sgp, len = 0, remain, status; if (host->data_len == data->bytes_xfered) return; - sgp = sg_virt(host->cur_sg); - remain = host->data_remain; + /* + * By updating sgm->consumes this will get a proper pointer into the + * buffer at any time. + */ + if (!sg_miter_next(sgm)) { + /* This shold not happen */ + dev_err(mmc_dev(host->mmc), "ran out of scatterlist prematurely\n"); + data->error = -EINVAL; + complete(&host->pio_complete); + return; + } + sgp = sgm->addr; + remain = sgm->length; + if (remain > host->data_len) + remain = host->data_len; if (data->flags & MMC_DATA_WRITE) { while (remain > 0) { @@ -331,6 +314,7 @@ static void moxart_transfer_pio(struct moxart_host *host) sgp++; len += 4; } + sgm->consumed += len; remain -= len; } @@ -347,22 +331,22 @@ static void moxart_transfer_pio(struct moxart_host *host) sgp++; len += 4; } + sgm->consumed += len; remain -= len; } } - data->bytes_xfered += host->data_remain - remain; - host->data_remain = remain; - - if (host->data_len != data->bytes_xfered) - moxart_next_sg(host); - else + data->bytes_xfered += sgm->consumed; + if (host->data_len == data->bytes_xfered) { complete(&host->pio_complete); + return; + } } static void moxart_prepare_data(struct moxart_host *host) { struct mmc_data *data = host->mrq->cmd->data; + unsigned int flags = SG_MITER_ATOMIC; /* Used from IRQ */ u32 datactrl; int blksz_bits; @@ -373,15 +357,19 @@ static void moxart_prepare_data(struct moxart_host *host) blksz_bits = ffs(data->blksz) - 1; BUG_ON(1 << blksz_bits != data->blksz); - moxart_init_sg(host, data); - datactrl = DCR_DATA_EN | (blksz_bits & DCR_BLK_SIZE); - if (data->flags & MMC_DATA_WRITE) + if (data->flags & MMC_DATA_WRITE) { + flags |= SG_MITER_FROM_SG; datactrl |= DCR_DATA_WRITE; + } else { + flags |= SG_MITER_TO_SG; + } if (moxart_use_dma(host)) datactrl |= DCR_DMA_EN; + else + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); writel(DCR_DATA_FIFO_RESET, host->base + REG_DATA_CONTROL); writel(MASK_DATA | FIFO_URUN | FIFO_ORUN, host->base + REG_CLEAR); @@ -454,6 +442,9 @@ static void moxart_request(struct mmc_host *mmc, struct mmc_request *mrq) } request_done: + if (!moxart_use_dma(host)) + sg_miter_stop(&host->sg_miter); + spin_unlock_irqrestore(&host->lock, flags); mmc_request_done(host->mmc, mrq); } From 2761822c00e8c271f10a10affdbd4917d900d7ea Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 27 Jan 2024 01:19:52 +0100 Subject: [PATCH 13/44] mmc: mvsdio: Use sg_miter for PIO Use the scatterlist memory iterator instead of just dereferencing virtual memory using sg_virt(). This make highmem references work properly. This driver also has a bug in the PIO sglist handling that is fixed as part of the patch: it does not travers the list of scatterbuffers: it will just process the first item in the list. This is fixed by augmenting the logic such that we do not process more than one sgitem per IRQ instead of counting down potentially the whole length of the request. We can suspect that the PIO path is quite untested. Suggested-by: Christoph Hellwig Link: https://lore.kernel.org/linux-mmc/20240122073423.GA25859@lst.de/ Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20240127-mmc-proper-kmap-v2-5-d8e732aa97d1@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/mvsdio.c | 71 +++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index ca01b7d204ba..af7f21888e27 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -38,8 +38,9 @@ struct mvsd_host { unsigned int xfer_mode; unsigned int intr_en; unsigned int ctrl; + bool use_pio; + struct sg_mapping_iter sg_miter; unsigned int pio_size; - void *pio_ptr; unsigned int sg_frags; unsigned int ns_per_clk; unsigned int clock; @@ -114,11 +115,18 @@ static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data) * data when the buffer is not aligned on a 64 byte * boundary. */ + unsigned int miter_flags = SG_MITER_ATOMIC; /* Used from IRQ */ + + if (data->flags & MMC_DATA_READ) + miter_flags |= SG_MITER_TO_SG; + else + miter_flags |= SG_MITER_FROM_SG; + host->pio_size = data->blocks * data->blksz; - host->pio_ptr = sg_virt(data->sg); + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, miter_flags); if (!nodma) - dev_dbg(host->dev, "fallback to PIO for data at 0x%p size %d\n", - host->pio_ptr, host->pio_size); + dev_dbg(host->dev, "fallback to PIO for data\n"); + host->use_pio = true; return 1; } else { dma_addr_t phys_addr; @@ -129,6 +137,7 @@ static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data) phys_addr = sg_dma_address(data->sg); mvsd_write(MVSD_SYS_ADDR_LOW, (u32)phys_addr & 0xffff); mvsd_write(MVSD_SYS_ADDR_HI, (u32)phys_addr >> 16); + host->use_pio = false; return 0; } } @@ -288,8 +297,8 @@ static u32 mvsd_finish_data(struct mvsd_host *host, struct mmc_data *data, { void __iomem *iobase = host->base; - if (host->pio_ptr) { - host->pio_ptr = NULL; + if (host->use_pio) { + sg_miter_stop(&host->sg_miter); host->pio_size = 0; } else { dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_frags, @@ -344,9 +353,12 @@ static u32 mvsd_finish_data(struct mvsd_host *host, struct mmc_data *data, static irqreturn_t mvsd_irq(int irq, void *dev) { struct mvsd_host *host = dev; + struct sg_mapping_iter *sgm = &host->sg_miter; void __iomem *iobase = host->base; u32 intr_status, intr_done_mask; int irq_handled = 0; + u16 *p; + int s; intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); dev_dbg(host->dev, "intr 0x%04x intr_en 0x%04x hw_state 0x%04x\n", @@ -370,15 +382,36 @@ static irqreturn_t mvsd_irq(int irq, void *dev) spin_lock(&host->lock); /* PIO handling, if needed. Messy business... */ - if (host->pio_size && + if (host->use_pio) { + /* + * As we set sgm->consumed this always gives a valid buffer + * position. + */ + if (!sg_miter_next(sgm)) { + /* This should not happen */ + dev_err(host->dev, "ran out of scatter segments\n"); + spin_unlock(&host->lock); + host->intr_en &= + ~(MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W | + MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W); + mvsd_write(MVSD_NOR_INTR_EN, host->intr_en); + return IRQ_HANDLED; + } + p = sgm->addr; + s = sgm->length; + if (s > host->pio_size) + s = host->pio_size; + } + + if (host->use_pio && (intr_status & host->intr_en & (MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W))) { - u16 *p = host->pio_ptr; - int s = host->pio_size; + while (s >= 32 && (intr_status & MVSD_NOR_RX_FIFO_8W)) { readsw(iobase + MVSD_FIFO, p, 16); p += 16; s -= 32; + sgm->consumed += 32; intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); } /* @@ -391,6 +424,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev) put_unaligned(mvsd_read(MVSD_FIFO), p++); put_unaligned(mvsd_read(MVSD_FIFO), p++); s -= 4; + sgm->consumed += 4; intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); } if (s && s < 4 && (intr_status & MVSD_NOR_RX_READY)) { @@ -398,10 +432,13 @@ static irqreturn_t mvsd_irq(int irq, void *dev) val[0] = mvsd_read(MVSD_FIFO); val[1] = mvsd_read(MVSD_FIFO); memcpy(p, ((void *)&val) + 4 - s, s); + sgm->consumed += s; s = 0; intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); } - if (s == 0) { + /* PIO transfer done */ + host->pio_size -= sgm->consumed; + if (host->pio_size == 0) { host->intr_en &= ~(MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W); mvsd_write(MVSD_NOR_INTR_EN, host->intr_en); @@ -413,14 +450,10 @@ static irqreturn_t mvsd_irq(int irq, void *dev) } dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n", s, intr_status, mvsd_read(MVSD_HW_STATE)); - host->pio_ptr = p; - host->pio_size = s; irq_handled = 1; - } else if (host->pio_size && + } else if (host->use_pio && (intr_status & host->intr_en & (MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W))) { - u16 *p = host->pio_ptr; - int s = host->pio_size; /* * The TX_FIFO_8W bit is unreliable. When set, bursting * 16 halfwords all at once in the FIFO drops data. Actually @@ -431,6 +464,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev) mvsd_write(MVSD_FIFO, get_unaligned(p++)); mvsd_write(MVSD_FIFO, get_unaligned(p++)); s -= 4; + sgm->consumed += 4; intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); } if (s < 4) { @@ -439,10 +473,13 @@ static irqreturn_t mvsd_irq(int irq, void *dev) memcpy(((void *)&val) + 4 - s, p, s); mvsd_write(MVSD_FIFO, val[0]); mvsd_write(MVSD_FIFO, val[1]); + sgm->consumed += s; s = 0; intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); } - if (s == 0) { + /* PIO transfer done */ + host->pio_size -= sgm->consumed; + if (host->pio_size == 0) { host->intr_en &= ~(MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W); mvsd_write(MVSD_NOR_INTR_EN, host->intr_en); @@ -450,8 +487,6 @@ static irqreturn_t mvsd_irq(int irq, void *dev) } dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n", s, intr_status, mvsd_read(MVSD_HW_STATE)); - host->pio_ptr = p; - host->pio_size = s; irq_handled = 1; } From 12658af54f2ed10d812dcd0a769d62cb9e9291e5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 27 Jan 2024 01:19:53 +0100 Subject: [PATCH 14/44] mmc: mxcmmc: Use sg_miter for PIO Use the scatterlist memory iterator instead of just dereferencing virtual memory using sg_virt(). This make highmem references work properly. Since this driver is using a worker, no atomic trickery is needed. Suggested-by: Christoph Hellwig Link: https://lore.kernel.org/linux-mmc/20240122073423.GA25859@lst.de/ Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20240127-mmc-proper-kmap-v2-6-d8e732aa97d1@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/mxcmmc.c | 53 ++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index 5b3ab0e20505..1edf65291354 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -266,11 +266,18 @@ static inline void buffer_swap32(u32 *buf, int len) static void mxcmci_swap_buffers(struct mmc_data *data) { - struct scatterlist *sg; - int i; + struct sg_mapping_iter sgm; + u32 *buf; - for_each_sg(data->sg, sg, data->sg_len, i) - buffer_swap32(sg_virt(sg), sg->length); + sg_miter_start(&sgm, data->sg, data->sg_len, + SG_MITER_TO_SG | SG_MITER_FROM_SG); + + while (sg_miter_next(&sgm)) { + buf = sgm.addr; + buffer_swap32(buf, sgm.length); + } + + sg_miter_stop(&sgm); } #else static inline void mxcmci_swap_buffers(struct mmc_data *data) {} @@ -526,10 +533,9 @@ static int mxcmci_poll_status(struct mxcmci_host *host, u32 mask) } while (1); } -static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes) +static int mxcmci_pull(struct mxcmci_host *host, u32 *buf, int bytes) { unsigned int stat; - u32 *buf = _buf; while (bytes > 3) { stat = mxcmci_poll_status(host, @@ -555,10 +561,9 @@ static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes) return 0; } -static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes) +static int mxcmci_push(struct mxcmci_host *host, u32 *buf, int bytes) { unsigned int stat; - u32 *buf = _buf; while (bytes > 3) { stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY); @@ -586,31 +591,39 @@ static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes) static int mxcmci_transfer_data(struct mxcmci_host *host) { struct mmc_data *data = host->req->data; - struct scatterlist *sg; - int stat, i; + struct sg_mapping_iter sgm; + int stat; + u32 *buf; host->data = data; host->datasize = 0; + sg_miter_start(&sgm, data->sg, data->sg_len, + (data->flags & MMC_DATA_READ) ? SG_MITER_TO_SG : SG_MITER_FROM_SG); if (data->flags & MMC_DATA_READ) { - for_each_sg(data->sg, sg, data->sg_len, i) { - stat = mxcmci_pull(host, sg_virt(sg), sg->length); + while (sg_miter_next(&sgm)) { + buf = sgm.addr; + stat = mxcmci_pull(host, buf, sgm.length); if (stat) - return stat; - host->datasize += sg->length; + goto transfer_error; + host->datasize += sgm.length; } } else { - for_each_sg(data->sg, sg, data->sg_len, i) { - stat = mxcmci_push(host, sg_virt(sg), sg->length); + while (sg_miter_next(&sgm)) { + buf = sgm.addr; + stat = mxcmci_push(host, buf, sgm.length); if (stat) - return stat; - host->datasize += sg->length; + goto transfer_error; + host->datasize += sgm.length; } stat = mxcmci_poll_status(host, STATUS_WRITE_OP_DONE); if (stat) - return stat; + goto transfer_error; } - return 0; + +transfer_error: + sg_miter_stop(&sgm); + return stat; } static void mxcmci_datawork(struct work_struct *work) From 68cfdeb4064e049647395271eec3c0d987141674 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 27 Jan 2024 01:19:54 +0100 Subject: [PATCH 15/44] mmc: omap: Use sg_miter for PIO Use the scatterlist memory iterator instead of just dereferencing virtual memory using sg_virt(). This make highmem references work properly. Suggested-by: Christoph Hellwig Link: https://lore.kernel.org/linux-mmc/20240122073423.GA25859@lst.de/ Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20240127-mmc-proper-kmap-v2-7-d8e732aa97d1@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap.c | 53 +++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 9fb8995b43a1..088f8ed4fdc4 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -148,10 +148,8 @@ struct mmc_omap_host { struct work_struct send_stop_work; struct mmc_data *stop_data; + struct sg_mapping_iter sg_miter; unsigned int sg_len; - int sg_idx; - u16 * buffer; - u32 buffer_bytes_left; u32 total_bytes_left; unsigned features; @@ -456,6 +454,8 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) { if (host->dma_in_use) mmc_omap_release_dma(host, data, data->error); + else + sg_miter_stop(&host->sg_miter); host->data = NULL; host->sg_len = 0; @@ -651,19 +651,6 @@ mmc_omap_cmd_timer(struct timer_list *t) spin_unlock_irqrestore(&host->slot_lock, flags); } -/* PIO only */ -static void -mmc_omap_sg_to_buf(struct mmc_omap_host *host) -{ - struct scatterlist *sg; - - sg = host->data->sg + host->sg_idx; - host->buffer_bytes_left = sg->length; - host->buffer = sg_virt(sg); - if (host->buffer_bytes_left > host->total_bytes_left) - host->buffer_bytes_left = host->total_bytes_left; -} - static void mmc_omap_clk_timer(struct timer_list *t) { @@ -676,33 +663,37 @@ mmc_omap_clk_timer(struct timer_list *t) static void mmc_omap_xfer_data(struct mmc_omap_host *host, int write) { + struct sg_mapping_iter *sgm = &host->sg_miter; int n, nwords; + u16 *buffer; - if (host->buffer_bytes_left == 0) { - host->sg_idx++; - BUG_ON(host->sg_idx == host->sg_len); - mmc_omap_sg_to_buf(host); + if (!sg_miter_next(sgm)) { + /* This should not happen */ + dev_err(mmc_dev(host->mmc), "ran out of scatterlist prematurely\n"); + return; } + buffer = sgm->addr; + n = 64; - if (n > host->buffer_bytes_left) - n = host->buffer_bytes_left; + if (n > sgm->length) + n = sgm->length; + if (n > host->total_bytes_left) + n = host->total_bytes_left; /* Round up to handle odd number of bytes to transfer */ nwords = DIV_ROUND_UP(n, 2); - host->buffer_bytes_left -= n; + sgm->consumed = n; host->total_bytes_left -= n; host->data->bytes_xfered += n; if (write) { __raw_writesw(host->virt_base + OMAP_MMC_REG(host, DATA), - host->buffer, nwords); + buffer, nwords); } else { __raw_readsw(host->virt_base + OMAP_MMC_REG(host, DATA), - host->buffer, nwords); + buffer, nwords); } - - host->buffer += nwords; } #ifdef CONFIG_MMC_DEBUG @@ -956,6 +947,7 @@ static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_reque static void mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) { + unsigned int miter_flags = SG_MITER_ATOMIC; /* Used from IRQ */ struct mmc_data *data = req->data; int i, use_dma = 1, block_size; struct scatterlist *sg; @@ -990,7 +982,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) } } - host->sg_idx = 0; if (use_dma) { enum dma_data_direction dma_data_dir; struct dma_async_tx_descriptor *tx; @@ -1071,7 +1062,11 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) OMAP_MMC_WRITE(host, BUF, 0x1f1f); host->total_bytes_left = data->blocks * block_size; host->sg_len = sg_len; - mmc_omap_sg_to_buf(host); + if (data->flags & MMC_DATA_READ) + miter_flags |= SG_MITER_TO_SG; + else + miter_flags |= SG_MITER_FROM_SG; + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, miter_flags); host->dma_in_use = 0; } From e8a167b84886ccca21ca4d0d0396f7e172876c92 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 27 Jan 2024 01:19:55 +0100 Subject: [PATCH 16/44] mmc: sdhci-esdhc-mcf: Use sg_miter for swapping Use sg_miter iterator instead of sg_virt() and custom code to loop over the scatterlist. The memory iterator will do bounce buffering if the page happens to be located in high memory, which the driver may or may not be using. Suggested-by: Christoph Hellwig Link: https://lore.kernel.org/linux-mmc/20240122073423.GA25859@lst.de/ Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20240127-mmc-proper-kmap-v2-8-d8e732aa97d1@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-mcf.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-mcf.c b/drivers/mmc/host/sdhci-esdhc-mcf.c index a07f8333cd6b..1909a11fd065 100644 --- a/drivers/mmc/host/sdhci-esdhc-mcf.c +++ b/drivers/mmc/host/sdhci-esdhc-mcf.c @@ -299,9 +299,8 @@ static void esdhc_mcf_pltfm_set_bus_width(struct sdhci_host *host, int width) static void esdhc_mcf_request_done(struct sdhci_host *host, struct mmc_request *mrq) { - struct scatterlist *sg; + struct sg_mapping_iter sgm; u32 *buffer; - int i; if (!mrq->data || !mrq->data->bytes_xfered) goto exit_done; @@ -313,10 +312,13 @@ static void esdhc_mcf_request_done(struct sdhci_host *host, * On mcf5441x there is no hw sdma option/flag to select the dma * transfer endiannes. A swap after the transfer is needed. */ - for_each_sg(mrq->data->sg, sg, mrq->data->sg_len, i) { - buffer = (u32 *)sg_virt(sg); - esdhc_mcf_buffer_swap32(buffer, sg->length); + sg_miter_start(&sgm, mrq->data->sg, mrq->data->sg_len, + SG_MITER_TO_SG | SG_MITER_FROM_SG); + while (sg_miter_next(&sgm)) { + buffer = sgm.addr; + esdhc_mcf_buffer_swap32(buffer, sgm.length); } + sg_miter_stop(&sgm); exit_done: mmc_request_done(host->mmc, mrq); From 27b57277d9ba3a900daffdb91856ba8a5f46b3a7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 27 Jan 2024 01:19:56 +0100 Subject: [PATCH 17/44] mmc: sh_mmcif: Use sg_miter for PIO Use sg_miter iterator instead of sg_virt() and custom code to loop over the scatterlist. The memory iterator will do bounce buffering if the page happens to be located in high memory, which the driver may or may not be using. Suggested-by: Christoph Hellwig Link: https://lore.kernel.org/linux-mmc/20240122073423.GA25859@lst.de/ Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20240127-mmc-proper-kmap-v2-9-d8e732aa97d1@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 102 ++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 39 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 077d711e964e..1ef6e153e5a3 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -227,14 +227,12 @@ struct sh_mmcif_host { bool dying; long timeout; void __iomem *addr; - u32 *pio_ptr; spinlock_t lock; /* protect sh_mmcif_host::state */ enum sh_mmcif_state state; enum sh_mmcif_wait_for wait_for; struct delayed_work timeout_work; size_t blocksize; - int sg_idx; - int sg_blkidx; + struct sg_mapping_iter sg_miter; bool power; bool ccs_enable; /* Command Completion Signal support */ bool clk_ctrl2_enable; @@ -600,32 +598,17 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host) return ret; } -static bool sh_mmcif_next_block(struct sh_mmcif_host *host, u32 *p) -{ - struct mmc_data *data = host->mrq->data; - - host->sg_blkidx += host->blocksize; - - /* data->sg->length must be a multiple of host->blocksize? */ - BUG_ON(host->sg_blkidx > data->sg->length); - - if (host->sg_blkidx == data->sg->length) { - host->sg_blkidx = 0; - if (++host->sg_idx < data->sg_len) - host->pio_ptr = sg_virt(++data->sg); - } else { - host->pio_ptr = p; - } - - return host->sg_idx != data->sg_len; -} - static void sh_mmcif_single_read(struct sh_mmcif_host *host, struct mmc_request *mrq) { + struct mmc_data *data = mrq->data; + host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) & BLOCK_SIZE_MASK) + 3; + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, + SG_MITER_ATOMIC | SG_MITER_TO_SG); + host->wait_for = MMCIF_WAIT_FOR_READ; /* buf read enable */ @@ -634,20 +617,32 @@ static void sh_mmcif_single_read(struct sh_mmcif_host *host, static bool sh_mmcif_read_block(struct sh_mmcif_host *host) { + struct sg_mapping_iter *sgm = &host->sg_miter; struct device *dev = sh_mmcif_host_to_dev(host); struct mmc_data *data = host->mrq->data; - u32 *p = sg_virt(data->sg); + u32 *p; int i; if (host->sd_error) { + sg_miter_stop(sgm); data->error = sh_mmcif_error_manage(host); dev_dbg(dev, "%s(): %d\n", __func__, data->error); return false; } + if (!sg_miter_next(sgm)) { + /* This should not happen on single blocks */ + sg_miter_stop(sgm); + return false; + } + + p = sgm->addr; + for (i = 0; i < host->blocksize / 4; i++) *p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA); + sg_miter_stop(&host->sg_miter); + /* buffer read end */ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFRE); host->wait_for = MMCIF_WAIT_FOR_READ_END; @@ -666,34 +661,40 @@ static void sh_mmcif_multi_read(struct sh_mmcif_host *host, host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) & BLOCK_SIZE_MASK; + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, + SG_MITER_ATOMIC | SG_MITER_TO_SG); + host->wait_for = MMCIF_WAIT_FOR_MREAD; - host->sg_idx = 0; - host->sg_blkidx = 0; - host->pio_ptr = sg_virt(data->sg); sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); } static bool sh_mmcif_mread_block(struct sh_mmcif_host *host) { + struct sg_mapping_iter *sgm = &host->sg_miter; struct device *dev = sh_mmcif_host_to_dev(host); struct mmc_data *data = host->mrq->data; - u32 *p = host->pio_ptr; + u32 *p; int i; if (host->sd_error) { + sg_miter_stop(sgm); data->error = sh_mmcif_error_manage(host); dev_dbg(dev, "%s(): %d\n", __func__, data->error); return false; } - BUG_ON(!data->sg->length); + if (!sg_miter_next(sgm)) { + sg_miter_stop(sgm); + return false; + } + + p = sgm->addr; for (i = 0; i < host->blocksize / 4; i++) *p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA); - if (!sh_mmcif_next_block(host, p)) - return false; + sgm->consumed = host->blocksize; sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); @@ -703,9 +704,14 @@ static bool sh_mmcif_mread_block(struct sh_mmcif_host *host) static void sh_mmcif_single_write(struct sh_mmcif_host *host, struct mmc_request *mrq) { + struct mmc_data *data = mrq->data; + host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) & BLOCK_SIZE_MASK) + 3; + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, + SG_MITER_ATOMIC | SG_MITER_FROM_SG); + host->wait_for = MMCIF_WAIT_FOR_WRITE; /* buf write enable */ @@ -714,20 +720,32 @@ static void sh_mmcif_single_write(struct sh_mmcif_host *host, static bool sh_mmcif_write_block(struct sh_mmcif_host *host) { + struct sg_mapping_iter *sgm = &host->sg_miter; struct device *dev = sh_mmcif_host_to_dev(host); struct mmc_data *data = host->mrq->data; - u32 *p = sg_virt(data->sg); + u32 *p; int i; if (host->sd_error) { + sg_miter_stop(sgm); data->error = sh_mmcif_error_manage(host); dev_dbg(dev, "%s(): %d\n", __func__, data->error); return false; } + if (!sg_miter_next(sgm)) { + /* This should not happen on single blocks */ + sg_miter_stop(sgm); + return false; + } + + p = sgm->addr; + for (i = 0; i < host->blocksize / 4; i++) sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++); + sg_miter_stop(&host->sg_miter); + /* buffer write end */ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MDTRANE); host->wait_for = MMCIF_WAIT_FOR_WRITE_END; @@ -746,34 +764,40 @@ static void sh_mmcif_multi_write(struct sh_mmcif_host *host, host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) & BLOCK_SIZE_MASK; + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, + SG_MITER_ATOMIC | SG_MITER_FROM_SG); + host->wait_for = MMCIF_WAIT_FOR_MWRITE; - host->sg_idx = 0; - host->sg_blkidx = 0; - host->pio_ptr = sg_virt(data->sg); sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); } static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host) { + struct sg_mapping_iter *sgm = &host->sg_miter; struct device *dev = sh_mmcif_host_to_dev(host); struct mmc_data *data = host->mrq->data; - u32 *p = host->pio_ptr; + u32 *p; int i; if (host->sd_error) { + sg_miter_stop(sgm); data->error = sh_mmcif_error_manage(host); dev_dbg(dev, "%s(): %d\n", __func__, data->error); return false; } - BUG_ON(!data->sg->length); + if (!sg_miter_next(sgm)) { + sg_miter_stop(sgm); + return false; + } + + p = sgm->addr; for (i = 0; i < host->blocksize / 4; i++) sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++); - if (!sh_mmcif_next_block(host, p)) - return false; + sgm->consumed = host->blocksize; sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); From 59fbb55a297d0cf6e10c0a27fb172aada9902bc2 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 5 Feb 2024 19:13:10 +0000 Subject: [PATCH 18/44] mmc: wbsd: remove redundant assignment to variable id The variable id is being initialized with a value that is never read, it is being re-assigned later on. The initialization is redundant and can be removed. Cleans up clang scan build warning: drivers/mmc/host/wbsd.c:1287:4: warning: Value stored to 'id' is never read [deadcode.DeadStores] Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20240205191310.1848561-1-colin.i.king@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/wbsd.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index 001a468bc149..f0562f712d98 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -1284,8 +1284,6 @@ static int wbsd_scan(struct wbsd_host *host) continue; for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) { - id = 0xFFFF; - host->config = config_ports[i]; host->unlock_code = unlock_codes[j]; From d6e5288c8bf4f9bfbb9d3630d8605f45f15a525f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 8 Feb 2024 21:21:37 +0100 Subject: [PATCH 19/44] mmc: renesas_sdhi: use typedef for dma_filter_fn Use existing typedef for dma_filter_fn to avoid duplicating type definition. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Wolfram Sang Link: https://lore.kernel.org/r/20240208202137.630281-1-krzysztof.kozlowski@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h index c1fb9740eab0..586f94d4dbfd 100644 --- a/drivers/mmc/host/renesas_sdhi.h +++ b/drivers/mmc/host/renesas_sdhi.h @@ -9,6 +9,7 @@ #ifndef RENESAS_SDHI_H #define RENESAS_SDHI_H +#include #include #include "tmio_mmc.h" @@ -63,7 +64,7 @@ struct renesas_sdhi_of_data_with_quirks { struct renesas_sdhi_dma { unsigned long end_flags; enum dma_slave_buswidth dma_buswidth; - bool (*filter)(struct dma_chan *chan, void *arg); + dma_filter_fn filter; void (*enable)(struct tmio_mmc_host *host, bool enable); struct completion dma_dataend; struct tasklet_struct dma_complete; From 02c801376bdd3dc9bcfae46ea9db382611f7612a Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Fri, 9 Feb 2024 09:09:34 -0700 Subject: [PATCH 20/44] MAINTAINERS: Update bouncing @codeaurora addresses for EMMC CMDQ The @codeaurora email domain's servers have been decommissioned for a long while now, and any emails addressed there will bounce. Asutosh has an entry in .mailmap pointing to a new address, but MAINTAINERS still lists an old @codeaurora address. Update MAINTAINERS to match .mailmap for anyone reading the file directly. Ritesh appears to have changed jobs, but looks to be still active in the community. Update Ritesh's address to the one used in recient community postings. Also Ritesh has indicated their entry should be changed from Maintainer (M:) to Reviewer (R:) so make that update while we are making changes to the entry. Signed-off-by: Jeffrey Hugo Acked-by: Ritesh Harjani Link: https://lore.kernel.org/r/20240209160934.3866475-1-quic_jhugo@quicinc.com Signed-off-by: Ulf Hansson --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 73d898383e51..137582fa1012 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7785,8 +7785,8 @@ F: drivers/media/usb/em28xx/ EMMC CMDQ HOST CONTROLLER INTERFACE (CQHCI) DRIVER M: Adrian Hunter -M: Ritesh Harjani -M: Asutosh Das +M: Asutosh Das +R: Ritesh Harjani L: linux-mmc@vger.kernel.org S: Supported F: drivers/mmc/host/cqhci* From 4aee14527e4fd92d4a7487c430b54d099b99b4d1 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 14 Feb 2024 13:59:57 +0100 Subject: [PATCH 21/44] dt-bindings: mmc: renesas,sdhi: Document R-Car V4M support Document support for the SD Card/MMC Interface in the Renesas R-Car V4M (R8A779H0) SoC. Signed-off-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Acked-by: Conor Dooley Link: https://lore.kernel.org/r/fffc5a0a73c4cc8e8d7c5d93679531cc24e006ca.1707915511.git.geert+renesas@glider.be Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml index f7a4c6bc70f6..29f2400247eb 100644 --- a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml +++ b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml @@ -67,6 +67,7 @@ properties: - renesas,sdhi-r8a779a0 # R-Car V3U - renesas,sdhi-r8a779f0 # R-Car S4-8 - renesas,sdhi-r8a779g0 # R-Car V4H + - renesas,sdhi-r8a779h0 # R-Car V4M - const: renesas,rcar-gen4-sdhi # R-Car Gen4 reg: From 25e69172db8a31ef8564ce1cf755ac5cb8374daa Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 15 Feb 2024 14:28:18 +0100 Subject: [PATCH 22/44] mmc: davinci_mmc: Drop dangling variable The sg_miter conversion left a dangling unused variable. Drop it. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202402142042.vg0lnLdb-lkp@intel.com/ Fixes: ed01d210fd91 ("mmc: davinci_mmc: Use sg_miter for PIO") Signed-off-by: Linus Walleij Acked-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20240215-mmc-fix-davinci-v1-1-a593678ca7bf@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/davinci_mmc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index c46577305138..8bd938919687 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -211,7 +211,6 @@ static void davinci_fifo_data_trans(struct mmc_davinci_host *host, unsigned int n) { struct sg_mapping_iter *sgm = &host->sg_miter; - size_t sglen; u8 *p; unsigned int i; @@ -224,7 +223,6 @@ static void davinci_fifo_data_trans(struct mmc_davinci_host *host, return; } p = sgm->addr; - sglen = sgm->length; /* NOTE: we never transfer more than rw_threshold bytes * to/from the fifo here; there's no I/O overlap. From 4c5e3efae66d04af3fc745621acaa47713d853a2 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 17 Feb 2024 11:01:59 +0100 Subject: [PATCH 23/44] mmc: meson-mx-sdhc: Use devm_clk_hw_get_clk() for clock retrieval Now that devm_clk_hw_get_clk() has been available for a while we can resolve an older TODO where this API did not exist yet. No functional changes intended. Signed-off-by: Martin Blumenstingl Link: https://lore.kernel.org/r/20240217100200.1494980-2-martin.blumenstingl@googlemail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/meson-mx-sdhc-clkc.c | 43 ++++++++++++++------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/drivers/mmc/host/meson-mx-sdhc-clkc.c b/drivers/mmc/host/meson-mx-sdhc-clkc.c index 19200b7079a6..cbd17a596cd2 100644 --- a/drivers/mmc/host/meson-mx-sdhc-clkc.c +++ b/drivers/mmc/host/meson-mx-sdhc-clkc.c @@ -71,12 +71,23 @@ static int meson_mx_sdhc_clk_hw_register(struct device *dev, static int meson_mx_sdhc_gate_clk_hw_register(struct device *dev, const char *name_suffix, struct clk_hw *parent, - struct clk_hw *hw) + struct clk_hw *hw, + struct clk_bulk_data *clk_bulk_data, + u8 bulk_index) { struct clk_parent_data parent_data = { .hw = parent }; + int ret; - return meson_mx_sdhc_clk_hw_register(dev, name_suffix, &parent_data, 1, - &clk_gate_ops, hw); + ret = meson_mx_sdhc_clk_hw_register(dev, name_suffix, &parent_data, 1, + &clk_gate_ops, hw); + if (ret) + return ret; + + clk_bulk_data[bulk_index].clk = devm_clk_hw_get_clk(dev, hw, name_suffix); + if (IS_ERR(clk_bulk_data[bulk_index].clk)) + return PTR_ERR(clk_bulk_data[bulk_index].clk); + + return 0; } int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base, @@ -115,7 +126,8 @@ int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base, clkc_data->mod_clk_en.bit_idx = 15; ret = meson_mx_sdhc_gate_clk_hw_register(dev, "mod_clk_on", &clkc_data->div.hw, - &clkc_data->mod_clk_en.hw); + &clkc_data->mod_clk_en.hw, + clk_bulk_data, 0); if (ret) return ret; @@ -123,7 +135,8 @@ int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base, clkc_data->tx_clk_en.bit_idx = 14; ret = meson_mx_sdhc_gate_clk_hw_register(dev, "tx_clk_on", &clkc_data->div.hw, - &clkc_data->tx_clk_en.hw); + &clkc_data->tx_clk_en.hw, + clk_bulk_data, 1); if (ret) return ret; @@ -131,7 +144,8 @@ int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base, clkc_data->rx_clk_en.bit_idx = 13; ret = meson_mx_sdhc_gate_clk_hw_register(dev, "rx_clk_on", &clkc_data->div.hw, - &clkc_data->rx_clk_en.hw); + &clkc_data->rx_clk_en.hw, + clk_bulk_data, 2); if (ret) return ret; @@ -139,18 +153,7 @@ int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base, clkc_data->sd_clk_en.bit_idx = 12; ret = meson_mx_sdhc_gate_clk_hw_register(dev, "sd_clk_on", &clkc_data->div.hw, - &clkc_data->sd_clk_en.hw); - if (ret) - return ret; - - /* - * TODO: Replace clk_hw.clk with devm_clk_hw_get_clk() once that is - * available. - */ - clk_bulk_data[0].clk = clkc_data->mod_clk_en.hw.clk; - clk_bulk_data[1].clk = clkc_data->sd_clk_en.hw.clk; - clk_bulk_data[2].clk = clkc_data->tx_clk_en.hw.clk; - clk_bulk_data[3].clk = clkc_data->rx_clk_en.hw.clk; - - return 0; + &clkc_data->sd_clk_en.hw, + clk_bulk_data, 3); + return ret; } From c0200efad3306435009fa2b00c02d4af45317a6f Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 17 Feb 2024 11:02:00 +0100 Subject: [PATCH 24/44] mmc: meson-mx-sdhc: Remove .card_hw_reset callback Commit 32f18e596141 ("mmc: improve API to make clear hw_reset callback is for cards") made it clear that the hw_reset callback is intended for resetting the card. Remove the .card_hw_reset callback from the meson-mx-sdhc-mmc driver because it's purpose is to reset the SDHC controller (FIFOs, PHY, DMA interface, ...). While here also rename and change the argument of meson_mx_sdhc_hw_reset so it cannot be called by accident as a replacement for card_hw_reset in the future. Signed-off-by: Martin Blumenstingl Link: https://lore.kernel.org/r/20240217100200.1494980-3-martin.blumenstingl@googlemail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/meson-mx-sdhc-mmc.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/meson-mx-sdhc-mmc.c b/drivers/mmc/host/meson-mx-sdhc-mmc.c index 1ed9731e77ef..31f750301dc1 100644 --- a/drivers/mmc/host/meson-mx-sdhc-mmc.c +++ b/drivers/mmc/host/meson-mx-sdhc-mmc.c @@ -65,10 +65,8 @@ static const struct regmap_config meson_mx_sdhc_regmap_config = { .max_register = MESON_SDHC_CLK2, }; -static void meson_mx_sdhc_hw_reset(struct mmc_host *mmc) +static void meson_mx_sdhc_reset(struct meson_mx_sdhc_host *host) { - struct meson_mx_sdhc_host *host = mmc_priv(mmc); - regmap_write(host->regmap, MESON_SDHC_SRST, MESON_SDHC_SRST_MAIN_CTRL | MESON_SDHC_SRST_RXFIFO | MESON_SDHC_SRST_TXFIFO | MESON_SDHC_SRST_DPHY_RX | MESON_SDHC_SRST_DPHY_TX | @@ -116,7 +114,7 @@ static void meson_mx_sdhc_wait_cmd_ready(struct mmc_host *mmc) dev_warn(mmc_dev(mmc), "Failed to poll for CMD_BUSY while processing CMD%d\n", host->cmd->opcode); - meson_mx_sdhc_hw_reset(mmc); + meson_mx_sdhc_reset(host); } ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, esta, @@ -127,7 +125,7 @@ static void meson_mx_sdhc_wait_cmd_ready(struct mmc_host *mmc) dev_warn(mmc_dev(mmc), "Failed to poll for ESTA[13:11] while processing CMD%d\n", host->cmd->opcode); - meson_mx_sdhc_hw_reset(mmc); + meson_mx_sdhc_reset(host); } } @@ -495,7 +493,6 @@ static int meson_mx_sdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) } static const struct mmc_host_ops meson_mx_sdhc_ops = { - .card_hw_reset = meson_mx_sdhc_hw_reset, .request = meson_mx_sdhc_request, .set_ios = meson_mx_sdhc_set_ios, .card_busy = meson_mx_sdhc_card_busy, @@ -618,7 +615,7 @@ static irqreturn_t meson_mx_sdhc_irq_thread(int irq, void *irq_data) } if (cmd->error == -EIO || cmd->error == -ETIMEDOUT) - meson_mx_sdhc_hw_reset(host->mmc); + meson_mx_sdhc_reset(host); else if (cmd->data) /* * Clear the FIFOs after completing data transfers to prevent @@ -728,7 +725,7 @@ static void meson_mx_sdhc_init_hw(struct mmc_host *mmc) { struct meson_mx_sdhc_host *host = mmc_priv(mmc); - meson_mx_sdhc_hw_reset(mmc); + meson_mx_sdhc_reset(host); regmap_write(host->regmap, MESON_SDHC_CTRL, FIELD_PREP(MESON_SDHC_CTRL_RX_PERIOD, 0xf) | From 849e81817b9b0658a65b668cd65849fa1fb054e0 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sat, 17 Feb 2024 22:42:01 +0800 Subject: [PATCH 25/44] dt-bindings: mmc: sdhci-of-dwcmhsc: Add Sophgo CV1800B and SG2002 support Add compatible value for the dwcmshc controller in Sophgo's CV1800B and SG2002. Signed-off-by: Jisheng Zhang Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240217144202.3808-2-jszhang@kernel.org Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml index 42804d955293..4d3031d9965f 100644 --- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml @@ -19,6 +19,8 @@ properties: - rockchip,rk3568-dwcmshc - rockchip,rk3588-dwcmshc - snps,dwcmshc-sdhci + - sophgo,cv1800b-dwcmshc + - sophgo,sg2002-dwcmshc - thead,th1520-dwcmshc reg: From 017199c2849c3d6d417791ec1cf5521005d663a1 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sat, 17 Feb 2024 22:42:02 +0800 Subject: [PATCH 26/44] mmc: sdhci-of-dwcmshc: Add support for Sophgo CV1800B and SG2002 Add support for the mmc controller in the Sophgo CV1800B and SG2002 with corresponding new compatible strings. Implement custom sdhci_ops. Signed-off-by: Jisheng Zhang Link: https://lore.kernel.org/r/20240217144202.3808-3-jszhang@kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 66 +++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index a1f57af6acfb..ab4b964d4058 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -52,6 +52,20 @@ #define AT_CTRL_SWIN_TH_VAL_MASK GENMASK(31, 24) /* bits [31:24] */ #define AT_CTRL_SWIN_TH_VAL 0x9 /* sampling window threshold */ +/* Sophgo CV18XX specific Registers */ +#define CV18XX_SDHCI_MSHC_CTRL 0x00 +#define CV18XX_EMMC_FUNC_EN BIT(0) +#define CV18XX_LATANCY_1T BIT(1) +#define CV18XX_SDHCI_PHY_TX_RX_DLY 0x40 +#define CV18XX_PHY_TX_DLY_MSK GENMASK(6, 0) +#define CV18XX_PHY_TX_SRC_MSK GENMASK(9, 8) +#define CV18XX_PHY_TX_SRC_INVERT_CLK_TX 0x1 +#define CV18XX_PHY_RX_DLY_MSK GENMASK(22, 16) +#define CV18XX_PHY_RX_SRC_MSK GENMASK(25, 24) +#define CV18XX_PHY_RX_SRC_INVERT_RX_CLK 0x1 +#define CV18XX_SDHCI_PHY_CONFIG 0x4c +#define CV18XX_PHY_TX_BPS BIT(0) + /* Rockchip specific Registers */ #define DWCMSHC_EMMC_DLL_CTRL 0x800 #define DWCMSHC_EMMC_DLL_RXCLK 0x804 @@ -642,6 +656,35 @@ static void th1520_sdhci_reset(struct sdhci_host *host, u8 mask) } } +static void cv18xx_sdhci_reset(struct sdhci_host *host, u8 mask) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + u32 val, emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; + + sdhci_reset(host, mask); + + if ((host->mmc->caps2 & emmc_caps) == emmc_caps) { + val = sdhci_readl(host, priv->vendor_specific_area1 + CV18XX_SDHCI_MSHC_CTRL); + val |= CV18XX_EMMC_FUNC_EN; + sdhci_writel(host, val, priv->vendor_specific_area1 + CV18XX_SDHCI_MSHC_CTRL); + } + + val = sdhci_readl(host, priv->vendor_specific_area1 + CV18XX_SDHCI_MSHC_CTRL); + val |= CV18XX_LATANCY_1T; + sdhci_writel(host, val, priv->vendor_specific_area1 + CV18XX_SDHCI_MSHC_CTRL); + + val = sdhci_readl(host, priv->vendor_specific_area1 + CV18XX_SDHCI_PHY_CONFIG); + val |= CV18XX_PHY_TX_BPS; + sdhci_writel(host, val, priv->vendor_specific_area1 + CV18XX_SDHCI_PHY_CONFIG); + + val = (FIELD_PREP(CV18XX_PHY_TX_DLY_MSK, 0) | + FIELD_PREP(CV18XX_PHY_TX_SRC_MSK, CV18XX_PHY_TX_SRC_INVERT_CLK_TX) | + FIELD_PREP(CV18XX_PHY_RX_DLY_MSK, 0) | + FIELD_PREP(CV18XX_PHY_RX_SRC_MSK, CV18XX_PHY_RX_SRC_INVERT_RX_CLK)); + sdhci_writel(host, val, priv->vendor_specific_area1 + CV18XX_SDHCI_PHY_TX_RX_DLY); +} + static const struct sdhci_ops sdhci_dwcmshc_ops = { .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, @@ -671,6 +714,15 @@ static const struct sdhci_ops sdhci_dwcmshc_th1520_ops = { .platform_execute_tuning = &th1520_execute_tuning, }; +static const struct sdhci_ops sdhci_dwcmshc_cv18xx_ops = { + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = dwcmshc_set_uhs_signaling, + .get_max_clock = dwcmshc_get_max_clock, + .reset = cv18xx_sdhci_reset, + .adma_write_desc = dwcmshc_adma_write_desc, +}; + static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = { .ops = &sdhci_dwcmshc_ops, .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, @@ -700,6 +752,12 @@ static const struct sdhci_pltfm_data sdhci_dwcmshc_th1520_pdata = { .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, }; +static const struct sdhci_pltfm_data sdhci_dwcmshc_cv18xx_pdata = { + .ops = &sdhci_dwcmshc_cv18xx_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, +}; + static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) { int err; @@ -768,6 +826,14 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { .compatible = "snps,dwcmshc-sdhci", .data = &sdhci_dwcmshc_pdata, }, + { + .compatible = "sophgo,cv1800b-dwcmshc", + .data = &sdhci_dwcmshc_cv18xx_pdata, + }, + { + .compatible = "sophgo,sg2002-dwcmshc", + .data = &sdhci_dwcmshc_cv18xx_pdata, + }, { .compatible = "thead,th1520-dwcmshc", .data = &sdhci_dwcmshc_th1520_pdata, From 68f5630ae15407d8d3daaadbbfbafbd319e3ecd4 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Mon, 19 Feb 2024 09:43:57 -0300 Subject: [PATCH 27/44] mmc: core: constify the struct device_type usage Since commit aed65af1cc2f ("drivers: make device_type const"), the driver core can properly handle constant struct device_type. Move the sdio_type, sd_type and mmc_type variables to be constant structures as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Link: https://lore.kernel.org/r/20240219-device_cleanup-mmc-v1-1-1910e283cf5a@marliere.net Signed-off-by: Ulf Hansson --- drivers/mmc/core/bus.c | 2 +- drivers/mmc/core/bus.h | 2 +- drivers/mmc/core/mmc.c | 2 +- drivers/mmc/core/sd.c | 2 +- drivers/mmc/core/sd.h | 2 +- drivers/mmc/core/sdio.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 48daeafdff7a..0ddaee0eae54 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -272,7 +272,7 @@ static void mmc_release_card(struct device *dev) /* * Allocate and initialise a new MMC card structure. */ -struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type) +struct mmc_card *mmc_alloc_card(struct mmc_host *host, const struct device_type *type) { struct mmc_card *card; diff --git a/drivers/mmc/core/bus.h b/drivers/mmc/core/bus.h index 3996b191b68d..cfd0d02d3420 100644 --- a/drivers/mmc/core/bus.h +++ b/drivers/mmc/core/bus.h @@ -23,7 +23,7 @@ static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *a static DEVICE_ATTR(name, S_IRUGO, mmc_##name##_show, NULL) struct mmc_card *mmc_alloc_card(struct mmc_host *host, - struct device_type *type); + const struct device_type *type); int mmc_add_card(struct mmc_card *card); void mmc_remove_card(struct mmc_card *card); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 58ed7193a3ca..5b2f7c285461 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -883,7 +883,7 @@ static struct attribute *mmc_std_attrs[] = { }; ATTRIBUTE_GROUPS(mmc_std); -static struct device_type mmc_type = { +static const struct device_type mmc_type = { .groups = mmc_std_groups, }; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index c3e554344c99..1c8148cdda50 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -805,7 +805,7 @@ static const struct attribute_group sd_std_group = { }; __ATTRIBUTE_GROUPS(sd_std); -struct device_type sd_type = { +const struct device_type sd_type = { .groups = sd_std_groups, }; diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h index 1af5a038bae9..fe6dd46927a4 100644 --- a/drivers/mmc/core/sd.h +++ b/drivers/mmc/core/sd.h @@ -4,7 +4,7 @@ #include -extern struct device_type sd_type; +extern const struct device_type sd_type; struct mmc_host; struct mmc_card; diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 5914516df2f7..4fb247fde5c0 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -66,7 +66,7 @@ static struct attribute *sdio_std_attrs[] = { }; ATTRIBUTE_GROUPS(sdio_std); -static struct device_type sdio_type = { +static const struct device_type sdio_type = { .groups = sdio_std_groups, }; From 6f3d56882a3f210e7d1b6a169c4b32d926283f39 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 21 Feb 2024 19:46:02 +0100 Subject: [PATCH 28/44] dt-bindings: mmc: fsl-imx-esdhc: add default and 100mhz state Some devices support only the default and the 100MHz case, add the support for this to the binding to avoid warnings. Signed-off-by: Marco Felsch Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240221184602.3639619-1-m.felsch@pengutronix.de Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml index 3f6dcc773092..82f7ee8702cb 100644 --- a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml +++ b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml @@ -177,6 +177,11 @@ properties: - const: state_100mhz - const: state_200mhz - const: sleep + - minItems: 2 + items: + - const: default + - const: state_100mhz + - const: sleep - minItems: 1 items: - const: default From 44d41bf65a4bd74e00300fda0b3220921128f722 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 28 Feb 2024 10:07:27 +0100 Subject: [PATCH 29/44] mmc: sdhci-esdhc-mcf: Flag the sg_miter as atomic The sg_miter used to loop over the returned sglist from a transfer in the esdhc subdriver for SDHCI can be called from atomic context so the miter needs to be atomic. sdhci_request_done() is always called from process context, either as a work or as part of the threaded interrupt handler, but the one case when we are actually calling .request_done() from an atomic context is in sdhci_irq(). Fix this by flagging the miter atomic so we always use kmap_atomic(). Fixes: e8a167b84886 ("mmc: sdhci-esdhc-mcf: Use sg_miter for swapping") Reported-by: Adrian Hunter Signed-off-by: Linus Walleij Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20240228-fix-sdhci-esdhc-mcf-2-v2-1-4ebb3fd691ea@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-mcf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc-mcf.c b/drivers/mmc/host/sdhci-esdhc-mcf.c index 1909a11fd065..c97363e2d86c 100644 --- a/drivers/mmc/host/sdhci-esdhc-mcf.c +++ b/drivers/mmc/host/sdhci-esdhc-mcf.c @@ -313,7 +313,7 @@ static void esdhc_mcf_request_done(struct sdhci_host *host, * transfer endiannes. A swap after the transfer is needed. */ sg_miter_start(&sgm, mrq->data->sg, mrq->data->sg_len, - SG_MITER_TO_SG | SG_MITER_FROM_SG); + SG_MITER_ATOMIC | SG_MITER_TO_SG | SG_MITER_FROM_SG); while (sg_miter_next(&sgm)) { buffer = sgm.addr; esdhc_mcf_buffer_swap32(buffer, sgm.length); From 727cba706d38bcd1460b21239992e917be7da8fb Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 21 Feb 2024 22:23:00 +0100 Subject: [PATCH 30/44] mmc: sh_mmcif: sg_miter must not be atomic All the sglist iterations happen in the *threaded* interrupt handler and that context is not atomic, so don't request an atomic sglist miter. Using an atomic miter results in "BUG: scheduling while atomic" splats. Reported-by: Geert Uytterhoeven Fixes: 27b57277d9ba ("mmc: sh_mmcif: Use sg_miter for PIO") Tested-by: Geert Uytterhoeven Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20240221-fix-sh-mmcif-v2-1-5e521eb25ae4@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 1ef6e153e5a3..669555b5e8fa 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -607,7 +607,7 @@ static void sh_mmcif_single_read(struct sh_mmcif_host *host, BLOCK_SIZE_MASK) + 3; sg_miter_start(&host->sg_miter, data->sg, data->sg_len, - SG_MITER_ATOMIC | SG_MITER_TO_SG); + SG_MITER_TO_SG); host->wait_for = MMCIF_WAIT_FOR_READ; @@ -662,7 +662,7 @@ static void sh_mmcif_multi_read(struct sh_mmcif_host *host, BLOCK_SIZE_MASK; sg_miter_start(&host->sg_miter, data->sg, data->sg_len, - SG_MITER_ATOMIC | SG_MITER_TO_SG); + SG_MITER_TO_SG); host->wait_for = MMCIF_WAIT_FOR_MREAD; @@ -710,7 +710,7 @@ static void sh_mmcif_single_write(struct sh_mmcif_host *host, BLOCK_SIZE_MASK) + 3; sg_miter_start(&host->sg_miter, data->sg, data->sg_len, - SG_MITER_ATOMIC | SG_MITER_FROM_SG); + SG_MITER_FROM_SG); host->wait_for = MMCIF_WAIT_FOR_WRITE; @@ -765,7 +765,7 @@ static void sh_mmcif_multi_write(struct sh_mmcif_host *host, BLOCK_SIZE_MASK; sg_miter_start(&host->sg_miter, data->sg, data->sg_len, - SG_MITER_ATOMIC | SG_MITER_FROM_SG); + SG_MITER_FROM_SG); host->wait_for = MMCIF_WAIT_FOR_MWRITE; From 4d4a2c71b862b9e696006fd264dbf5dbd6484d77 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 21 Feb 2024 22:23:01 +0100 Subject: [PATCH 31/44] mmc: sh_mmcif: Advance sg_miter before reading blocks The introduction of sg_miter was a bit sloppy as it didn't exactly mimic the semantics of the old code on multiblock reads and writes: these like you to: - Advance to the first sglist entry *before* starting to read any blocks from the card. - Advance and check availability of the next entry *right after* processing one block. Not checking if we have more sglist entries right after reading a block will lead to this not being checked until we return to the callback to read out more blocks, i.e. until the next interrupt arrives. Since the last block is the last one (no more data will arrive) there will not be a next interrupt, and we will be waiting forever resulting in a timeout for command 18 when reading multiple blocks. The same bug was fixed also in the writing of multiple blocks. Reported-by: Geert Uytterhoeven Fixes: 27b57277d9ba ("mmc: sh_mmcif: Use sg_miter for PIO") Signed-off-by: Linus Walleij Tested-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20240221-fix-sh-mmcif-v2-2-5e521eb25ae4@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 38 +++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 669555b5e8fa..08b4312af94e 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -653,6 +653,7 @@ static bool sh_mmcif_read_block(struct sh_mmcif_host *host) static void sh_mmcif_multi_read(struct sh_mmcif_host *host, struct mmc_request *mrq) { + struct sg_mapping_iter *sgm = &host->sg_miter; struct mmc_data *data = mrq->data; if (!data->sg_len || !data->sg->length) @@ -661,9 +662,15 @@ static void sh_mmcif_multi_read(struct sh_mmcif_host *host, host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) & BLOCK_SIZE_MASK; - sg_miter_start(&host->sg_miter, data->sg, data->sg_len, + sg_miter_start(sgm, data->sg, data->sg_len, SG_MITER_TO_SG); + /* Advance to the first sglist entry */ + if (!sg_miter_next(sgm)) { + sg_miter_stop(sgm); + return; + } + host->wait_for = MMCIF_WAIT_FOR_MREAD; sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); @@ -684,11 +691,6 @@ static bool sh_mmcif_mread_block(struct sh_mmcif_host *host) return false; } - if (!sg_miter_next(sgm)) { - sg_miter_stop(sgm); - return false; - } - p = sgm->addr; for (i = 0; i < host->blocksize / 4; i++) @@ -698,6 +700,11 @@ static bool sh_mmcif_mread_block(struct sh_mmcif_host *host) sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); + if (!sg_miter_next(sgm)) { + sg_miter_stop(sgm); + return false; + } + return true; } @@ -756,6 +763,7 @@ static bool sh_mmcif_write_block(struct sh_mmcif_host *host) static void sh_mmcif_multi_write(struct sh_mmcif_host *host, struct mmc_request *mrq) { + struct sg_mapping_iter *sgm = &host->sg_miter; struct mmc_data *data = mrq->data; if (!data->sg_len || !data->sg->length) @@ -764,9 +772,15 @@ static void sh_mmcif_multi_write(struct sh_mmcif_host *host, host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) & BLOCK_SIZE_MASK; - sg_miter_start(&host->sg_miter, data->sg, data->sg_len, + sg_miter_start(sgm, data->sg, data->sg_len, SG_MITER_FROM_SG); + /* Advance to the first sglist entry */ + if (!sg_miter_next(sgm)) { + sg_miter_stop(sgm); + return; + } + host->wait_for = MMCIF_WAIT_FOR_MWRITE; sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); @@ -787,11 +801,6 @@ static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host) return false; } - if (!sg_miter_next(sgm)) { - sg_miter_stop(sgm); - return false; - } - p = sgm->addr; for (i = 0; i < host->blocksize / 4; i++) @@ -799,6 +808,11 @@ static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host) sgm->consumed = host->blocksize; + if (!sg_miter_next(sgm)) { + sg_miter_stop(sgm); + return false; + } + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); return true; From a119357feff8afd96af5d8e9bd47d8f3735110f6 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 22 Feb 2024 11:39:11 -0300 Subject: [PATCH 32/44] dt-bindings: mmc: fsl-imx-mmc: Document the required clocks The fsl-imx-mmc hardware needs two clocks to operate: ipg and per. Document these required clocks. This fixes the following schema warning: imx27-apf27dev.dtb: mmc@10014000: Unevaluated properties are not allowed ('clock-names', 'clocks' were unexpected) from schema $id: http://devicetree.org/schemas/mmc/fsl-imx-mmc.yaml# Signed-off-by: Fabio Estevam Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240222143911.979058-1-festevam@gmail.com Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/fsl-imx-mmc.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/fsl-imx-mmc.yaml b/Documentation/devicetree/bindings/mmc/fsl-imx-mmc.yaml index 221f5bc047bd..7911316fbd6a 100644 --- a/Documentation/devicetree/bindings/mmc/fsl-imx-mmc.yaml +++ b/Documentation/devicetree/bindings/mmc/fsl-imx-mmc.yaml @@ -24,6 +24,14 @@ properties: reg: maxItems: 1 + clocks: + maxItems: 2 + + clock-names: + items: + - const: ipg + - const: per + interrupts: maxItems: 1 @@ -34,6 +42,8 @@ properties: const: rx-tx required: + - clocks + - clock-names - compatible - reg - interrupts @@ -46,6 +56,8 @@ examples: compatible = "fsl,imx27-mmc", "fsl,imx21-mmc"; reg = <0x10014000 0x1000>; interrupts = <11>; + clocks = <&clks 29>, <&clks 60>; + clock-names = "ipg", "per"; dmas = <&dma 7>; dma-names = "rx-tx"; bus-width = <4>; From e8d1b41e69d72c62865bebe8f441163ec00b3d44 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 5 Mar 2024 11:42:56 +0100 Subject: [PATCH 33/44] mmc: tmio: avoid concurrent runs of mmc_request_done() With the to-be-fixed commit, the reset_work handler cleared 'host->mrq' outside of the spinlock protected critical section. That leaves a small race window during execution of 'tmio_mmc_reset()' where the done_work handler could grab a pointer to the now invalid 'host->mrq'. Both would use it to call mmc_request_done() causing problems (see link below). However, 'host->mrq' cannot simply be cleared earlier inside the critical section. That would allow new mrqs to come in asynchronously while the actual reset of the controller still needs to be done. So, like 'tmio_mmc_set_ios()', an ERR_PTR is used to prevent new mrqs from coming in but still avoiding concurrency between work handlers. Reported-by: Dirk Behme Closes: https://lore.kernel.org/all/20240220061356.3001761-1-dirk.behme@de.bosch.com/ Fixes: df3ef2d3c92c ("mmc: protect the tmio_mmc driver against a theoretical race") Signed-off-by: Wolfram Sang Tested-by: Dirk Behme Reviewed-by: Dirk Behme Cc: stable@vger.kernel.org # 3.0+ Link: https://lore.kernel.org/r/20240305104423.3177-2-wsa+renesas@sang-engineering.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index be7f18fd4836..c253d176db69 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -259,6 +259,8 @@ static void tmio_mmc_reset_work(struct work_struct *work) else mrq->cmd->error = -ETIMEDOUT; + /* No new calls yet, but disallow concurrent tmio_mmc_done_work() */ + host->mrq = ERR_PTR(-EBUSY); host->cmd = NULL; host->data = NULL; From ae5004a40a262d329039b99b62bd3fe7645b66ad Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 26 Feb 2024 22:37:39 +0100 Subject: [PATCH 34/44] mmc: wmt-sdmmc: remove an incorrect release_mem_region() call in the .remove function This looks strange to call release_mem_region() in a remove function without any request_mem_region() in the probe or "struct resource" somewhere. So remove the corresponding code. Fixes: 3a96dff0f828 ("mmc: SD/MMC Host Controller for Wondermedia WM8505/WM8650") Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/bb0bb1ed1e18de55e8c0547625bde271e64b8c31.1708983064.git.christophe.jaillet@wanadoo.fr Signed-off-by: Ulf Hansson --- drivers/mmc/host/wmt-sdmmc.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index 77d5f1d24489..860380931b6c 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -883,7 +883,6 @@ static void wmt_mci_remove(struct platform_device *pdev) { struct mmc_host *mmc; struct wmt_mci_priv *priv; - struct resource *res; u32 reg_tmp; mmc = platform_get_drvdata(pdev); @@ -911,9 +910,6 @@ static void wmt_mci_remove(struct platform_device *pdev) clk_disable_unprepare(priv->clk_sdmmc); clk_put(priv->clk_sdmmc); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - mmc_free_host(mmc); dev_info(&pdev->dev, "WMT MCI device removed\n"); From 88803989ff6d73155969df94cc84f1ecd28c4d9c Mon Sep 17 00:00:00 2001 From: Yang Xiwen Date: Thu, 29 Feb 2024 09:36:19 +0800 Subject: [PATCH 35/44] mmc: core: Use a struct device* as in-param to mmc_of_parse_clk_phase() Parsing dt usually happens very early, sometimes even before the struct mmc_host has been allocated (e.g. dw_mci_probe() and dw_mci_parse_dt() in dw_mmc.c). Looking at the source of mmc_of_parse_clk_phase(), it's actually not needed to have an initialized mmc_host, let's therefore pass a struct device* to it instead. Also update the only current user, sdhci-of-aspeed. Reviewed-by: Paul Menzel Acked-by: Andrew Jeffery Signed-off-by: Yang Xiwen Link: https://lore.kernel.org/r/20240229-b4-mmc-hi3798mv200-v7-1-10c03f316285@outlook.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 4 +--- drivers/mmc/host/sdhci-of-aspeed.c | 2 +- include/linux/mmc/host.h | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 7cc9a33d28ca..3aa9dda6fd4f 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -234,10 +234,8 @@ static void mmc_of_parse_timing_phase(struct device *dev, const char *prop, } void -mmc_of_parse_clk_phase(struct mmc_host *host, struct mmc_clk_phase_map *map) +mmc_of_parse_clk_phase(struct device *dev, struct mmc_clk_phase_map *map) { - struct device *dev = host->parent; - mmc_of_parse_timing_phase(dev, "clk-phase-legacy", &map->phase[MMC_TIMING_LEGACY]); mmc_of_parse_timing_phase(dev, "clk-phase-mmc-hs", diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c index 42d54532cabe..430c1f90037b 100644 --- a/drivers/mmc/host/sdhci-of-aspeed.c +++ b/drivers/mmc/host/sdhci-of-aspeed.c @@ -435,7 +435,7 @@ static int aspeed_sdhci_probe(struct platform_device *pdev) goto err_sdhci_add; if (dev->phase_desc) - mmc_of_parse_clk_phase(host->mmc, &dev->phase_map); + mmc_of_parse_clk_phase(&pdev->dev, &dev->phase_map); ret = sdhci_add_host(host); if (ret) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 2f445c651742..5894bf912f7b 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -539,7 +539,7 @@ struct mmc_host *devm_mmc_alloc_host(struct device *dev, int extra); int mmc_add_host(struct mmc_host *); void mmc_remove_host(struct mmc_host *); void mmc_free_host(struct mmc_host *); -void mmc_of_parse_clk_phase(struct mmc_host *host, +void mmc_of_parse_clk_phase(struct device *dev, struct mmc_clk_phase_map *map); int mmc_of_parse(struct mmc_host *host); int mmc_of_parse_voltage(struct mmc_host *host, u32 *mask); From dab267cf0e568fec2f6d38bbfe9abac749539ca8 Mon Sep 17 00:00:00 2001 From: Yang Xiwen Date: Thu, 29 Feb 2024 09:36:20 +0800 Subject: [PATCH 36/44] mmc: dw_mmc-hi3798cv200: remove MODULE_ALIAS() The alias is not used and should be removed. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Yang Xiwen Link: https://lore.kernel.org/r/20240229-b4-mmc-hi3798mv200-v7-2-10c03f316285@outlook.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-hi3798cv200.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc-hi3798cv200.c b/drivers/mmc/host/dw_mmc-hi3798cv200.c index e9470c50a348..61923a518369 100644 --- a/drivers/mmc/host/dw_mmc-hi3798cv200.c +++ b/drivers/mmc/host/dw_mmc-hi3798cv200.c @@ -201,4 +201,3 @@ module_platform_driver(dw_mci_hi3798cv200_driver); MODULE_DESCRIPTION("HiSilicon Hi3798CV200 Specific DW-MSHC Driver Extension"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:dwmmc_hi3798cv200"); From 832ff3126527339160990e99c1cbc7a5ddc0576c Mon Sep 17 00:00:00 2001 From: Yang Xiwen Date: Thu, 29 Feb 2024 09:36:21 +0800 Subject: [PATCH 37/44] dt-bindings: mmc: dw-mshc-hi3798cv200: convert to YAML convert the legacy txt binding to modern YAML and rename to hisilicon,hi3798cv200-dw-mshc.yaml. No semantic change. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Yang Xiwen Link: https://lore.kernel.org/r/20240229-b4-mmc-hi3798mv200-v7-3-10c03f316285@outlook.com Signed-off-by: Ulf Hansson --- .../bindings/mmc/hi3798cv200-dw-mshc.txt | 40 ---------- .../mmc/hisilicon,hi3798cv200-dw-mshc.yaml | 75 +++++++++++++++++++ 2 files changed, 75 insertions(+), 40 deletions(-) delete mode 100644 Documentation/devicetree/bindings/mmc/hi3798cv200-dw-mshc.txt create mode 100644 Documentation/devicetree/bindings/mmc/hisilicon,hi3798cv200-dw-mshc.yaml diff --git a/Documentation/devicetree/bindings/mmc/hi3798cv200-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/hi3798cv200-dw-mshc.txt deleted file mode 100644 index a0693b7145f2..000000000000 --- a/Documentation/devicetree/bindings/mmc/hi3798cv200-dw-mshc.txt +++ /dev/null @@ -1,40 +0,0 @@ -* Hisilicon Hi3798CV200 specific extensions to the Synopsys Designware Mobile - Storage Host Controller - -Read synopsys-dw-mshc.txt for more details - -The Synopsys designware mobile storage host controller is used to interface -a SoC with storage medium such as eMMC or SD/MMC cards. This file documents -differences between the core Synopsys dw mshc controller properties described -by synopsys-dw-mshc.txt and the properties used by the Hisilicon Hi3798CV200 -specific extensions to the Synopsys Designware Mobile Storage Host Controller. - -Required Properties: -- compatible: Should contain "hisilicon,hi3798cv200-dw-mshc". -- clocks: A list of phandle + clock-specifier pairs for the clocks listed - in clock-names. -- clock-names: Should contain the following: - "ciu" - The ciu clock described in synopsys-dw-mshc.txt. - "biu" - The biu clock described in synopsys-dw-mshc.txt. - "ciu-sample" - Hi3798CV200 extended phase clock for ciu sampling. - "ciu-drive" - Hi3798CV200 extended phase clock for ciu driving. - -Example: - - emmc: mmc@9830000 { - compatible = "hisilicon,hi3798cv200-dw-mshc"; - reg = <0x9830000 0x10000>; - interrupts = ; - clocks = <&crg HISTB_MMC_CIU_CLK>, - <&crg HISTB_MMC_BIU_CLK>, - <&crg HISTB_MMC_SAMPLE_CLK>, - <&crg HISTB_MMC_DRV_CLK>; - clock-names = "ciu", "biu", "ciu-sample", "ciu-drive"; - fifo-depth = <256>; - clock-frequency = <200000000>; - cap-mmc-highspeed; - mmc-ddr-1_8v; - mmc-hs200-1_8v; - non-removable; - bus-width = <8>; - }; diff --git a/Documentation/devicetree/bindings/mmc/hisilicon,hi3798cv200-dw-mshc.yaml b/Documentation/devicetree/bindings/mmc/hisilicon,hi3798cv200-dw-mshc.yaml new file mode 100644 index 000000000000..f3dc973cb490 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/hisilicon,hi3798cv200-dw-mshc.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/hisilicon,hi3798cv200-dw-mshc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Hisilicon Hi3798CV200 SoC specific extensions to the Synopsys DWMMC controller + +maintainers: + - Yang Xiwen + +properties: + compatible: + enum: + - hisilicon,hi3798cv200-dw-mshc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: bus interface unit clock + - description: card interface unit clock + - description: card input sample phase clock + - description: controller output drive phase clock + + clock-names: + items: + - const: ciu + - const: biu + - const: ciu-sample + - const: ciu-drive + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +allOf: + - $ref: synopsys-dw-mshc-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + + mmc@9830000 { + compatible = "hisilicon,hi3798cv200-dw-mshc"; + reg = <0x9830000 0x10000>; + interrupts = ; + clocks = <&crg HISTB_MMC_CIU_CLK>, + <&crg HISTB_MMC_BIU_CLK>, + <&crg HISTB_MMC_SAMPLE_CLK>, + <&crg HISTB_MMC_DRV_CLK>; + clock-names = "ciu", "biu", "ciu-sample", "ciu-drive"; + resets = <&crg 0xa0 4>; + reset-names = "reset"; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_pins_1 &emmc_pins_2 + &emmc_pins_3 &emmc_pins_4>; + fifo-depth = <256>; + clock-frequency = <200000000>; + cap-mmc-highspeed; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + non-removable; + bus-width = <8>; + }; From cddacdce8ffb168d6cc5ffd0bd5a55153e528135 Mon Sep 17 00:00:00 2001 From: Yang Xiwen Date: Thu, 29 Feb 2024 09:36:22 +0800 Subject: [PATCH 38/44] dt-bindings: mmc: hisilicon,hi3798cv200-dw-mshc: add Hi3798MV200 binding Add binding and an extra property for Hi3798MV200 DWMMC specific extension. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Yang Xiwen Link: https://lore.kernel.org/r/20240229-b4-mmc-hi3798mv200-v7-4-10c03f316285@outlook.com Signed-off-by: Ulf Hansson --- .../mmc/hisilicon,hi3798cv200-dw-mshc.yaml | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/hisilicon,hi3798cv200-dw-mshc.yaml b/Documentation/devicetree/bindings/mmc/hisilicon,hi3798cv200-dw-mshc.yaml index f3dc973cb490..41c9b22523e7 100644 --- a/Documentation/devicetree/bindings/mmc/hisilicon,hi3798cv200-dw-mshc.yaml +++ b/Documentation/devicetree/bindings/mmc/hisilicon,hi3798cv200-dw-mshc.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/mmc/hisilicon,hi3798cv200-dw-mshc.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Hisilicon Hi3798CV200 SoC specific extensions to the Synopsys DWMMC controller +title: Hisilicon HiSTB SoCs specific extensions to the Synopsys DWMMC controller maintainers: - Yang Xiwen @@ -13,6 +13,7 @@ properties: compatible: enum: - hisilicon,hi3798cv200-dw-mshc + - hisilicon,hi3798mv200-dw-mshc reg: maxItems: 1 @@ -34,6 +35,15 @@ properties: - const: ciu-sample - const: ciu-drive + hisilicon,sap-dll-reg: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: | + DWMMC core on Hi3798MV2x SoCs has a delay-locked-loop(DLL) attached to card data input path. + It is integrated into CRG core on the SoC and has to be controlled during tuning. + items: + - description: A phandle pointed to the CRG syscon node + - description: Sample DLL register offset in CRG address space + required: - compatible - reg @@ -44,6 +54,18 @@ required: allOf: - $ref: synopsys-dw-mshc-common.yaml# + - if: + properties: + compatible: + contains: + const: hisilicon,hi3798mv200-dw-mshc + then: + required: + - hisilicon,sap-dll-reg + else: + properties: + hisilicon,sap-dll-reg: false + unevaluatedProperties: false examples: From 25d043841db17b7ad7d850caf4652bd8b3d580ed Mon Sep 17 00:00:00 2001 From: Yang Xiwen Date: Thu, 29 Feb 2024 09:36:23 +0800 Subject: [PATCH 39/44] mmc: dw_mmc: add support for hi3798mv200 Add support for Hi3798MV200 specific extension. Signed-off-by: Yang Xiwen Link: https://lore.kernel.org/r/20240229-b4-mmc-hi3798mv200-v7-5-10c03f316285@outlook.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 9 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/dw_mmc-hi3798mv200.c | 251 ++++++++++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 drivers/mmc/host/dw_mmc-hi3798mv200.c diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 81f2c4e05287..aebc587f77a7 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -798,6 +798,15 @@ config MMC_DW_HI3798CV200 Synopsys DesignWare Memory Card Interface driver. Select this option for platforms based on HiSilicon Hi3798CV200 SoC. +config MMC_DW_HI3798MV200 + tristate "Hi3798MV200 specific extensions for Synopsys DW Memory Card Interface" + depends on MMC_DW + select MMC_DW_PLTFM + help + This selects support for HiSilicon Hi3798MV200 SoC specific extensions to the + Synopsys DesignWare Memory Card Interface driver. Select this option + for platforms based on HiSilicon Hi3798MV200 SoC. + config MMC_DW_K3 tristate "K3 specific extensions for Synopsys DW Memory Card Interface" depends on MMC_DW diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index d0be4465f3ec..f53f86d200ac 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o obj-$(CONFIG_MMC_DW_BLUEFIELD) += dw_mmc-bluefield.o obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.o +obj-$(CONFIG_MMC_DW_HI3798MV200) += dw_mmc-hi3798mv200.o obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o diff --git a/drivers/mmc/host/dw_mmc-hi3798mv200.c b/drivers/mmc/host/dw_mmc-hi3798mv200.c new file mode 100644 index 000000000000..989ae8dda722 --- /dev/null +++ b/drivers/mmc/host/dw_mmc-hi3798mv200.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Modified from dw_mmc-hi3798cv200.c + * + * Copyright (c) 2024 Yang Xiwen + * Copyright (c) 2018 HiSilicon Technologies Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "dw_mmc.h" +#include "dw_mmc-pltfm.h" + +#define SDMMC_TUNING_CTRL 0x118 +#define SDMMC_TUNING_FIND_EDGE BIT(5) + +#define ALL_INT_CLR 0x1ffff + +/* DLL ctrl reg */ +#define SAP_DLL_CTRL_DLLMODE BIT(16) + +struct dw_mci_hi3798mv200_priv { + struct clk *sample_clk; + struct clk *drive_clk; + struct regmap *crg_reg; + u32 sap_dll_offset; + struct mmc_clk_phase_map phase_map; +}; + +static void dw_mci_hi3798mv200_set_ios(struct dw_mci *host, struct mmc_ios *ios) +{ + struct dw_mci_hi3798mv200_priv *priv = host->priv; + struct mmc_clk_phase phase = priv->phase_map.phase[ios->timing]; + u32 val; + + val = mci_readl(host, ENABLE_SHIFT); + if (ios->timing == MMC_TIMING_MMC_DDR52 + || ios->timing == MMC_TIMING_UHS_DDR50) + val |= SDMMC_ENABLE_PHASE; + else + val &= ~SDMMC_ENABLE_PHASE; + mci_writel(host, ENABLE_SHIFT, val); + + val = mci_readl(host, DDR_REG); + if (ios->timing == MMC_TIMING_MMC_HS400) + val |= SDMMC_DDR_HS400; + else + val &= ~SDMMC_DDR_HS400; + mci_writel(host, DDR_REG, val); + + if (clk_set_rate(host->ciu_clk, ios->clock)) + dev_warn(host->dev, "Failed to set rate to %u\n", ios->clock); + else + /* + * CLK_MUX_ROUND_NEAREST is enabled for this clock + * The actual clock rate is not what we set, but a rounded value + * so we should get the rate once again + */ + host->bus_hz = clk_get_rate(host->ciu_clk); + + if (phase.valid) { + clk_set_phase(priv->drive_clk, phase.out_deg); + clk_set_phase(priv->sample_clk, phase.in_deg); + } else { + dev_warn(host->dev, + "The phase entry for timing mode %d is missing in device tree.\n", + ios->timing); + } +} + +static inline int dw_mci_hi3798mv200_enable_tuning(struct dw_mci_slot *slot) +{ + struct dw_mci_hi3798mv200_priv *priv = slot->host->priv; + + return regmap_clear_bits(priv->crg_reg, priv->sap_dll_offset, SAP_DLL_CTRL_DLLMODE); +} + +static inline int dw_mci_hi3798mv200_disable_tuning(struct dw_mci_slot *slot) +{ + struct dw_mci_hi3798mv200_priv *priv = slot->host->priv; + + return regmap_set_bits(priv->crg_reg, priv->sap_dll_offset, SAP_DLL_CTRL_DLLMODE); +} + +static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot, + u32 opcode) +{ + static const int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 }; + struct dw_mci *host = slot->host; + struct dw_mci_hi3798mv200_priv *priv = host->priv; + int raise_point = -1, fall_point = -1, mid; + int err, prev_err = -1; + int found = 0; + int regval; + int i; + int ret; + + ret = dw_mci_hi3798mv200_enable_tuning(slot); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(degrees); i++) { + clk_set_phase(priv->sample_clk, degrees[i]); + mci_writel(host, RINTSTS, ALL_INT_CLR); + + /* + * HiSilicon implemented a tuning mechanism. + * It needs special interaction with the DLL. + * + * Treat edge(flip) found as an error too. + */ + err = mmc_send_tuning(slot->mmc, opcode, NULL); + regval = mci_readl(host, TUNING_CTRL); + if (err || (regval & SDMMC_TUNING_FIND_EDGE)) + err = 1; + else + found = 1; + + if (i > 0) { + if (err && !prev_err) + fall_point = i - 1; + if (!err && prev_err) + raise_point = i; + } + + if (raise_point != -1 && fall_point != -1) + goto tuning_out; + + prev_err = err; + err = 0; + } + +tuning_out: + ret = dw_mci_hi3798mv200_disable_tuning(slot); + if (ret < 0) + return ret; + + if (found) { + if (raise_point == -1) + raise_point = 0; + if (fall_point == -1) + fall_point = ARRAY_SIZE(degrees) - 1; + if (fall_point < raise_point) { + if ((raise_point + fall_point) > + (ARRAY_SIZE(degrees) - 1)) + mid = fall_point / 2; + else + mid = (raise_point + ARRAY_SIZE(degrees) - 1) / 2; + } else { + mid = (raise_point + fall_point) / 2; + } + + /* + * We don't care what timing we are tuning for, + * simply use the same phase for all timing needs tuning. + */ + priv->phase_map.phase[MMC_TIMING_MMC_HS200].in_deg = degrees[mid]; + priv->phase_map.phase[MMC_TIMING_MMC_HS400].in_deg = degrees[mid]; + priv->phase_map.phase[MMC_TIMING_UHS_SDR104].in_deg = degrees[mid]; + + clk_set_phase(priv->sample_clk, degrees[mid]); + dev_dbg(host->dev, "Tuning clk_sample[%d, %d], set[%d]\n", + raise_point, fall_point, degrees[mid]); + ret = 0; + } else { + dev_err(host->dev, "No valid clk_sample shift!\n"); + ret = -EINVAL; + } + + mci_writel(host, RINTSTS, ALL_INT_CLR); + + return ret; +} + +static int dw_mci_hi3798mv200_init(struct dw_mci *host) +{ + struct dw_mci_hi3798mv200_priv *priv; + struct device_node *np = host->dev->of_node; + int ret; + + priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mmc_of_parse_clk_phase(host->dev, &priv->phase_map); + + priv->sample_clk = devm_clk_get_enabled(host->dev, "ciu-sample"); + if (IS_ERR(priv->sample_clk)) + return dev_err_probe(host->dev, PTR_ERR(priv->sample_clk), + "failed to get enabled ciu-sample clock\n"); + + priv->drive_clk = devm_clk_get_enabled(host->dev, "ciu-drive"); + if (IS_ERR(priv->drive_clk)) + return dev_err_probe(host->dev, PTR_ERR(priv->drive_clk), + "failed to get enabled ciu-drive clock\n"); + + priv->crg_reg = syscon_regmap_lookup_by_phandle(np, "hisilicon,sap-dll-reg"); + if (IS_ERR(priv->crg_reg)) + return dev_err_probe(host->dev, PTR_ERR(priv->crg_reg), + "failed to get CRG reg\n"); + + ret = of_property_read_u32_index(np, "hisilicon,sap-dll-reg", 1, &priv->sap_dll_offset); + if (ret) + return dev_err_probe(host->dev, ret, "failed to get sample DLL register offset\n"); + + host->priv = priv; + return 0; +} + +static const struct dw_mci_drv_data hi3798mv200_data = { + .common_caps = MMC_CAP_CMD23, + .init = dw_mci_hi3798mv200_init, + .set_ios = dw_mci_hi3798mv200_set_ios, + .execute_tuning = dw_mci_hi3798mv200_execute_tuning_mix_mode, +}; + +static const struct of_device_id dw_mci_hi3798mv200_match[] = { + { .compatible = "hisilicon,hi3798mv200-dw-mshc" }, + {}, +}; + +static int dw_mci_hi3798mv200_probe(struct platform_device *pdev) +{ + return dw_mci_pltfm_register(pdev, &hi3798mv200_data); +} + +static void dw_mci_hi3798mv200_remove(struct platform_device *pdev) +{ + dw_mci_pltfm_remove(pdev); +} + +MODULE_DEVICE_TABLE(of, dw_mci_hi3798mv200_match); +static struct platform_driver dw_mci_hi3798mv200_driver = { + .probe = dw_mci_hi3798mv200_probe, + .remove_new = dw_mci_hi3798mv200_remove, + .driver = { + .name = "dwmmc_hi3798mv200", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = dw_mci_hi3798mv200_match, + }, +}; +module_platform_driver(dw_mci_hi3798mv200_driver); + +MODULE_DESCRIPTION("HiSilicon Hi3798MV200 Specific DW-MSHC Driver Extension"); +MODULE_LICENSE("GPL"); From 67e90a7deacb1243d87567ec7b89e34ff639d97d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 4 Mar 2024 19:56:06 +0200 Subject: [PATCH 40/44] mmc: dw_mmc: Remove unused of_gpio.h of_gpio.h is deprecated and subject to remove. The driver doesn't use it, simply remove the unused header. Signed-off-by: Andy Shevchenko Reviewed-by: Sam Protsenko Link: https://lore.kernel.org/r/20240304175606.1200076-1-andriy.shevchenko@linux.intel.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-exynos.c | 1 - drivers/mmc/host/dw_mmc.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 698408e8bad0..6dc057718d2c 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 829af2c98a44..8e2d676b9239 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include "dw_mmc.h" From a5f372a1bbaaf07b4028d0d2002c460958c46911 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 4 Mar 2024 20:48:30 +0200 Subject: [PATCH 41/44] mmc: mmc_spi: Don't mention DMA direction Since driver doesn't handle any DMA requests, drop any use of DMA bits, such as DMA direction. Instead, use MMC_DATA_WRITE flag directly. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240304184830.1319526-1-andriy.shevchenko@linux.intel.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmc_spi.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index b8dda8160c4e..922275de0593 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -510,10 +509,7 @@ mmc_spi_command_send(struct mmc_spi_host *host, * so we explicitly initialize it to all ones on RX paths. */ static void -mmc_spi_setup_data_message( - struct mmc_spi_host *host, - bool multiple, - enum dma_data_direction direction) +mmc_spi_setup_data_message(struct mmc_spi_host *host, bool multiple, bool write) { struct spi_transfer *t; struct scratch *scratch = host->data; @@ -523,7 +519,7 @@ mmc_spi_setup_data_message( /* for reads, readblock() skips 0xff bytes before finding * the token; for writes, this transfer issues that token. */ - if (direction == DMA_TO_DEVICE) { + if (write) { t = &host->token; memset(t, 0, sizeof(*t)); t->len = 1; @@ -547,7 +543,7 @@ mmc_spi_setup_data_message( t = &host->crc; memset(t, 0, sizeof(*t)); t->len = 2; - if (direction == DMA_TO_DEVICE) { + if (write) { /* the actual CRC may get written later */ t->tx_buf = &scratch->crc_val; } else { @@ -570,10 +566,10 @@ mmc_spi_setup_data_message( * the next token (next data block, or STOP_TRAN). We can try to * minimize I/O ops by using a single read to collect end-of-busy. */ - if (multiple || direction == DMA_TO_DEVICE) { + if (multiple || write) { t = &host->early_status; memset(t, 0, sizeof(*t)); - t->len = (direction == DMA_TO_DEVICE) ? sizeof(scratch->status) : 1; + t->len = write ? sizeof(scratch->status) : 1; t->tx_buf = host->ones; t->rx_buf = scratch->status; t->cs_change = 1; @@ -777,15 +773,15 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd, { struct spi_device *spi = host->spi; struct spi_transfer *t; - enum dma_data_direction direction = mmc_get_dma_dir(data); struct scatterlist *sg; unsigned n_sg; bool multiple = (data->blocks > 1); - const char *write_or_read = (direction == DMA_TO_DEVICE) ? "write" : "read"; + bool write = (data->flags & MMC_DATA_WRITE); + const char *write_or_read = write ? "write" : "read"; u32 clock_rate; unsigned long timeout; - mmc_spi_setup_data_message(host, multiple, direction); + mmc_spi_setup_data_message(host, multiple, write); t = &host->t; if (t->speed_hz) @@ -807,7 +803,7 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd, /* allow pio too; we don't allow highmem */ kmap_addr = kmap(sg_page(sg)); - if (direction == DMA_TO_DEVICE) + if (write) t->tx_buf = kmap_addr + sg->offset; else t->rx_buf = kmap_addr + sg->offset; @@ -818,7 +814,7 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd, dev_dbg(&spi->dev, " %s block, %d bytes\n", write_or_read, t->len); - if (direction == DMA_TO_DEVICE) + if (write) status = mmc_spi_writeblock(host, t, timeout); else status = mmc_spi_readblock(host, t, timeout); @@ -833,7 +829,9 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd, } /* discard mappings */ - if (direction == DMA_FROM_DEVICE) + if (write) + /* nothing to do */; + else flush_dcache_page(sg_page(sg)); kunmap(sg_page(sg)); @@ -850,7 +848,7 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd, * that can affect the STOP_TRAN logic. Complete (and current) * MMC specs should sort that out before Linux starts using CMD23. */ - if (direction == DMA_TO_DEVICE && multiple) { + if (write && multiple) { struct scratch *scratch = host->data; int tmp; const unsigned statlen = sizeof(scratch->status); From 0cd8fd15a75e82ef249e99b3d80e2012a729d028 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 5 Mar 2024 11:56:24 +0100 Subject: [PATCH 42/44] mmc: tmio: comment the ERR_PTR usage in this driver It is not super obvious why the driver sometimes uses an ERR_PTR for the current mrq. Explain why in comments. Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20240305105623.3379-2-wsa+renesas@sang-engineering.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index c253d176db69..93e912afd3ae 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -972,6 +972,7 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) return; } + /* Disallow new mrqs and work handlers to run */ host->mrq = ERR_PTR(-EBUSY); spin_unlock_irqrestore(&host->lock, flags); @@ -1006,8 +1007,9 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) "%s.%d: IOS interrupted: clk %u, mode %u", current->comm, task_pid_nr(current), ios->clock, ios->power_mode); - host->mrq = NULL; + /* Ready for new mrqs */ + host->mrq = NULL; host->clk_cache = ios->clock; mutex_unlock(&host->ios_lock); From 4af59a8df5ea930038cd3355e822f5eedf4accc1 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Wed, 6 Mar 2024 10:44:38 +0900 Subject: [PATCH 43/44] mmc: core: Fix switch on gp3 partition Commit e7794c14fd73 ("mmc: rpmb: fixes pause retune on all RPMB partitions.") added a mask check for 'part_type', but the mask used was wrong leading to the code intended for rpmb also being executed for GP3. On some MMCs (but not all) this would make gp3 partition inaccessible: armadillo:~# head -c 1 < /dev/mmcblk2gp3 head: standard input: I/O error armadillo:~# dmesg -c [ 422.976583] mmc2: running CQE recovery [ 423.058182] mmc2: running CQE recovery [ 423.137607] mmc2: running CQE recovery [ 423.137802] blk_update_request: I/O error, dev mmcblk2gp3, sector 0 op 0x0:(READ) flags 0x80700 phys_seg 4 prio class 0 [ 423.237125] mmc2: running CQE recovery [ 423.318206] mmc2: running CQE recovery [ 423.397680] mmc2: running CQE recovery [ 423.397837] blk_update_request: I/O error, dev mmcblk2gp3, sector 0 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0 [ 423.408287] Buffer I/O error on dev mmcblk2gp3, logical block 0, async page read the part_type values of interest here are defined as follow: main 0 boot0 1 boot1 2 rpmb 3 gp0 4 gp1 5 gp2 6 gp3 7 so mask with EXT_CSD_PART_CONFIG_ACC_MASK (7) to correctly identify rpmb Fixes: e7794c14fd73 ("mmc: rpmb: fixes pause retune on all RPMB partitions.") Cc: stable@vger.kernel.org Cc: Jorge Ramirez-Ortiz Signed-off-by: Dominique Martinet Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20240306-mmc-partswitch-v1-1-bf116985d950@codewreck.org Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 32d49100dff5..86efa6084696 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -874,10 +874,11 @@ static const struct block_device_operations mmc_bdops = { static int mmc_blk_part_switch_pre(struct mmc_card *card, unsigned int part_type) { - const unsigned int mask = EXT_CSD_PART_CONFIG_ACC_RPMB; + const unsigned int mask = EXT_CSD_PART_CONFIG_ACC_MASK; + const unsigned int rpmb = EXT_CSD_PART_CONFIG_ACC_RPMB; int ret = 0; - if ((part_type & mask) == mask) { + if ((part_type & mask) == rpmb) { if (card->ext_csd.cmdq_en) { ret = mmc_cmdq_disable(card); if (ret) @@ -892,10 +893,11 @@ static int mmc_blk_part_switch_pre(struct mmc_card *card, static int mmc_blk_part_switch_post(struct mmc_card *card, unsigned int part_type) { - const unsigned int mask = EXT_CSD_PART_CONFIG_ACC_RPMB; + const unsigned int mask = EXT_CSD_PART_CONFIG_ACC_MASK; + const unsigned int rpmb = EXT_CSD_PART_CONFIG_ACC_RPMB; int ret = 0; - if ((part_type & mask) == mask) { + if ((part_type & mask) == rpmb) { mmc_retune_unpause(card->host); if (card->reenable_cmdq && !card->ext_csd.cmdq_en) ret = mmc_cmdq_enable(card); From faf3b8014c357d71c7a9414302e217a1dd1679af Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Tue, 5 Mar 2024 16:49:30 -0300 Subject: [PATCH 44/44] mmc: core: make mmc_host_class constant Since commit 43a7206b0963 ("driver core: class: make class_register() take a const *"), the driver core allows for struct class to be in read-only memory, so move the mmc_host_class structure to be declared at build time placing it into read-only memory, instead of having to be dynamically allocated at boot time. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Link: https://lore.kernel.org/r/20240305-class_cleanup-mmc-v1-1-4a66e7122ff3@marliere.net Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 3aa9dda6fd4f..8f8781d6c25e 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -88,7 +88,7 @@ static int mmc_host_classdev_shutdown(struct device *dev) return 0; } -static struct class mmc_host_class = { +static const struct class mmc_host_class = { .name = "mmc_host", .dev_release = mmc_host_classdev_release, .shutdown_pre = mmc_host_classdev_shutdown,