Commit 57c0902f authored by Kaustabh Chakraborty's avatar Kaustabh Chakraborty Committed by Ulf Hansson
Browse files

mmc: dw_mmc: add a quirk for accessing 64-bit FIFOs in two halves



In certain DW MMC implementations (such as in some Exynos7870
controllers), 64-bit read/write is not allowed from a 64-bit FIFO.
Add a quirk which facilitates accessing the 64-bit FIFO registers in two
32-bit halves.

Signed-off-by: default avatarKaustabh Chakraborty <kauschluss@disroot.org>
Link: https://lore.kernel.org/r/20250219-exynos7870-mmc-v2-2-b4255a3e39ed@disroot.org


Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent 63dde9c3
Loading
Loading
Loading
Loading
+92 −2
Original line number Diff line number Diff line
@@ -2579,6 +2579,91 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
	}
}

static void dw_mci_push_data64_32(struct dw_mci *host, void *buf, int cnt)
{
	struct mmc_data *data = host->data;
	int init_cnt = cnt;

	/* try and push anything in the part_buf */
	if (unlikely(host->part_buf_count)) {
		int len = dw_mci_push_part_bytes(host, buf, cnt);

		buf += len;
		cnt -= len;

		if (host->part_buf_count == 8) {
			mci_fifo_l_writeq(host->fifo_reg, host->part_buf);
			host->part_buf_count = 0;
		}
	}
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
	if (unlikely((unsigned long)buf & 0x7)) {
		while (cnt >= 8) {
			u64 aligned_buf[16];
			int len = min(cnt & -8, (int)sizeof(aligned_buf));
			int items = len >> 3;
			int i;
			/* memcpy from input buffer into aligned buffer */
			memcpy(aligned_buf, buf, len);
			buf += len;
			cnt -= len;
			/* push data from aligned buffer into fifo */
			for (i = 0; i < items; ++i)
				mci_fifo_l_writeq(host->fifo_reg, aligned_buf[i]);
		}
	} else
#endif
	{
		u64 *pdata = buf;

		for (; cnt >= 8; cnt -= 8)
			mci_fifo_l_writeq(host->fifo_reg, *pdata++);
		buf = pdata;
	}
	/* put anything remaining in the part_buf */
	if (cnt) {
		dw_mci_set_part_bytes(host, buf, cnt);
		/* Push data if we have reached the expected data length */
		if ((data->bytes_xfered + init_cnt) ==
		    (data->blksz * data->blocks))
			mci_fifo_l_writeq(host->fifo_reg, host->part_buf);
	}
}

static void dw_mci_pull_data64_32(struct dw_mci *host, void *buf, int cnt)
{
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
	if (unlikely((unsigned long)buf & 0x7)) {
		while (cnt >= 8) {
			/* pull data from fifo into aligned buffer */
			u64 aligned_buf[16];
			int len = min(cnt & -8, (int)sizeof(aligned_buf));
			int items = len >> 3;
			int i;

			for (i = 0; i < items; ++i)
				aligned_buf[i] = mci_fifo_l_readq(host->fifo_reg);

			/* memcpy from aligned buffer into output buffer */
			memcpy(buf, aligned_buf, len);
			buf += len;
			cnt -= len;
		}
	} else
#endif
	{
		u64 *pdata = buf;

		for (; cnt >= 8; cnt -= 8)
			*pdata++ = mci_fifo_l_readq(host->fifo_reg);
		buf = pdata;
	}
	if (cnt) {
		host->part_buf = mci_fifo_l_readq(host->fifo_reg);
		dw_mci_pull_final_bytes(host, buf, cnt);
	}
}

static void dw_mci_pull_data(struct dw_mci *host, void *buf, int cnt)
{
	int len;
@@ -3379,8 +3464,13 @@ int dw_mci_probe(struct dw_mci *host)
		width = 16;
		host->data_shift = 1;
	} else if (i == 2) {
		if ((host->quirks & DW_MMC_QUIRK_FIFO64_32)) {
			host->push_data = dw_mci_push_data64_32;
			host->pull_data = dw_mci_pull_data64_32;
		} else {
			host->push_data = dw_mci_push_data64;
			host->pull_data = dw_mci_pull_data64;
		}
		width = 64;
		host->data_shift = 3;
	} else {
+27 −0
Original line number Diff line number Diff line
@@ -281,6 +281,8 @@ struct dw_mci_board {

/* Support for longer data read timeout */
#define DW_MMC_QUIRK_EXTENDED_TMOUT            BIT(0)
/* Force 32-bit access to the FIFO */
#define DW_MMC_QUIRK_FIFO64_32                 BIT(1)

#define DW_MMC_240A		0x240a
#define DW_MMC_280A		0x280a
@@ -472,6 +474,31 @@ struct dw_mci_board {
#define mci_fifo_writel(__value, __reg)	__raw_writel(__reg, __value)
#define mci_fifo_writeq(__value, __reg)	__raw_writeq(__reg, __value)

/*
 * Some dw_mmc devices have 64-bit FIFOs, but expect them to be
 * accessed using two 32-bit accesses. If such controller is used
 * with a 64-bit kernel, this has to be done explicitly.
 */
static inline u64 mci_fifo_l_readq(void __iomem *addr)
{
	u64 ans;
	u32 proxy[2];

	proxy[0] = mci_fifo_readl(addr);
	proxy[1] = mci_fifo_readl(addr + 4);
	memcpy(&ans, proxy, 8);
	return ans;
}

static inline void mci_fifo_l_writeq(void __iomem *addr, u64 value)
{
	u32 proxy[2];

	memcpy(proxy, &value, 8);
	mci_fifo_writel(addr, proxy[0]);
	mci_fifo_writel(addr + 4, proxy[1]);
}

/* Register access macros */
#define mci_readl(dev, reg)			\
	readl_relaxed((dev)->regs + SDMMC_##reg)