Merge branch 'debugfs_automount' of git://git.kernel.org/pub/scm/linux/kernel/git...
[cascardo/linux.git] / drivers / bus / mvebu-mbus.c
index 81bf297..fb9ec62 100644 (file)
@@ -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.
@@ -69,6 +70,7 @@
  */
 #define WIN_CTRL_OFF           0x0000
 #define   WIN_CTRL_ENABLE       BIT(0)
+#define   WIN_CTRL_SYNCBARRIER  BIT(1)
 #define   WIN_CTRL_TGT_MASK     0xf0
 #define   WIN_CTRL_TGT_SHIFT    4
 #define   WIN_CTRL_ATTR_MASK    0xff00
@@ -82,6 +84,9 @@
 #define   WIN_REMAP_LOW         0xffff0000
 #define WIN_REMAP_HI_OFF       0x000c
 
+#define UNIT_SYNC_BARRIER_OFF   0x84
+#define   UNIT_SYNC_BARRIER_ALL 0xFFFF
+
 #define ATTR_HW_COHERENCY      (0x1 << 4)
 
 #define DDR_BASE_CS_OFF(n)     (0x0000 + ((n) << 3))
 
 /* 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
@@ -106,9 +113,9 @@ struct mvebu_mbus_state;
 
 struct mvebu_mbus_soc_data {
        unsigned int num_wins;
-       unsigned int num_remappable_wins;
        bool has_mbus_bridge;
        unsigned int (*win_cfg_offset)(const int win);
+       unsigned int (*win_remap_offset)(const int win);
        void (*setup_cpu_target)(struct mvebu_mbus_state *s);
        int (*save_cpu_target)(struct mvebu_mbus_state *s,
                               u32 *store_addr);
@@ -154,6 +161,13 @@ const struct mbus_dram_target_info *mv_mbus_dram_info(void)
 }
 EXPORT_SYMBOL_GPL(mv_mbus_dram_info);
 
+/* Checks whether the given window has remap capability */
+static bool mvebu_mbus_window_is_remappable(struct mvebu_mbus_state *mbus,
+                                           const int win)
+{
+       return mbus->soc->win_remap_offset(win) != MVEBU_MBUS_NO_REMAP;
+}
+
 /*
  * Functions to manipulate the address decoding windows
  */
