aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/bus/mvebu-mbus.c
diff options
context:
space:
mode:
authorThomas Petazzoni <thomas.petazzoni@free-electrons.com>2015-01-09 11:59:04 -0500
committerAndrew Lunn <andrew@lunn.ch>2015-01-19 17:09:16 -0500
commit1737cac6936938a9bc52c03c4a3ff2032c702fa5 (patch)
tree7b9fe5e057ca940bab429cd17eaf9a1173494828 /drivers/bus/mvebu-mbus.c
parent7fdf3d8a0316ce31f87513f903addcb8f3b0dfb2 (diff)
bus: mvebu-mbus: make sure SDRAM CS for DMA don't overlap the MBus bridge window
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@free-electrons.com> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> [Andrew Lunn <andrew@lunn.ch>: Fixed merge conflict] Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Diffstat (limited to 'drivers/bus/mvebu-mbus.c')
-rw-r--r--drivers/bus/mvebu-mbus.c105
1 files changed, 89 insertions, 16 deletions
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
index a62c8ae253c3..fb9ec6221730 100644
--- a/drivers/bus/mvebu-mbus.c
+++ b/drivers/bus/mvebu-mbus.c
@@ -58,6 +58,7 @@
58#include <linux/debugfs.h> 58#include <linux/debugfs.h>
59#include <linux/log2.h> 59#include <linux/log2.h>
60#include <linux/syscore_ops.h> 60#include <linux/syscore_ops.h>
61#include <linux/memblock.h>
61 62
62/* 63/*
63 * DDR target is the same on all platforms. 64 * DDR target is the same on all platforms.
@@ -101,7 +102,9 @@
101 102
102/* Relative to mbusbridge_base */ 103/* Relative to mbusbridge_base */
103#define MBUS_BRIDGE_CTRL_OFF 0x0 104#define MBUS_BRIDGE_CTRL_OFF 0x0
105#define MBUS_BRIDGE_SIZE_MASK 0xffff0000
104#define MBUS_BRIDGE_BASE_OFF 0x4 106#define MBUS_BRIDGE_BASE_OFF 0x4
107#define MBUS_BRIDGE_BASE_MASK 0xffff0000
105 108
106/* Maximum number of windows, for all known platforms */ 109/* Maximum number of windows, for all known platforms */
107#define MBUS_WINS_MAX 20 110#define MBUS_WINS_MAX 20
@@ -574,36 +577,106 @@ static unsigned int armada_xp_mbus_win_remap_offset(int win)
574 return MVEBU_MBUS_NO_REMAP; 577 return MVEBU_MBUS_NO_REMAP;
575} 578}
576 579
580/*
581 * Use the memblock information to find the MBus bridge hole in the
582 * physical address space.
583 */
584static void __init
585mvebu_mbus_find_bridge_hole(uint64_t *start, uint64_t *end)
586{
587 struct memblock_region *r;
588 uint64_t s = 0;
589
590 for_each_memblock(memory, r) {
591 /*
592 * This part of the memory is above 4 GB, so we don't
593 * care for the MBus bridge hole.
594 */
595 if (r->base >= 0x100000000)
596 continue;
597
598 /*
599 * The MBus bridge hole is at the end of the RAM under
600 * the 4 GB limit.
601 */
602 if (r->base + r->size > s)
603 s = r->base + r->size;
604 }
605
606 *start = s;
607 *end = 0x100000000;
608}
609
577static void __init 610static void __init
578mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus) 611mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
579{ 612{
580 int i; 613 int i;
581 int cs; 614 int cs;
615 uint64_t mbus_bridge_base, mbus_bridge_end;
582 616
583 mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR; 617 mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR;
584 618
619 mvebu_mbus_find_bridge_hole(&mbus_bridge_base, &mbus_bridge_end);
620
585 for (i = 0, cs = 0; i < 4; i++) { 621 for (i = 0, cs = 0; i < 4; i++) {
586 u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i)); 622 u64 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i));
587 u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i)); 623 u64 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i));
624 u64 end;
625 struct mbus_dram_window *w;
626
627 /* Ignore entries that are not enabled */
628 if (!(size & DDR_SIZE_ENABLED))
629 continue;
588 630
589 /* 631 /*
590 * We only take care of entries for which the chip 632 * Ignore entries whose base address is above 2^32,
591 * select is enabled, and that don't have high base 633 * since devices cannot DMA to such high addresses
592 * address bits set (devices can only access the first
593 * 32 bits of the memory).
594 */ 634 */
595 if ((size & DDR_SIZE_ENABLED) && 635 if (base & DDR_BASE_CS_HIGH_MASK)
596 !(base & DDR_BASE_CS_HIGH_MASK)) { 636 continue;
597 struct mbus_dram_window *w;
598 637
599 w = &mvebu_mbus_dram_info.cs[cs++]; 638 base = base & DDR_BASE_CS_LOW_MASK;
600 w->cs_index = i; 639 size = (size | ~DDR_SIZE_MASK) + 1;
601 w->mbus_attr = 0xf & ~(1 << i); 640 end = base + size;
602 if (mbus->hw_io_coherency) 641
603 w->mbus_attr |= ATTR_HW_COHERENCY; 642 /*
604 w->base = base & DDR_BASE_CS_LOW_MASK; 643 * Adjust base/size of the current CS to make sure it
605 w->size = (size | ~DDR_SIZE_MASK) + 1; 644 * doesn't overlap with the MBus bridge hole. This is
645 * particularly important for devices that do DMA from
646 * DRAM to a SRAM mapped in a MBus window, such as the
647 * CESA cryptographic engine.
648 */
649
650 /*
651 * The CS is fully enclosed inside the MBus bridge
652 * area, so ignore it.
653 */
654 if (base >= mbus_bridge_base && end <= mbus_bridge_end)
655 continue;
656
657 /*
658 * Beginning of CS overlaps with end of MBus, raise CS
659 * base address, and shrink its size.
660 */
661 if (base >= mbus_bridge_base && end > mbus_bridge_end) {
662 size -= mbus_bridge_end - base;
663 base = mbus_bridge_end;
606 } 664 }
665
666 /*
667 * End of CS overlaps with beginning of MBus, shrink
668 * CS size.
669 */
670 if (base < mbus_bridge_base && end > mbus_bridge_base)
671 size -= end - mbus_bridge_base;
672
673 w = &mvebu_mbus_dram_info.cs[cs++];
674 w->cs_index = i;
675 w->mbus_attr = 0xf & ~(1 << i);
676 if (mbus->hw_io_coherency)
677 w->mbus_attr |= ATTR_HW_COHERENCY;
678 w->base = base;
679 w->size = size;
607 } 680 }
608 mvebu_mbus_dram_info.num_cs = cs; 681 mvebu_mbus_dram_info.num_cs = cs;
609} 682}