mmc: dw_mmc: add a quirk for accessing 64-bit FIFOs in two halves
[ Upstream commit 57c0902f8bec51add5a1eb908d8b876592725d81 ] 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>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
6d32a30fa1
commit
fe74885e36
@@ -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) {
|
||||
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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user