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

bus: mvebu-mbus: add mv_mbus_dram_info_nooverlap()

This commit introduces a variant of the mv_mbus_dram_info() function
called mv_mbus_dram_info_nooverlap(). Both functions are used by
Marvell drivers supporting devices doing DMA, and provide them a
description the DRAM ranges that they need to configure their DRAM
windows.

The ranges provided by the mv_mbus_dram_info() function may overlap
with the I/O windows if there is a lot (>= 4 GB) of RAM
installed. This is not a problem for most of the DMA masters, except
for the upcoming new CESA crypto driver because it does DMA to the
SRAM, which is mapped through an I/O window. For this unit, we need to
have DRAM ranges that do not overlap with the I/O windows.

A first implementation done in commit 1737cac69369 ("bus: mvebu-mbus:
make sure SDRAM CS for DMA don't overlap the MBus bridge window"),
changed the information returned by mv_mbus_dram_info() to match this
requirement. However, it broke the requirement of the other DMA
masters than the DRAM ranges should have power of two sizes.

To solve this situation, this commit introduces a new
mv_mbus_dram_info_nooverlap() function, which returns the same
information as mv_mbus_dram_info(), but guaranteed to not overlap with
the I/O windows.

In the end, it gives us two variants of the mv_mbus_dram_info*()
functions:

- The normal one, mv_mbus_dram_info(), which has been around for many
years. This function returns the raw DRAM ranges, which are
guaranteed to use power of two sizes, but will overlap with I/O
windows. This function will therefore be used by all DMA masters
(SATA, XOR, Ethernet, etc.) except the CESA crypto driver.

- The new 'nooverlap' variant, mv_mbus_dram_info_nooverlap(). This
function returns DRAM ranges after they have been "tweaked" to make
sure they don't overlap with I/O windows. By doing this tweaking,
we remove the power of two size guarantee. This variant will be
used by the new CESA crypto driver.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>

authored by

Thomas Petazzoni and committed by
Gregory CLEMENT
bfa1ce5f 885dbd15

+122
+117
drivers/bus/mvebu-mbus.c
··· 57 57 #include <linux/of_address.h> 58 58 #include <linux/debugfs.h> 59 59 #include <linux/log2.h> 60 + #include <linux/memblock.h> 60 61 #include <linux/syscore_ops.h> 61 62 62 63 /* ··· 153 152 154 153 static struct mvebu_mbus_state mbus_state; 155 154 155 + /* 156 + * We provide two variants of the mv_mbus_dram_info() function: 157 + * 158 + * - The normal one, where the described DRAM ranges may overlap with 159 + * the I/O windows, but for which the DRAM ranges are guaranteed to 160 + * have a power of two size. Such ranges are suitable for the DMA 161 + * masters that only DMA between the RAM and the device, which is 162 + * actually all devices except the crypto engines. 163 + * 164 + * - The 'nooverlap' one, where the described DRAM ranges are 165 + * guaranteed to not overlap with the I/O windows, but for which the 166 + * DRAM ranges will not have power of two sizes. They will only be 167 + * aligned on a 64 KB boundary, and have a size multiple of 64 168 + * KB. Such ranges are suitable for the DMA masters that DMA between 169 + * the crypto SRAM (which is mapped through an I/O window) and a 170 + * device. This is the case for the crypto engines. 171 + */ 172 + 156 173 static struct mbus_dram_target_info mvebu_mbus_dram_info; 174 + static struct mbus_dram_target_info mvebu_mbus_dram_info_nooverlap; 175 + 157 176 const struct mbus_dram_target_info *mv_mbus_dram_info(void) 158 177 { 159 178 return &mvebu_mbus_dram_info; 160 179 } 161 180 EXPORT_SYMBOL_GPL(mv_mbus_dram_info); 181 + 182 + const struct mbus_dram_target_info *mv_mbus_dram_info_nooverlap(void) 183 + { 184 + return &mvebu_mbus_dram_info_nooverlap; 185 + } 186 + EXPORT_SYMBOL_GPL(mv_mbus_dram_info_nooverlap); 162 187 163 188 /* Checks whether the given window has remap capability */ 164 189 static bool mvebu_mbus_window_is_remappable(struct mvebu_mbus_state *mbus, ··· 603 576 return MVEBU_MBUS_NO_REMAP; 604 577 } 605 578 579 + /* 580 + * Use the memblock information to find the MBus bridge hole in the 581 + * physical address space. 582 + */ 583 + static void __init 584 + mvebu_mbus_find_bridge_hole(uint64_t *start, uint64_t *end) 585 + { 586 + struct memblock_region *r; 587 + uint64_t s = 0; 588 + 589 + for_each_memblock(memory, r) { 590 + /* 591 + * This part of the memory is above 4 GB, so we don't 592 + * care for the MBus bridge hole. 593 + */ 594 + if (r->base >= 0x100000000ULL) 595 + continue; 596 + 597 + /* 598 + * The MBus bridge hole is at the end of the RAM under 599 + * the 4 GB limit. 600 + */ 601 + if (r->base + r->size > s) 602 + s = r->base + r->size; 603 + } 604 + 605 + *start = s; 606 + *end = 0x100000000ULL; 607 + } 608 + 609 + /* 610 + * This function fills in the mvebu_mbus_dram_info_nooverlap data 611 + * structure, by looking at the mvebu_mbus_dram_info data, and 612 + * removing the parts of it that overlap with I/O windows. 613 + */ 614 + static void __init 615 + mvebu_mbus_setup_cpu_target_nooverlap(struct mvebu_mbus_state *mbus) 616 + { 617 + uint64_t mbus_bridge_base, mbus_bridge_end; 618 + int cs_nooverlap = 0; 619 + int i; 620 + 621 + mvebu_mbus_find_bridge_hole(&mbus_bridge_base, &mbus_bridge_end); 622 + 623 + for (i = 0; i < mvebu_mbus_dram_info.num_cs; i++) { 624 + struct mbus_dram_window *w; 625 + u64 base, size, end; 626 + 627 + w = &mvebu_mbus_dram_info.cs[i]; 628 + base = w->base; 629 + size = w->size; 630 + end = base + size; 631 + 632 + /* 633 + * The CS is fully enclosed inside the MBus bridge 634 + * area, so ignore it. 635 + */ 636 + if (base >= mbus_bridge_base && end <= mbus_bridge_end) 637 + continue; 638 + 639 + /* 640 + * Beginning of CS overlaps with end of MBus, raise CS 641 + * base address, and shrink its size. 642 + */ 643 + if (base >= mbus_bridge_base && end > mbus_bridge_end) { 644 + size -= mbus_bridge_end - base; 645 + base = mbus_bridge_end; 646 + } 647 + 648 + /* 649 + * End of CS overlaps with beginning of MBus, shrink 650 + * CS size. 651 + */ 652 + if (base < mbus_bridge_base && end > mbus_bridge_base) 653 + size -= end - mbus_bridge_base; 654 + 655 + w = &mvebu_mbus_dram_info_nooverlap.cs[cs_nooverlap++]; 656 + w->cs_index = i; 657 + w->mbus_attr = 0xf & ~(1 << i); 658 + if (mbus->hw_io_coherency) 659 + w->mbus_attr |= ATTR_HW_COHERENCY; 660 + w->base = base; 661 + w->size = size; 662 + } 663 + 664 + mvebu_mbus_dram_info_nooverlap.mbus_dram_target_id = TARGET_DDR; 665 + mvebu_mbus_dram_info_nooverlap.num_cs = cs_nooverlap; 666 + } 667 + 606 668 static void __init 607 669 mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus) 608 670 { ··· 1080 964 mvebu_mbus_disable_window(mbus, win); 1081 965 1082 966 mbus->soc->setup_cpu_target(mbus); 967 + mvebu_mbus_setup_cpu_target_nooverlap(mbus); 1083 968 1084 969 if (is_coherent) 1085 970 writel(UNIT_SYNC_BARRIER_ALL,
+5
include/linux/mbus.h
··· 54 54 */ 55 55 #ifdef CONFIG_PLAT_ORION 56 56 extern const struct mbus_dram_target_info *mv_mbus_dram_info(void); 57 + extern const struct mbus_dram_target_info *mv_mbus_dram_info_nooverlap(void); 57 58 #else 58 59 static inline const struct mbus_dram_target_info *mv_mbus_dram_info(void) 60 + { 61 + return NULL; 62 + } 63 + static inline const struct mbus_dram_target_info *mv_mbus_dram_info_nooverlap(void) 59 64 { 60 65 return NULL; 61 66 }