- compatible:  Should be set to "marvell,mbus-controller".
 
 - reg:          Device's register space.
-               Two entries are expected (see the examples below):
-               the first one controls the devices decoding window and
-               the second one controls the SDRAM decoding window.
+               Two or three entries are expected (see the examples below):
+               the first one controls the devices decoding window,
+               the second one controls the SDRAM decoding window and
+               the third controls the MBus bridge (only with the
+               marvell,armada370-mbus and marvell,armadaxp-mbus
+               compatible strings)
 
 Example:
 
 
                        mbusc: mbus-controller@20000 {
                                compatible = "marvell,mbus-controller";
-                               reg = <0x20000 0x100>, <0x20180 0x20>;
+                               reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
                        };
 
                        /* more children ...*/
 
                        mbusc: mbus-controller@20000 {
                                compatible = "marvell,mbus-controller";
-                               reg = <0x20000 0x100>, <0x20180 0x20>;
+                               reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
                        };
 
                        /* more children ...*/
 
                        mbusc: mbus-controller@20000 {
                                compatible = "marvell,mbus-controller";
-                               reg = <0x20000 0x100>, <0x20180 0x20>;
+                               reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
                        };
 
                        /* other children */
                        ranges = <0 MBUS_ID(0xf0, 0x01) 0 0x100000>;
 
                        mbusc: mbus-controller@20000 {
-                               reg = <0x20000 0x100>, <0x20180 0x20>;
+                               reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
                        };
 
                        interrupt-controller@20000 {
 
 #include <linux/of_address.h>
 #include <linux/debugfs.h>
 #include <linux/log2.h>
+#include <linux/syscore_ops.h>
 
 /*
  * DDR target is the same on all platforms.
 
 #define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4)
 
+/* Relative to mbusbridge_base */
+#define MBUS_BRIDGE_CTRL_OFF   0x0
+#define MBUS_BRIDGE_BASE_OFF   0x4
+
+/* Maximum number of windows, for all known platforms */
+#define MBUS_WINS_MAX           20
+
 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);
        void (*setup_cpu_target)(struct mvebu_mbus_state *s);
        int (*show_cpu_target)(struct mvebu_mbus_state *s,
                               struct seq_file *seq, void *v);
 };
 
+/*
+ * Used to store the state of one MBus window accross suspend/resume.
+ */
+struct mvebu_mbus_win_data {
+       u32 ctrl;
+       u32 base;
+       u32 remap_lo;
+       u32 remap_hi;
+};
+
 struct mvebu_mbus_state {
        void __iomem *mbuswins_base;
        void __iomem *sdramwins_base;
+       void __iomem *mbusbridge_base;
        struct dentry *debugfs_root;
        struct dentry *debugfs_sdram;
        struct dentry *debugfs_devs;
        struct resource pcie_io_aperture;
        const struct mvebu_mbus_soc_data *soc;
        int hw_io_coherency;
+
+       /* Used during suspend/resume */
+       u32 mbus_bridge_ctrl;
+       u32 mbus_bridge_base;
+       struct mvebu_mbus_win_data wins[MBUS_WINS_MAX];
 };
 
 static struct mvebu_mbus_state mbus_state;
 static const struct mvebu_mbus_soc_data armada_370_xp_mbus_data = {
        .num_wins            = 20,
        .num_remappable_wins = 8,
+       .has_mbus_bridge     = true,
        .win_cfg_offset      = armada_370_xp_mbus_win_offset,
        .setup_cpu_target    = mvebu_mbus_default_setup_cpu_target,
        .show_cpu_target     = mvebu_sdram_debug_show_orion,
 }
 fs_initcall(mvebu_mbus_debugfs_init);
 
