Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mmc: meson-gx: do not use memcpy_to/fromio for dram-access-quirk

The memory at the end of the controller only accepts 32bit read/write
accesses, but the arm64 memcpy_to/fromio implementation only uses 64bit
(which will be split into two 32bit access) and 8bit leading to incomplete
copies to/from this memory when the buffer is not multiple of 8bytes.

Add a local copy using writel/readl accesses to make sure we use the right
memory access width.

The switch to memcpy_to/fromio was done because of 285133040e6c
("arm64: Import latest memcpy()/memmove() implementation"), but using memcpy
worked before since it mainly used 32bit memory acceses.

Fixes: 103a5348c22c ("mmc: meson-gx: use memcpy_to/fromio for dram-access-quirk")
Reported-by: Christian Hewitt <christianshewitt@gmail.com>
Suggested-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Tested-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20210928073652.434690-1-narmstrong@baylibre.com
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

authored by

Neil Armstrong and committed by
Ulf Hansson
8a38a4d5 30d4b990

+59 -14
+59 -14
drivers/mmc/host/meson-gx-mmc.c
··· 746 746 writel(start, host->regs + SD_EMMC_START); 747 747 } 748 748 749 - /* local sg copy to buffer version with _to/fromio usage for dram_access_quirk */ 749 + /* local sg copy for dram_access_quirk */ 750 750 static void meson_mmc_copy_buffer(struct meson_host *host, struct mmc_data *data, 751 751 size_t buflen, bool to_buffer) 752 752 { ··· 764 764 sg_miter_start(&miter, sgl, nents, sg_flags); 765 765 766 766 while ((offset < buflen) && sg_miter_next(&miter)) { 767 - unsigned int len; 767 + unsigned int buf_offset = 0; 768 + unsigned int len, left; 769 + u32 *buf = miter.addr; 768 770 769 771 len = min(miter.length, buflen - offset); 772 + left = len; 770 773 771 - /* When dram_access_quirk, the bounce buffer is a iomem mapping */ 772 - if (host->dram_access_quirk) { 773 - if (to_buffer) 774 - memcpy_toio(host->bounce_iomem_buf + offset, miter.addr, len); 775 - else 776 - memcpy_fromio(miter.addr, host->bounce_iomem_buf + offset, len); 774 + if (to_buffer) { 775 + do { 776 + writel(*buf++, host->bounce_iomem_buf + offset + buf_offset); 777 + 778 + buf_offset += 4; 779 + left -= 4; 780 + } while (left); 777 781 } else { 778 - if (to_buffer) 779 - memcpy(host->bounce_buf + offset, miter.addr, len); 780 - else 781 - memcpy(miter.addr, host->bounce_buf + offset, len); 782 + do { 783 + *buf++ = readl(host->bounce_iomem_buf + offset + buf_offset); 784 + 785 + buf_offset += 4; 786 + left -= 4; 787 + } while (left); 782 788 } 783 789 784 790 offset += len; ··· 836 830 if (data->flags & MMC_DATA_WRITE) { 837 831 cmd_cfg |= CMD_CFG_DATA_WR; 838 832 WARN_ON(xfer_bytes > host->bounce_buf_size); 839 - meson_mmc_copy_buffer(host, data, xfer_bytes, true); 833 + if (host->dram_access_quirk) 834 + meson_mmc_copy_buffer(host, data, xfer_bytes, true); 835 + else 836 + sg_copy_to_buffer(data->sg, data->sg_len, 837 + host->bounce_buf, xfer_bytes); 840 838 dma_wmb(); 841 839 } 842 840 ··· 859 849 writel(cmd->arg, host->regs + SD_EMMC_CMD_ARG); 860 850 } 861 851 852 + static int meson_mmc_validate_dram_access(struct mmc_host *mmc, struct mmc_data *data) 853 + { 854 + struct scatterlist *sg; 855 + int i; 856 + 857 + /* Reject request if any element offset or size is not 32bit aligned */ 858 + for_each_sg(data->sg, sg, data->sg_len, i) { 859 + if (!IS_ALIGNED(sg->offset, sizeof(u32)) || 860 + !IS_ALIGNED(sg->length, sizeof(u32))) { 861 + dev_err(mmc_dev(mmc), "unaligned sg offset %u len %u\n", 862 + data->sg->offset, data->sg->length); 863 + return -EINVAL; 864 + } 865 + } 866 + 867 + return 0; 868 + } 869 + 862 870 static void meson_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) 863 871 { 864 872 struct meson_host *host = mmc_priv(mmc); 865 873 bool needs_pre_post_req = mrq->data && 866 874 !(mrq->data->host_cookie & SD_EMMC_PRE_REQ_DONE); 875 + 876 + /* 877 + * The memory at the end of the controller used as bounce buffer for 878 + * the dram_access_quirk only accepts 32bit read/write access, 879 + * check the aligment and length of the data before starting the request. 880 + */ 881 + if (host->dram_access_quirk && mrq->data) { 882 + mrq->cmd->error = meson_mmc_validate_dram_access(mmc, mrq->data); 883 + if (mrq->cmd->error) { 884 + mmc_request_done(mmc, mrq); 885 + return; 886 + } 887 + } 867 888 868 889 if (needs_pre_post_req) { 869 890 meson_mmc_get_transfer_mode(mmc, mrq); ··· 1040 999 if (meson_mmc_bounce_buf_read(data)) { 1041 1000 xfer_bytes = data->blksz * data->blocks; 1042 1001 WARN_ON(xfer_bytes > host->bounce_buf_size); 1043 - meson_mmc_copy_buffer(host, data, xfer_bytes, false); 1002 + if (host->dram_access_quirk) 1003 + meson_mmc_copy_buffer(host, data, xfer_bytes, false); 1004 + else 1005 + sg_copy_from_buffer(data->sg, data->sg_len, 1006 + host->bounce_buf, xfer_bytes); 1044 1007 } 1045 1008 1046 1009 next_cmd = meson_mmc_get_next_command(cmd);