|
@@ -57,8 +57,8 @@
|
|
|
#include <linux/of_address.h>
|
|
|
#include <linux/debugfs.h>
|
|
|
#include <linux/log2.h>
|
|
|
-#include <linux/syscore_ops.h>
|
|
|
#include <linux/memblock.h>
|
|
|
+#include <linux/syscore_ops.h>
|
|
|
|
|
|
/*
|
|
|
* DDR target is the same on all platforms.
|
|
@@ -70,6 +70,7 @@
|
|
|
*/
|
|
|
#define WIN_CTRL_OFF 0x0000
|
|
|
#define WIN_CTRL_ENABLE BIT(0)
|
|
|
+/* Only on HW I/O coherency capable platforms */
|
|
|
#define WIN_CTRL_SYNCBARRIER BIT(1)
|
|
|
#define WIN_CTRL_TGT_MASK 0xf0
|
|
|
#define WIN_CTRL_TGT_SHIFT 4
|
|
@@ -102,9 +103,7 @@
|
|
|
|
|
|
/* 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
|
|
@@ -154,13 +153,39 @@ struct mvebu_mbus_state {
|
|
|
|
|
|
static struct mvebu_mbus_state mbus_state;
|
|
|
|
|
|
+/*
|
|
|
+ * We provide two variants of the mv_mbus_dram_info() function:
|
|
|
+ *
|
|
|
+ * - The normal one, where the described DRAM ranges may overlap with
|
|
|
+ * the I/O windows, but for which the DRAM ranges are guaranteed to
|
|
|
+ * have a power of two size. Such ranges are suitable for the DMA
|
|
|
+ * masters that only DMA between the RAM and the device, which is
|
|
|
+ * actually all devices except the crypto engines.
|
|
|
+ *
|
|
|
+ * - The 'nooverlap' one, where the described DRAM ranges are
|
|
|
+ * guaranteed to not overlap with the I/O windows, but for which the
|
|
|
+ * DRAM ranges will not have power of two sizes. They will only be
|
|
|
+ * aligned on a 64 KB boundary, and have a size multiple of 64
|
|
|
+ * KB. Such ranges are suitable for the DMA masters that DMA between
|
|
|
+ * the crypto SRAM (which is mapped through an I/O window) and a
|
|
|
+ * device. This is the case for the crypto engines.
|
|
|
+ */
|
|
|
+
|
|
|
static struct mbus_dram_target_info mvebu_mbus_dram_info;
|
|
|
+static struct mbus_dram_target_info mvebu_mbus_dram_info_nooverlap;
|
|
|
+
|
|
|
const struct mbus_dram_target_info *mv_mbus_dram_info(void)
|
|
|
{
|
|
|
return &mvebu_mbus_dram_info;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(mv_mbus_dram_info);
|
|
|
|
|
|
+const struct mbus_dram_target_info *mv_mbus_dram_info_nooverlap(void)
|
|
|
+{
|
|
|
+ return &mvebu_mbus_dram_info_nooverlap;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(mv_mbus_dram_info_nooverlap);
|
|
|
+
|
|
|
/* Checks whether the given window has remap capability */
|
|
|
static bool mvebu_mbus_window_is_remappable(struct mvebu_mbus_state *mbus,
|
|
|
const int win)
|
|
@@ -323,8 +348,9 @@ static int mvebu_mbus_setup_window(struct mvebu_mbus_state *mbus,
|
|
|
ctrl = ((size - 1) & WIN_CTRL_SIZE_MASK) |
|
|
|
(attr << WIN_CTRL_ATTR_SHIFT) |
|
|
|
(target << WIN_CTRL_TGT_SHIFT) |
|
|
|
- WIN_CTRL_SYNCBARRIER |
|
|
|
WIN_CTRL_ENABLE;
|
|
|
+ if (mbus->hw_io_coherency)
|
|
|
+ ctrl |= WIN_CTRL_SYNCBARRIER;
|
|
|
|
|
|
writel(base & WIN_BASE_LOW, addr + WIN_BASE_OFF);
|
|
|
writel(ctrl, addr + WIN_CTRL_OFF);
|
|
@@ -592,7 +618,7 @@ mvebu_mbus_find_bridge_hole(uint64_t *start, uint64_t *end)
|
|
|
* This part of the memory is above 4 GB, so we don't
|
|
|
* care for the MBus bridge hole.
|
|
|
*/
|
|
|
- if (r->base >= 0x100000000)
|
|
|
+ if (r->base >= 0x100000000ULL)
|
|
|
continue;
|
|
|
|
|
|
/*
|
|
@@ -604,49 +630,32 @@ mvebu_mbus_find_bridge_hole(uint64_t *start, uint64_t *end)
|
|
|
}
|
|
|
|
|
|
*start = s;
|
|
|
- *end = 0x100000000;
|
|
|
+ *end = 0x100000000ULL;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * This function fills in the mvebu_mbus_dram_info_nooverlap data
|
|
|
+ * structure, by looking at the mvebu_mbus_dram_info data, and
|
|
|
+ * removing the parts of it that overlap with I/O windows.
|
|
|
+ */
|
|
|
static void __init
|
|
|
-mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
|
|
|
+mvebu_mbus_setup_cpu_target_nooverlap(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;
|
|
|
+ int cs_nooverlap = 0;
|
|
|
+ int i;
|
|
|
|
|
|
mvebu_mbus_find_bridge_hole(&mbus_bridge_base, &mbus_bridge_end);
|
|
|
|
|
|
- for (i = 0, cs = 0; i < 4; 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;
|
|
|
+ for (i = 0; i < mvebu_mbus_dram_info.num_cs; i++) {
|
|
|
struct mbus_dram_window *w;
|
|
|
+ u64 base, size, end;
|
|
|
|
|
|
- /* Ignore entries that are not enabled */
|
|
|
- if (!(size & DDR_SIZE_ENABLED))
|
|
|
- continue;
|
|
|
-
|
|
|
- /*
|
|
|
- * Ignore entries whose base address is above 2^32,
|
|
|
- * since devices cannot DMA to such high addresses
|
|
|
- */
|
|
|
- if (base & DDR_BASE_CS_HIGH_MASK)
|
|
|
- continue;
|
|
|
-
|
|
|
- base = base & DDR_BASE_CS_LOW_MASK;
|
|
|
- size = (size | ~DDR_SIZE_MASK) + 1;
|
|
|
+ w = &mvebu_mbus_dram_info.cs[i];
|
|
|
+ base = w->base;
|
|
|
+ size = w->size;
|
|
|
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.
|
|
@@ -670,7 +679,7 @@ mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
|
|
|
if (base < mbus_bridge_base && end > mbus_bridge_base)
|
|
|
size -= end - mbus_bridge_base;
|
|
|
|
|
|
- w = &mvebu_mbus_dram_info.cs[cs++];
|
|
|
+ w = &mvebu_mbus_dram_info_nooverlap.cs[cs_nooverlap++];
|
|
|
w->cs_index = i;
|
|
|
w->mbus_attr = 0xf & ~(1 << i);
|
|
|
if (mbus->hw_io_coherency)
|
|
@@ -678,6 +687,42 @@ mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
|
|
|
w->base = base;
|
|
|
w->size = size;
|
|
|
}
|
|
|
+
|
|
|
+ mvebu_mbus_dram_info_nooverlap.mbus_dram_target_id = TARGET_DDR;
|
|
|
+ mvebu_mbus_dram_info_nooverlap.num_cs = cs_nooverlap;
|
|
|
+}
|
|
|
+
|
|
|
+static void __init
|
|
|
+mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ int cs;
|
|
|
+
|
|
|
+ mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR;
|
|
|
+
|
|
|
+ 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));
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 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).
|
|
|
+ */
|
|
|
+ if ((size & DDR_SIZE_ENABLED) &&
|
|
|
+ !(base & DDR_BASE_CS_HIGH_MASK)) {
|
|
|
+ struct mbus_dram_window *w;
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
mvebu_mbus_dram_info.num_cs = cs;
|
|
|
}
|
|
|
|
|
@@ -1035,6 +1080,7 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
|
|
|
mvebu_mbus_disable_window(mbus, win);
|
|
|
|
|
|
mbus->soc->setup_cpu_target(mbus);
|
|
|
+ mvebu_mbus_setup_cpu_target_nooverlap(mbus);
|
|
|
|
|
|
if (is_coherent)
|
|
|
writel(UNIT_SYNC_BARRIER_ALL,
|