diff options
author | Thomas Petazzoni <thomas.petazzoni@free-electrons.com> | 2015-05-28 05:40:54 -0400 |
---|---|---|
committer | Gregory CLEMENT <gregory.clement@free-electrons.com> | 2015-05-28 06:21:08 -0400 |
commit | bfa1ce5f38938cc9e6c7f2d1011f88eba2b9e2b2 (patch) | |
tree | b4eb98e065a7342c9d02548df3e7ea22a09529b7 /drivers/bus | |
parent | 885dbd154b2f2ee305cec6fd0a162e1a77ae2b06 (diff) |
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>
Diffstat (limited to 'drivers/bus')
-rw-r--r-- | drivers/bus/mvebu-mbus.c | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 6f047dcb94c2..c43c3d2baf73 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c | |||
@@ -57,6 +57,7 @@ | |||
57 | #include <linux/of_address.h> | 57 | #include <linux/of_address.h> |
58 | #include <linux/debugfs.h> | 58 | #include <linux/debugfs.h> |
59 | #include <linux/log2.h> | 59 | #include <linux/log2.h> |
60 | #include <linux/memblock.h> | ||
60 | #include <linux/syscore_ops.h> | 61 | #include <linux/syscore_ops.h> |
61 | 62 | ||
62 | /* | 63 | /* |
@@ -152,13 +153,39 @@ struct mvebu_mbus_state { | |||
152 | 153 | ||
153 | static struct mvebu_mbus_state mbus_state; | 154 | static struct mvebu_mbus_state mbus_state; |
154 | 155 | ||
156 | /* | ||
157 | * We provide two variants of the mv_mbus_dram_info() function: | ||
158 | * | ||
159 | * - The normal one, where the described DRAM ranges may overlap with | ||
160 | * the I/O windows, but for which the DRAM ranges are guaranteed to | ||
161 | * have a power of two size. Such ranges are suitable for the DMA | ||
162 | * masters that only DMA between the RAM and the device, which is | ||
163 | * actually all devices except the crypto engines. | ||
164 | * | ||
165 | * - The 'nooverlap' one, where the described DRAM ranges are | ||
166 | * guaranteed to not overlap with the I/O windows, but for which the | ||
167 | * DRAM ranges will not have power of two sizes. They will only be | ||
168 | * aligned on a 64 KB boundary, and have a size multiple of 64 | ||
169 | * KB. Such ranges are suitable for the DMA masters that DMA between | ||
170 | * the crypto SRAM (which is mapped through an I/O window) and a | ||
171 | * device. This is the case for the crypto engines. | ||
172 | */ | ||
173 | |||
155 | static struct mbus_dram_target_info mvebu_mbus_dram_info; | 174 | static struct mbus_dram_target_info mvebu_mbus_dram_info; |
175 | static struct mbus_dram_target_info mvebu_mbus_dram_info_nooverlap; | ||
176 | |||
156 | const struct mbus_dram_target_info *mv_mbus_dram_info(void) | 177 | const struct mbus_dram_target_info *mv_mbus_dram_info(void) |
157 | { | 178 | { |
158 | return &mvebu_mbus_dram_info; | 179 | return &mvebu_mbus_dram_info; |
159 | } | 180 | } |
160 | EXPORT_SYMBOL_GPL(mv_mbus_dram_info); | 181 | EXPORT_SYMBOL_GPL(mv_mbus_dram_info); |
161 | 182 | ||
183 | const struct mbus_dram_target_info *mv_mbus_dram_info_nooverlap(void) | ||
184 | { | ||
185 | return &mvebu_mbus_dram_info_nooverlap; | ||
186 | } | ||
187 | EXPORT_SYMBOL_GPL(mv_mbus_dram_info_nooverlap); | ||
188 | |||
162 | /* Checks whether the given window has remap capability */ | 189 | /* Checks whether the given window has remap capability */ |
163 | static bool mvebu_mbus_window_is_remappable(struct mvebu_mbus_state *mbus, | 190 | static bool mvebu_mbus_window_is_remappable(struct mvebu_mbus_state *mbus, |
164 | const int win) | 191 | const int win) |
@@ -576,6 +603,95 @@ static unsigned int armada_xp_mbus_win_remap_offset(int win) | |||
576 | return MVEBU_MBUS_NO_REMAP; | 603 | return MVEBU_MBUS_NO_REMAP; |
577 | } | 604 | } |
578 | 605 | ||
606 | /* | ||
607 | * Use the memblock information to find the MBus bridge hole in the | ||
608 | * physical address space. | ||
609 | */ | ||
610 | static void __init | ||
611 | mvebu_mbus_find_bridge_hole(uint64_t *start, uint64_t *end) | ||
612 | { | ||
613 | struct memblock_region *r; | ||
614 | uint64_t s = 0; | ||
615 | |||
616 | for_each_memblock(memory, r) { | ||
617 | /* | ||
618 | * This part of the memory is above 4 GB, so we don't | ||
619 | * care for the MBus bridge hole. | ||
620 | */ | ||
621 | if (r->base >= 0x100000000ULL) | ||
622 | continue; | ||
623 | |||
624 | /* | ||
625 | * The MBus bridge hole is at the end of the RAM under | ||
626 | * the 4 GB limit. | ||
627 | */ | ||
628 | if (r->base + r->size > s) | ||
629 | s = r->base + r->size; | ||
630 | } | ||
631 | |||
632 | *start = s; | ||
633 | *end = 0x100000000ULL; | ||
634 | } | ||
635 | |||
636 | /* | ||
637 | * This function fills in the mvebu_mbus_dram_info_nooverlap data | ||
638 | * structure, by looking at the mvebu_mbus_dram_info data, and | ||
639 | * removing the parts of it that overlap with I/O windows. | ||
640 | */ | ||
641 | static void __init | ||
642 | mvebu_mbus_setup_cpu_target_nooverlap(struct mvebu_mbus_state *mbus) | ||
643 | { | ||
644 | uint64_t mbus_bridge_base, mbus_bridge_end; | ||
645 | int cs_nooverlap = 0; | ||
646 | int i; | ||
647 | |||
648 | mvebu_mbus_find_bridge_hole(&mbus_bridge_base, &mbus_bridge_end); | ||
649 | |||
650 | for (i = 0; i < mvebu_mbus_dram_info.num_cs; i++) { | ||
651 | struct mbus_dram_window *w; | ||
652 | u64 base, size, end; | ||
653 | |||
654 | w = &mvebu_mbus_dram_info.cs[i]; | ||
655 | base = w->base; | ||
656 | size = w->size; | ||
657 | end = base + size; | ||
658 | |||
659 | /* | ||
660 | * The CS is fully enclosed inside the MBus bridge | ||
661 | * area, so ignore it. | ||
662 | */ | ||
663 | if (base >= mbus_bridge_base && end <= mbus_bridge_end) | ||
664 | continue; | ||
665 | |||
666 | /* | ||
667 | * Beginning of CS overlaps with end of MBus, raise CS | ||
668 | * base address, and shrink its size. | ||
669 | */ | ||
670 | if (base >= mbus_bridge_base && end > mbus_bridge_end) { | ||
671 | size -= mbus_bridge_end - base; | ||
672 | base = mbus_bridge_end; | ||
673 | } | ||
674 | |||
675 | /* | ||
676 | * End of CS overlaps with beginning of MBus, shrink | ||
677 | * CS size. | ||
678 | */ | ||
679 | if (base < mbus_bridge_base && end > mbus_bridge_base) | ||
680 | size -= end - mbus_bridge_base; | ||
681 | |||
682 | w = &mvebu_mbus_dram_info_nooverlap.cs[cs_nooverlap++]; | ||
683 | w->cs_index = i; | ||
684 | w->mbus_attr = 0xf & ~(1 << i); | ||
685 | if (mbus->hw_io_coherency) | ||
686 | w->mbus_attr |= ATTR_HW_COHERENCY; | ||
687 | w->base = base; | ||
688 | w->size = size; | ||
689 | } | ||
690 | |||
691 | mvebu_mbus_dram_info_nooverlap.mbus_dram_target_id = TARGET_DDR; | ||
692 | mvebu_mbus_dram_info_nooverlap.num_cs = cs_nooverlap; | ||
693 | } | ||
694 | |||
579 | static void __init | 695 | static void __init |
580 | mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus) | 696 | mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus) |
581 | { | 697 | { |
@@ -964,6 +1080,7 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, | |||
964 | mvebu_mbus_disable_window(mbus, win); | 1080 | mvebu_mbus_disable_window(mbus, win); |
965 | 1081 | ||
966 | mbus->soc->setup_cpu_target(mbus); | 1082 | mbus->soc->setup_cpu_target(mbus); |
1083 | mvebu_mbus_setup_cpu_target_nooverlap(mbus); | ||
967 | 1084 | ||
968 | if (is_coherent) | 1085 | if (is_coherent) |
969 | writel(UNIT_SYNC_BARRIER_ALL, | 1086 | writel(UNIT_SYNC_BARRIER_ALL, |