[PATCH 4/6] bus: mvebu-mbus: make sure SDRAM CS for DMA don't overlap the MBus bridge window
Andrew Lunn
andrew at lunn.ch
Fri Jan 9 08:59:15 PST 2015
On Tue, Dec 30, 2014 at 01:43:45PM +0100, Thomas Petazzoni wrote:
> The mvebu-mbus driver reads the SDRAM window registers, and make the
> information about the DRAM CS configuration available to device
> drivers using the mv_mbus_dram_info() API. This information is used by
> the DMA-capable device drivers to program their address decoding
> windows.
>
> Until now, we were basically providing the SDRAM window register
> details as is. However, it turns out that the DMA capability of the
> CESA cryptographic engine consists in doing DMA being the DRAM and the
> crypto SRAM mapped as a MBus window. For this case, it is very
> important that the SDRAM CS information does not overlap with the MBus
> bridge window.
>
> Therefore, this commit improves the mvebu-mbus driver to make sure we
> adjust the SDRAM CS information so that it doesn't overlap with the
> MBus bridge window. This problem was reported by Boris Brezillon,
> while working on the mv_cesa driver for Armada 37x/38x/XP. We use the
> memblock memory information to know where the usable RAM is located,
> as this information is guaranteed to be correct on all SoC variants.
>
> We could have used the MBus bridge window registers on Armada 370/XP,
> but they are not really used on Armada 375/38x (Cortex-A9 based),
> since the PL310 L2 filtering is used instead to discriminate between
> RAM accesses and I/O accesses. Therefore, using the memblock
> information is more generic and works accross the different platforms.
>
> Reported-by: Boris Brezillon <boris.brezillon at free-electrons.com>
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
Hi Thomas
Added to mvebu/soc for the next merge window.
Andrew
> ---
> We have decided to not backport this commit to stable. Even though it
> is really fixing the DRAM CS information exposed to device drivers,
> there is no way in mainline for the current situation to cause any
> problem.
>
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
> ---
> drivers/bus/mvebu-mbus.c | 105 +++++++++++++++++++++++++++++++++++++++--------
> 1 file changed, 89 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
> index b6694df..bb0dd21 100644
> --- a/drivers/bus/mvebu-mbus.c
> +++ b/drivers/bus/mvebu-mbus.c
> @@ -58,6 +58,7 @@
> #include <linux/debugfs.h>
> #include <linux/log2.h>
> #include <linux/syscore_ops.h>
> +#include <linux/memblock.h>
>
> /*
> * DDR target is the same on all platforms.
> @@ -97,7 +98,9 @@
>
> /* Relative to mbusbridge_base */
> #define MBUS_BRIDGE_CTRL_OFF 0x0
> +#define MBUS_BRIDGE_SIZE_MASK 0xffff0000
> #define MBUS_BRIDGE_BASE_OFF 0x4
> +#define MBUS_BRIDGE_BASE_MASK 0xffff0000
>
> /* Maximum number of windows, for all known platforms */
> #define MBUS_WINS_MAX 20
> @@ -567,36 +570,106 @@ static unsigned int armada_xp_mbus_win_remap_offset(int win)
> return MVEBU_MBUS_NO_REMAP;
> }
>
> +/*
> + * Use the memblock information to find the MBus bridge hole in the
> + * physical address space.
> + */
> +static void __init
> +mvebu_mbus_find_bridge_hole(uint64_t *start, uint64_t *end)
> +{
> + struct memblock_region *r;
> + uint64_t s = 0;
> +
> + for_each_memblock(memory, r) {
> + /*
> + * This part of the memory is above 4 GB, so we don't
> + * care for the MBus bridge hole.
> + */
> + if (r->base >= 0x100000000)
> + continue;
> +
> + /*
> + * The MBus bridge hole is at the end of the RAM under
> + * the 4 GB limit.
> + */
> + if (r->base + r->size > s)
> + s = r->base + r->size;
> + }
> +
> + *start = s;
> + *end = 0x100000000;
> +}
> +
> static void __init
> mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
> {
> int i;
> int cs;
> + uint64_t mbus_bridge_base, mbus_bridge_end;
>
> mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR;
>
> + mvebu_mbus_find_bridge_hole(&mbus_bridge_base, &mbus_bridge_end);
> +
> for (i = 0, cs = 0; i < 4; i++) {
> - u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i));
> - u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i));
> + u64 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i));
> + u64 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i));
> + u64 end;
> + struct mbus_dram_window *w;
> +
> + /* Ignore entries that are not enabled */
> + if (!(size & DDR_SIZE_ENABLED))
> + continue;
>
> /*
> - * We only take care of entries for which the chip
> - * select is enabled, and that don't have high base
> - * address bits set (devices can only access the first
> - * 32 bits of the memory).
> + * Ignore entries whose base address is above 2^32,
> + * since devices cannot DMA to such high addresses
> */
> - if ((size & DDR_SIZE_ENABLED) &&
> - !(base & DDR_BASE_CS_HIGH_MASK)) {
> - struct mbus_dram_window *w;
> + if (base & DDR_BASE_CS_HIGH_MASK)
> + continue;
>
> - w = &mvebu_mbus_dram_info.cs[cs++];
> - w->cs_index = i;
> - w->mbus_attr = 0xf & ~(1 << i);
> - if (mbus->hw_io_coherency)
> - w->mbus_attr |= ATTR_HW_COHERENCY;
> - w->base = base & DDR_BASE_CS_LOW_MASK;
> - w->size = (size | ~DDR_SIZE_MASK) + 1;
> + base = base & DDR_BASE_CS_LOW_MASK;
> + size = (size | ~DDR_SIZE_MASK) + 1;
> + end = base + size;
> +
> + /*
> + * Adjust base/size of the current CS to make sure it
> + * doesn't overlap with the MBus bridge hole. This is
> + * particularly important for devices that do DMA from
> + * DRAM to a SRAM mapped in a MBus window, such as the
> + * CESA cryptographic engine.
> + */
> +
> + /*
> + * The CS is fully enclosed inside the MBus bridge
> + * area, so ignore it.
> + */
> + if (base >= mbus_bridge_base && end <= mbus_bridge_end)
> + continue;
> +
> + /*
> + * Beginning of CS overlaps with end of MBus, raise CS
> + * base address, and shrink its size.
> + */
> + if (base >= mbus_bridge_base && end > mbus_bridge_end) {
> + size -= mbus_bridge_end - base;
> + base = mbus_bridge_end;
> }
> +
> + /*
> + * End of CS overlaps with beginning of MBus, shrink
> + * CS size.
> + */
> + if (base < mbus_bridge_base && end > mbus_bridge_base)
> + size -= end - mbus_bridge_base;
> +
> + w = &mvebu_mbus_dram_info.cs[cs++];
> + w->cs_index = i;
> + w->mbus_attr = 0xf & ~(1 << i);
> + if (mbus->hw_io_coherency)
> + w->mbus_attr |= ATTR_HW_COHERENCY;
> + w->base = base;
> + w->size = size;
> }
> mvebu_mbus_dram_info.num_cs = cs;
> }
> --
> 2.1.0
>
More information about the linux-arm-kernel
mailing list