Skip to content

Commit fe74885

Browse files
Kaustabh Chakrabortygregkh
authored andcommitted
mmc: dw_mmc: add a quirk for accessing 64-bit FIFOs in two halves
[ 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>
1 parent 6d32a30 commit fe74885

File tree

2 files changed

+119
-2
lines changed

2 files changed

+119
-2
lines changed

drivers/mmc/host/dw_mmc.c

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2579,6 +2579,91 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
25792579
}
25802580
}
25812581

2582+
static void dw_mci_push_data64_32(struct dw_mci *host, void *buf, int cnt)
2583+
{
2584+
struct mmc_data *data = host->data;
2585+
int init_cnt = cnt;
2586+
2587+
/* try and push anything in the part_buf */
2588+
if (unlikely(host->part_buf_count)) {
2589+
int len = dw_mci_push_part_bytes(host, buf, cnt);
2590+
2591+
buf += len;
2592+
cnt -= len;
2593+
2594+
if (host->part_buf_count == 8) {
2595+
mci_fifo_l_writeq(host->fifo_reg, host->part_buf);
2596+
host->part_buf_count = 0;
2597+
}
2598+
}
2599+
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
2600+
if (unlikely((unsigned long)buf & 0x7)) {
2601+
while (cnt >= 8) {
2602+
u64 aligned_buf[16];
2603+
int len = min(cnt & -8, (int)sizeof(aligned_buf));
2604+
int items = len >> 3;
2605+
int i;
2606+
/* memcpy from input buffer into aligned buffer */
2607+
memcpy(aligned_buf, buf, len);
2608+
buf += len;
2609+
cnt -= len;
2610+
/* push data from aligned buffer into fifo */
2611+
for (i = 0; i < items; ++i)
2612+
mci_fifo_l_writeq(host->fifo_reg, aligned_buf[i]);
2613+
}
2614+
} else
2615+
#endif
2616+
{
2617+
u64 *pdata = buf;
2618+
2619+
for (; cnt >= 8; cnt -= 8)
2620+
mci_fifo_l_writeq(host->fifo_reg, *pdata++);
2621+
buf = pdata;
2622+
}
2623+
/* put anything remaining in the part_buf */
2624+
if (cnt) {
2625+
dw_mci_set_part_bytes(host, buf, cnt);
2626+
/* Push data if we have reached the expected data length */
2627+
if ((data->bytes_xfered + init_cnt) ==
2628+
(data->blksz * data->blocks))
2629+
mci_fifo_l_writeq(host->fifo_reg, host->part_buf);
2630+
}
2631+
}
2632+
2633+
static void dw_mci_pull_data64_32(struct dw_mci *host, void *buf, int cnt)
2634+
{
2635+
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
2636+
if (unlikely((unsigned long)buf & 0x7)) {
2637+
while (cnt >= 8) {
2638+
/* pull data from fifo into aligned buffer */
2639+
u64 aligned_buf[16];
2640+
int len = min(cnt & -8, (int)sizeof(aligned_buf));
2641+
int items = len >> 3;
2642+
int i;
2643+
2644+
for (i = 0; i < items; ++i)
2645+
aligned_buf[i] = mci_fifo_l_readq(host->fifo_reg);
2646+
2647+
/* memcpy from aligned buffer into output buffer */
2648+
memcpy(buf, aligned_buf, len);
2649+
buf += len;
2650+
cnt -= len;
2651+
}
2652+
} else
2653+
#endif
2654+
{
2655+
u64 *pdata = buf;
2656+
2657+
for (; cnt >= 8; cnt -= 8)
2658+
*pdata++ = mci_fifo_l_readq(host->fifo_reg);
2659+
buf = pdata;
2660+
}
2661+
if (cnt) {
2662+
host->part_buf = mci_fifo_l_readq(host->fifo_reg);
2663+
dw_mci_pull_final_bytes(host, buf, cnt);
2664+
}
2665+
}
2666+
25822667
static void dw_mci_pull_data(struct dw_mci *host, void *buf, int cnt)
25832668
{
25842669
int len;
@@ -3379,8 +3464,13 @@ int dw_mci_probe(struct dw_mci *host)
33793464
width = 16;
33803465
host->data_shift = 1;
33813466
} else if (i == 2) {
3382-
host->push_data = dw_mci_push_data64;
3383-
host->pull_data = dw_mci_pull_data64;
3467+
if ((host->quirks & DW_MMC_QUIRK_FIFO64_32)) {
3468+
host->push_data = dw_mci_push_data64_32;
3469+
host->pull_data = dw_mci_pull_data64_32;
3470+
} else {
3471+
host->push_data = dw_mci_push_data64;
3472+
host->pull_data = dw_mci_pull_data64;
3473+
}
33843474
width = 64;
33853475
host->data_shift = 3;
33863476
} else {

drivers/mmc/host/dw_mmc.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,8 @@ struct dw_mci_board {
281281

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

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

477+
/*
478+
* Some dw_mmc devices have 64-bit FIFOs, but expect them to be
479+
* accessed using two 32-bit accesses. If such controller is used
480+
* with a 64-bit kernel, this has to be done explicitly.
481+
*/
482+
static inline u64 mci_fifo_l_readq(void __iomem *addr)
483+
{
484+
u64 ans;
485+
u32 proxy[2];
486+
487+
proxy[0] = mci_fifo_readl(addr);
488+
proxy[1] = mci_fifo_readl(addr + 4);
489+
memcpy(&ans, proxy, 8);
490+
return ans;
491+
}
492+
493+
static inline void mci_fifo_l_writeq(void __iomem *addr, u64 value)
494+
{
495+
u32 proxy[2];
496+
497+
memcpy(proxy, &value, 8);
498+
mci_fifo_writel(addr, proxy[0]);
499+
mci_fifo_writel(addr + 4, proxy[1]);
500+
}
501+
475502
/* Register access macros */
476503
#define mci_readl(dev, reg) \
477504
readl_relaxed((dev)->regs + SDMMC_##reg)

0 commit comments

Comments
 (0)