+static int mvebu_mbus_suspend(void)
+{
+       struct mvebu_mbus_state *s = &mbus_state;
+       int win;
+
+       if (!s->mbusbridge_base)
+               return -ENODEV;
+
+       for (win = 0; win < s->soc->num_wins; win++) {
+               void __iomem *addr = s->mbuswins_base +
+                       s->soc->win_cfg_offset(win);
+
+               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)
+                       continue;
+
+               s->wins[win].remap_lo = readl(addr + WIN_REMAP_LO_OFF);
+               s->wins[win].remap_hi = readl(addr + WIN_REMAP_HI_OFF);
+       }
+
+       s->mbus_bridge_ctrl = readl(s->mbusbridge_base +
+                                   MBUS_BRIDGE_CTRL_OFF);
+       s->mbus_bridge_base = readl(s->mbusbridge_base +
+                                   MBUS_BRIDGE_BASE_OFF);
+
+       return 0;
+}
+
+static void mvebu_mbus_resume(void)
+{
+       struct mvebu_mbus_state *s = &mbus_state;
+       int win;
+
+       writel(s->mbus_bridge_ctrl,
+              s->mbusbridge_base + MBUS_BRIDGE_CTRL_OFF);
+       writel(s->mbus_bridge_base,
+              s->mbusbridge_base + MBUS_BRIDGE_BASE_OFF);
+
+       for (win = 0; win < s->soc->num_wins; win++) {
+               void __iomem *addr = s->mbuswins_base +
+                       s->soc->win_cfg_offset(win);
+
+               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)
+                       continue;
+
+               writel(s->wins[win].remap_lo, addr + WIN_REMAP_LO_OFF);
+               writel(s->wins[win].remap_hi, addr + WIN_REMAP_HI_OFF);
+       }
+}
+
+struct syscore_ops mvebu_mbus_syscore_ops = {
+       .suspend        = mvebu_mbus_suspend,
+       .resume         = mvebu_mbus_resume,
+};
+
 static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
                                         phys_addr_t mbuswins_phys_base,
                                         size_t mbuswins_size,
                                         phys_addr_t sdramwins_phys_base,
-                                        size_t sdramwins_size)
+                                        size_t sdramwins_size,
+                                        phys_addr_t mbusbridge_phys_base,
+                                        size_t mbusbridge_size)
 {
        int win;
 
                return -ENOMEM;
        }
 
+       if (mbusbridge_phys_base) {
+               mbus->mbusbridge_base = ioremap(mbusbridge_phys_base,
+                                               mbusbridge_size);
+               if (!mbus->mbusbridge_base) {
+                       iounmap(mbus->sdramwins_base);
+                       iounmap(mbus->mbuswins_base);
+                       return -ENOMEM;
+               }
+       } else
+               mbus->mbusbridge_base = NULL;
+
        for (win = 0; win < mbus->soc->num_wins; win++)
                mvebu_mbus_disable_window(mbus, win);
 
        mbus->soc->setup_cpu_target(mbus);
 
+       register_syscore_ops(&mvebu_mbus_syscore_ops);
+
        return 0;
 }
 
                        mbuswins_phys_base,
                        mbuswins_size,
                        sdramwins_phys_base,
-                       sdramwins_size);
+                       sdramwins_size, 0, 0);
 }
 
 #ifdef CONFIG_OF
 
 int __init mvebu_mbus_dt_init(bool is_coherent)
 {
-       struct resource mbuswins_res, sdramwins_res;
+       struct resource mbuswins_res, sdramwins_res, mbusbridge_res;
        struct device_node *np, *controller;
        const struct of_device_id *of_id;
        const __be32 *prop;
                return -EINVAL;
        }
 
+       /*
+        * Set the resource to 0 so that it can be left unmapped by
+        * mvebu_mbus_common_init() if the DT doesn't carry the
+        * necessary information. This is needed to preserve backward
+        * compatibility.
+        */
+       memset(&mbusbridge_res, 0, sizeof(mbusbridge_res));
+
+       if (mbus_state.soc->has_mbus_bridge) {
+               if (of_address_to_resource(controller, 2, &mbusbridge_res))
+                       pr_warn(FW_WARN "deprecated mbus-mvebu Device Tree, suspend/resume will not work\n");
+       }
+
        mbus_state.hw_io_coherency = is_coherent;
 
        /* Get optional pcie-{mem,io}-aperture properties */
                                     mbuswins_res.start,
                                     resource_size(&mbuswins_res),
                                     sdramwins_res.start,
-                                    resource_size(&sdramwins_res));
+                                    resource_size(&sdramwins_res),
+                                    mbusbridge_res.start,
+                                    resource_size(&mbusbridge_res));
        if (ret)
                return ret;