Skip to content

Commit

Permalink
mmc: dw_mmc: add a quirk for accessing 64-bit FIFOs in two halves
Browse files Browse the repository at this point in the history
[ Upstream commit 57c0902 ]

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: Kaustabh Chakraborty <kauschluss@disroot.org>
Link: https://lore.kernel.org/r/20250219-exynos7870-mmc-v2-2-b4255a3e39ed@disroot.org
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
Kaustabh Chakraborty authored and Greg Kroah-Hartman committed Apr 25, 2025
1 parent 4e587fb commit 2de2827
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 2 deletions.
94 changes: 92 additions & 2 deletions drivers/mmc/host/dw_mmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2574,6 +2574,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;
Expand Down Expand Up @@ -3374,8 +3459,13 @@ int dw_mci_probe(struct dw_mci *host)
width = 16;
host->data_shift = 1;
} else if (i == 2) {
host->push_data = dw_mci_push_data64;
host->pull_data = dw_mci_pull_data64;
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 {
Expand Down
27 changes: 27 additions & 0 deletions drivers/mmc/host/dw_mmc.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,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
Expand Down Expand Up @@ -471,6 +473,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)
Expand Down

0 comments on commit 2de2827

Please sign in to comment.