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

Configure Feed

Select the types of activity you want to include in your feed.

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: 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>

authored by

Kaustabh Chakraborty and committed by
Ulf Hansson
57c0902f 63dde9c3

+119 -2
+92 -2
drivers/mmc/host/dw_mmc.c
··· 2579 2579 } 2580 2580 } 2581 2581 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 + 2582 2667 static void dw_mci_pull_data(struct dw_mci *host, void *buf, int cnt) 2583 2668 { 2584 2669 int len; ··· 3464 3379 width = 16; 3465 3380 host->data_shift = 1; 3466 3381 } else if (i == 2) { 3467 - host->push_data = dw_mci_push_data64; 3468 - host->pull_data = dw_mci_pull_data64; 3382 + if ((host->quirks & DW_MMC_QUIRK_FIFO64_32)) { 3383 + host->push_data = dw_mci_push_data64_32; 3384 + host->pull_data = dw_mci_pull_data64_32; 3385 + } else { 3386 + host->push_data = dw_mci_push_data64; 3387 + host->pull_data = dw_mci_pull_data64; 3388 + } 3469 3389 width = 64; 3470 3390 host->data_shift = 3; 3471 3391 } else {
+27
drivers/mmc/host/dw_mmc.h
··· 281 281 282 282 /* Support for longer data read timeout */ 283 283 #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) 284 286 285 287 #define DW_MMC_240A 0x240a 286 288 #define DW_MMC_280A 0x280a ··· 473 471 #define mci_fifo_writew(__value, __reg) __raw_writew(__reg, __value) 474 472 #define mci_fifo_writel(__value, __reg) __raw_writel(__reg, __value) 475 473 #define mci_fifo_writeq(__value, __reg) __raw_writeq(__reg, __value) 474 + 475 + /* 476 + * Some dw_mmc devices have 64-bit FIFOs, but expect them to be 477 + * accessed using two 32-bit accesses. If such controller is used 478 + * with a 64-bit kernel, this has to be done explicitly. 479 + */ 480 + static inline u64 mci_fifo_l_readq(void __iomem *addr) 481 + { 482 + u64 ans; 483 + u32 proxy[2]; 484 + 485 + proxy[0] = mci_fifo_readl(addr); 486 + proxy[1] = mci_fifo_readl(addr + 4); 487 + memcpy(&ans, proxy, 8); 488 + return ans; 489 + } 490 + 491 + static inline void mci_fifo_l_writeq(void __iomem *addr, u64 value) 492 + { 493 + u32 proxy[2]; 494 + 495 + memcpy(proxy, &value, 8); 496 + mci_fifo_writel(addr, proxy[0]); 497 + mci_fifo_writel(addr + 4, proxy[1]); 498 + } 476 499 477 500 /* Register access macros */ 478 501 #define mci_readl(dev, reg) \