|
@@ -57,6 +57,7 @@
|
|
|
#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.
|
|
@@ -94,20 +95,42 @@
|
|
|
|
|
|
#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 (*save_cpu_target)(struct mvebu_mbus_state *s,
|
|
|
+ u32 *store_addr);
|
|
|
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;
|
|
|
+ phys_addr_t sdramwins_phys_base;
|
|
|
struct dentry *debugfs_root;
|
|
|
struct dentry *debugfs_sdram;
|
|
|
struct dentry *debugfs_devs;
|
|
@@ -115,6 +138,11 @@ struct mvebu_mbus_state {
|
|
|
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;
|
|
@@ -516,6 +544,28 @@ mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
|
|
|
mvebu_mbus_dram_info.num_cs = cs;
|
|
|
}
|
|
|
|
|
|
+static int
|
|
|
+mvebu_mbus_default_save_cpu_target(struct mvebu_mbus_state *mbus,
|
|
|
+ u32 *store_addr)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 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));
|
|
|
+
|
|
|
+ writel(mbus->sdramwins_phys_base + DDR_BASE_CS_OFF(i),
|
|
|
+ store_addr++);
|
|
|
+ writel(base, store_addr++);
|
|
|
+ writel(mbus->sdramwins_phys_base + DDR_SIZE_CS_OFF(i),
|
|
|
+ store_addr++);
|
|
|
+ writel(size, store_addr++);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* We've written 16 words to the store address */
|
|
|
+ return 16;
|
|
|
+}
|
|
|
+
|
|
|
static void __init
|
|
|
mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus)
|
|
|
{
|
|
@@ -546,10 +596,35 @@ mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus)
|
|
|
mvebu_mbus_dram_info.num_cs = cs;
|
|
|
}
|
|
|
|
|
|
+static int
|
|
|
+mvebu_mbus_dove_save_cpu_target(struct mvebu_mbus_state *mbus,
|
|
|
+ u32 *store_addr)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < 2; i++) {
|
|
|
+ u32 map = readl(mbus->sdramwins_base + DOVE_DDR_BASE_CS_OFF(i));
|
|
|
+
|
|
|
+ writel(mbus->sdramwins_phys_base + DOVE_DDR_BASE_CS_OFF(i),
|
|
|
+ store_addr++);
|
|
|
+ writel(map, store_addr++);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* We've written 4 words to the store address */
|
|
|
+ return 4;
|
|
|
+}
|
|
|
+
|
|
|
+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 = {
|
|
|
.num_wins = 20,
|
|
|
.num_remappable_wins = 8,
|
|
|
+ .has_mbus_bridge = true,
|
|
|
.win_cfg_offset = armada_370_xp_mbus_win_offset,
|
|
|
+ .save_cpu_target = mvebu_mbus_default_save_cpu_target,
|
|
|
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
|
|
|
.show_cpu_target = mvebu_sdram_debug_show_orion,
|
|
|
};
|
|
@@ -558,6 +633,7 @@ static const struct mvebu_mbus_soc_data kirkwood_mbus_data = {
|
|
|
.num_wins = 8,
|
|
|
.num_remappable_wins = 4,
|
|
|
.win_cfg_offset = orion_mbus_win_offset,
|
|
|
+ .save_cpu_target = mvebu_mbus_default_save_cpu_target,
|
|
|
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
|
|
|
.show_cpu_target = mvebu_sdram_debug_show_orion,
|
|
|
};
|
|
@@ -566,6 +642,7 @@ static const struct mvebu_mbus_soc_data dove_mbus_data = {
|
|
|
.num_wins = 8,
|
|
|
.num_remappable_wins = 4,
|
|
|
.win_cfg_offset = orion_mbus_win_offset,
|
|
|
+ .save_cpu_target = mvebu_mbus_dove_save_cpu_target,
|
|
|
.setup_cpu_target = mvebu_mbus_dove_setup_cpu_target,
|
|
|
.show_cpu_target = mvebu_sdram_debug_show_dove,
|
|
|
};
|
|
@@ -578,6 +655,7 @@ 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,
|
|
|
+ .save_cpu_target = mvebu_mbus_default_save_cpu_target,
|
|
|
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
|
|
|
.show_cpu_target = mvebu_sdram_debug_show_orion,
|
|
|
};
|
|
@@ -586,6 +664,7 @@ 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,
|
|
|
+ .save_cpu_target = mvebu_mbus_default_save_cpu_target,
|
|
|
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
|
|
|
.show_cpu_target = mvebu_sdram_debug_show_orion,
|
|
|
};
|
|
@@ -594,6 +673,7 @@ static const struct mvebu_mbus_soc_data mv78xx0_mbus_data = {
|
|
|
.num_wins = 14,
|
|
|
.num_remappable_wins = 8,
|
|
|
.win_cfg_offset = mv78xx0_mbus_win_offset,
|
|
|
+ .save_cpu_target = mvebu_mbus_default_save_cpu_target,
|
|
|
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
|
|
|
.show_cpu_target = mvebu_sdram_debug_show_orion,
|
|
|
};
|
|
@@ -698,11 +778,73 @@ static __init int mvebu_mbus_debugfs_init(void)
|
|
|
}
|
|
|
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;
|
|
|
|
|
@@ -716,11 +858,26 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
|
|
|
+ mbus->sdramwins_phys_base = sdramwins_phys_base;
|
|
|
+
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
@@ -746,7 +903,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);
|
|
|
+ sdramwins_size, 0, 0);
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_OF
|
|
@@ -887,7 +1044,7 @@ static void __init mvebu_mbus_get_pcie_resources(struct device_node *np,
|
|
|
|
|
|
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;
|
|
@@ -923,6 +1080,19 @@ int __init mvebu_mbus_dt_init(bool is_coherent)
|
|
|
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 */
|
|
@@ -933,7 +1103,9 @@ int __init mvebu_mbus_dt_init(bool is_coherent)
|
|
|
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;
|
|
|
|