@@ -185,9 +199,12 @@ static void mvebu_mbus_read_window(struct mvebu_mbus_state *mbus,
                *attr = (ctrlreg & WIN_CTRL_ATTR_MASK) >> WIN_CTRL_ATTR_SHIFT;
 
        if (remap) {
-               if (win < mbus->soc->num_remappable_wins) {
-                       u32 remap_low = readl(addr + WIN_REMAP_LO_OFF);
-                       u32 remap_hi  = readl(addr + WIN_REMAP_HI_OFF);
+               if (mvebu_mbus_window_is_remappable(mbus, win)) {
+                       u32 remap_low, remap_hi;
+                       void __iomem *addr_rmp = mbus->mbuswins_base +
+                               mbus->soc->win_remap_offset(win);
+                       remap_low = readl(addr_rmp + WIN_REMAP_LO_OFF);
+                       remap_hi  = readl(addr_rmp + WIN_REMAP_HI_OFF);
                        *remap = ((u64)remap_hi << 32) | remap_low;
                } else
                        *remap = 0;
@@ -200,10 +217,11 @@ static void mvebu_mbus_disable_window(struct mvebu_mbus_state *mbus,
        void __iomem *addr;
 
        addr = mbus->mbuswins_base + mbus->soc->win_cfg_offset(win);
-
        writel(0, addr + WIN_BASE_OFF);
        writel(0, addr + WIN_CTRL_OFF);
-       if (win < mbus->soc->num_remappable_wins) {
+
+       if (mvebu_mbus_window_is_remappable(mbus, win)) {
+               addr = mbus->mbuswins_base + mbus->soc->win_remap_offset(win);
                writel(0, addr + WIN_REMAP_LO_OFF);
                writel(0, addr + WIN_REMAP_HI_OFF);
        }
@@ -211,14 +229,6 @@ static void mvebu_mbus_disable_window(struct mvebu_mbus_state *mbus,
 
 /* Checks whether the given window number is available */
 
-/* On Armada XP, 375 and 38x the MBus window 13 has the remap
- * capability, like windows 0 to 7. However, the mvebu-mbus driver
- * isn't currently taking into account this special case, which means
- * that when window 13 is actually used, the remap registers are left
- * to 0, making the device using this MBus window unavailable. The
- * quick fix for stable is to not use window 13. A follow up patch
- * will correctly handle this window.
-*/
 static int mvebu_mbus_window_is_free(struct mvebu_mbus_state *mbus,
                                     const int win)
 {
@@ -226,9 +236,6 @@ static int mvebu_mbus_window_is_free(struct mvebu_mbus_state *mbus,
                mbus->soc->win_cfg_offset(win);
        u32 ctrl = readl(addr + WIN_CTRL_OFF);
 
-       if (win == 13)
-               return false;
-
        return !(ctrl & WIN_CTRL_ENABLE);
 }
 
@@ -316,17 +323,22 @@ 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;
 
        writel(base & WIN_BASE_LOW, addr + WIN_BASE_OFF);
        writel(ctrl, addr + WIN_CTRL_OFF);
-       if (win < mbus->soc->num_remappable_wins) {
+
+       if (mvebu_mbus_window_is_remappable(mbus, win)) {
+               void __iomem *addr_rmp = mbus->mbuswins_base +
+                       mbus->soc->win_remap_offset(win);
+
                if (remap == MVEBU_MBUS_NO_REMAP)
                        remap_addr = base;
                else
                        remap_addr = remap;
-               writel(remap_addr & WIN_REMAP_LOW, addr + WIN_REMAP_LO_OFF);
-               writel(0, addr + WIN_REMAP_HI_OFF);
+               writel(remap_addr & WIN_REMAP_LOW, addr_rmp + WIN_REMAP_LO_OFF);
+               writel(0, addr_rmp + WIN_REMAP_HI_OFF);
        }
 
        return 0;
@@ -340,19 +352,27 @@ static int mvebu_mbus_alloc_window(struct mvebu_mbus_state *mbus,
        int win;
 
        if (remap == MVEBU_MBUS_NO_REMAP) {
-               for (win = mbus->soc->num_remappable_wins;
-                    win < mbus->soc->num_wins; win++)
+               for (win = 0; win < mbus->soc->num_wins; win++) {
+                       if (mvebu_mbus_window_is_remappable(mbus, win))
+                               continue;
+
                        if (mvebu_mbus_window_is_free(mbus, win))
                                return mvebu_mbus_setup_window(mbus, win, base,
                                                               size, remap,
                                                               target, attr);
+               }
        }
 
+       for (win = 0; win < mbus->soc->num_wins; win++) {
+               /* Skip window if need remap but is not supported */
+               if ((remap != MVEBU_MBUS_NO_REMAP) &&
+                   !mvebu_mbus_window_is_remappable(mbus, win))
+                       continue;
 
-       for (win = 0; win < mbus->soc->num_wins; win++)
                if (mvebu_mbus_window_is_free(mbus, win))
                        return mvebu_mbus_setup_window(mbus, win, base, size,
                                                       remap, target, attr);
+       }
 
        return -ENOMEM;
 }
@@ -464,7 +484,7 @@ static int mvebu_devs_debug_show(struct seq_file *seq, void *v)
                    ((wbase & (u64)(wsize - 1)) != 0))
                        seq_puts(seq, " (Invalid base/size!!)");
 
-               if (win < mbus->soc->num_remappable_wins) {
+               if (mvebu_mbus_window_is_remappable(mbus, win)) {
                        seq_printf(seq, " (remap %016llx)\n",
                                   (unsigned long long)wremap);
                } else
@@ -490,12 +510,12 @@ static const struct file_operations mvebu_devs_debug_fops = {
  * SoC-specific functions and definitions
  */
 
-static unsigned int orion_mbus_win_offset(int win)
+static unsigned int generic_mbus_win_cfg_offset(int win)
 {
        return win << 4;
 }
 
-static unsigned int armada_370_xp_mbus_win_offset(int win)
+static unsigned int armada_370_xp_mbus_win_cfg_offset(int win)
 {
        /* The register layout is a bit annoying and the below code
         * tries to cope with it.
@@ -515,7 +535,7 @@ static unsigned int armada_370_xp_mbus_win_offset(int win)
                return 0x90 + ((win - 8) << 3);
 }
 
-static unsigned int mv78xx0_mbus_win_offset(int win)
+static unsigned int mv78xx0_mbus_win_cfg_offset(int win)
 {
        if (win < 8)
                return win << 4;
@@ -523,36 +543,140 @@ static unsigned int mv78xx0_mbus_win_offset(int win)
                return 0x900 + ((win - 8) << 4);
 }
 
+static unsigned int generic_mbus_win_remap_2_offset(int win)
+{
+       if (win < 2)
+               return generic_mbus_win_cfg_offset(win);
+       else
+               return MVEBU_MBUS_NO_REMAP;
+}
+
+static unsigned int generic_mbus_win_remap_4_offset(int win)
+{
+       if (win < 4)
+               return generic_mbus_win_cfg_offset(win);
+       else
+               return MVEBU_MBUS_NO_REMAP;
+}
+
+static unsigned int generic_mbus_win_remap_8_offset(int win)
+{
+       if (win < 8)
+               return generic_mbus_win_cfg_offset(win);
+       else
+               return MVEBU_MBUS_NO_REMAP;
+}
+
+static unsigned int armada_xp_mbus_win_remap_offset(int win)
+{
+       if (win < 8)
+               return generic_mbus_win_cfg_offset(win);
+       else if (win == 13)
+               return 0xF0 - WIN_REMAP_LO_OFF;
+       else
+               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;
 }
@@ -632,30 +756,40 @@ int mvebu_mbus_save_cpu_target(u32 *store_addr)
        return mbus_state.soc->save_cpu_target(&mbus_state, store_addr);
 }
 
-static const struct mvebu_mbus_soc_data armada_370_xp_mbus_data = {
+static const struct mvebu_mbus_soc_data armada_370_mbus_data = {
        .num_wins            = 20,
-       .num_remappable_wins = 8,
        .has_mbus_bridge     = true,
-       .win_cfg_offset      = armada_370_xp_mbus_win_offset,
+       .win_cfg_offset      = armada_370_xp_mbus_win_cfg_offset,
+       .win_remap_offset    = generic_mbus_win_remap_8_offset,
+       .setup_cpu_target    = mvebu_mbus_default_setup_cpu_target,
+       .show_cpu_target     = mvebu_sdram_debug_show_orion,
        .save_cpu_target     = mvebu_mbus_default_save_cpu_target,
+};
+
+static const struct mvebu_mbus_soc_data armada_xp_mbus_data = {
+       .num_wins            = 20,
+       .has_mbus_bridge     = true,
+       .win_cfg_offset      = armada_370_xp_mbus_win_cfg_offset,
+       .win_remap_offset    = armada_xp_mbus_win_remap_offset,
        .setup_cpu_target    = mvebu_mbus_default_setup_cpu_target,
        .show_cpu_target     = mvebu_sdram_debug_show_orion,
+       .save_cpu_target     = mvebu_mbus_default_save_cpu_target,
 };
 
 static const struct mvebu_mbus_soc_data kirkwood_mbus_data = {
        .num_wins            = 8,
-       .num_remappable_wins = 4,
-       .win_cfg_offset      = orion_mbus_win_offset,
+       .win_cfg_offset      = generic_mbus_win_cfg_offset,
        .save_cpu_target     = mvebu_mbus_default_save_cpu_target,
+       .win_remap_offset    = generic_mbus_win_remap_4_offset,
        .setup_cpu_target    = mvebu_mbus_default_setup_cpu_target,
        .show_cpu_target     = mvebu_sdram_debug_show_orion,
 };
 
 static const struct mvebu_mbus_soc_data dove_mbus_data = {
        .num_wins            = 8,
-       .num_remappable_wins = 4,
-       .win_cfg_offset      = orion_mbus_win_offset,
+       .win_cfg_offset      = generic_mbus_win_cfg_offset,
        .save_cpu_target     = mvebu_mbus_dove_save_cpu_target,
+       .win_remap_offset    = generic_mbus_win_remap_4_offset,
        .setup_cpu_target    = mvebu_mbus_dove_setup_cpu_target,
        .show_cpu_target     = mvebu_sdram_debug_show_dove,
 };
@@ -666,36 +800,40 @@ static const struct mvebu_mbus_soc_data dove_mbus_data = {
  */
 static const struct mvebu_mbus_soc_data orion5x_4win_mbus_data = {
        .num_wins            = 8,
-       .num_remappable_wins = 4,
-       .win_cfg_offset      = orion_mbus_win_offset,
+       .win_cfg_offset      = generic_mbus_win_cfg_offset,
        .save_cpu_target     = mvebu_mbus_default_save_cpu_target,
+       .win_remap_offset    = generic_mbus_win_remap_4_offset,
        .setup_cpu_target    = mvebu_mbus_default_setup_cpu_target,
        .show_cpu_target     = mvebu_sdram_debug_show_orion,
 };
 
 static const struct mvebu_mbus_soc_data orion5x_2win_mbus_data = {
        .num_wins            = 8,
-       .num_remappable_wins = 2,
-       .win_cfg_offset      = orion_mbus_win_offset,
+       .win_cfg_offset      = generic_mbus_win_cfg_offset,
        .save_cpu_target     = mvebu_mbus_default_save_cpu_target,
+       .win_remap_offset    = generic_mbus_win_remap_2_offset,
        .setup_cpu_target    = mvebu_mbus_default_setup_cpu_target,
        .show_cpu_target     = mvebu_sdram_debug_show_orion,
 };
 
 static const struct mvebu_mbus_soc_data mv78xx0_mbus_data = {
        .num_wins            = 14,
-       .num_remappable_wins = 8,
-       .win_cfg_offset      = mv78xx0_mbus_win_offset,
+       .win_cfg_offset      = mv78xx0_mbus_win_cfg_offset,
        .save_cpu_target     = mvebu_mbus_default_save_cpu_target,
+       .win_remap_offset    = generic_mbus_win_remap_8_offset,
        .setup_cpu_target    = mvebu_mbus_default_setup_cpu_target,
        .show_cpu_target     = mvebu_sdram_debug_show_orion,
 };
 
 static const struct of_device_id of_mvebu_mbus_ids[] = {
        { .compatible = "marvell,armada370-mbus",
-         .data = &armada_370_xp_mbus_data, },
+         .data = &armada_370_mbus_data, },
+       { .compatible = "marvell,armada375-mbus",
+         .data = &armada_xp_mbus_data, },
+       { .compatible = "marvell,armada380-mbus",
+         .data = &armada_xp_mbus_data, },
        { .compatible = "marvell,armadaxp-mbus",
-         .data = &armada_370_xp_mbus_data, },
+         .data = &armada_xp_mbus_data, },
        { .compatible = "marvell,kirkwood-mbus",
          .data = &kirkwood_mbus_data, },
        { .compatible = "marvell,dove-mbus",
@@ -802,15 +940,19 @@ static int mvebu_mbus_suspend(void)
        for (win = 0; win < s->soc->num_wins; win++) {
                void __iomem *addr = s->mbuswins_base +
                        s->soc->win_cfg_offset(win);
+               void __iomem *addr_rmp;
 
                s->wins[win].base = readl(addr + WIN_BASE_OFF);
                s->wins[win].ctrl = readl(addr + WIN_CTRL_OFF);
 
-               if (win >= s->soc->num_remappable_wins)
+               if (!mvebu_mbus_window_is_remappable(s, win))
                        continue;
 
-               s->wins[win].remap_lo = readl(addr + WIN_REMAP_LO_OFF);
-               s->wins[win].remap_hi = readl(addr + WIN_REMAP_HI_OFF);
+               addr_rmp = s->mbuswins_base +
+                       s->soc->win_remap_offset(win);
+
+               s->wins[win].remap_lo = readl(addr_rmp + WIN_REMAP_LO_OFF);
+               s->wins[win].remap_hi = readl(addr_rmp + WIN_REMAP_HI_OFF);
        }
 
        s->mbus_bridge_ctrl = readl(s->mbusbridge_base +
@@ -834,15 +976,19 @@ static void mvebu_mbus_resume(void)
        for (win = 0; win < s->soc->num_wins; win++) {
                void __iomem *addr = s->mbuswins_base +
                        s->soc->win_cfg_offset(win);
+               void __iomem *addr_rmp;
 
                writel(s->wins[win].base, addr + WIN_BASE_OFF);
                writel(s->wins[win].ctrl, addr + WIN_CTRL_OFF);
 
-               if (win >= s->soc->num_remappable_wins)
+               if (!mvebu_mbus_window_is_remappable(s, win))
                        continue;
 
-               writel(s->wins[win].remap_lo, addr + WIN_REMAP_LO_OFF);
-               writel(s->wins[win].remap_hi, addr + WIN_REMAP_HI_OFF);
+               addr_rmp = s->mbuswins_base +
+                       s->soc->win_remap_offset(win);
+
+               writel(s->wins[win].remap_lo, addr_rmp + WIN_REMAP_LO_OFF);
+               writel(s->wins[win].remap_hi, addr_rmp + WIN_REMAP_HI_OFF);
        }
 }
 
@@ -857,7 +1003,8 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
                                         phys_addr_t sdramwins_phys_base,
                                         size_t sdramwins_size,
                                         phys_addr_t mbusbridge_phys_base,
-                                        size_t mbusbridge_size)
+                                        size_t mbusbridge_size,
+                                        bool is_coherent)
 {
        int win;
 
@@ -889,6 +1036,10 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
 
        mbus->soc->setup_cpu_target(mbus);
 
+       if (is_coherent)
+               writel(UNIT_SYNC_BARRIER_ALL,
+                      mbus->mbuswins_base + UNIT_SYNC_BARRIER_OFF);
+
        register_syscore_ops(&mvebu_mbus_syscore_ops);
 
        return 0;
@@ -916,7 +1067,7 @@ int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base,
                        mbuswins_phys_base,
                        mbuswins_size,
                        sdramwins_phys_base,
-                       sdramwins_size, 0, 0);
+                       sdramwins_size, 0, 0, false);
 }
 
 #ifdef CONFIG_OF
@@ -1118,7 +1269,8 @@ int __init mvebu_mbus_dt_init(bool is_coherent)
                                     sdramwins_res.start,
                                     resource_size(&sdramwins_res),
                                     mbusbridge_res.start,
-                                    resource_size(&mbusbridge_res));
+                                    resource_size(&mbusbridge_res),
+                                    is_coherent);
        if (ret)
                return ret;