mvebu SoC suspend changes for v3.19

- Armada 370/XP suspend/resume support
 
  - mvebu SoC driver suspend/resume support
     - irqchip
     - clocksource
     - mbus
     - clk
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABAgAGBQJUe0lIAAoJEP45WPkGe8Zn/IgP/jOO8c7t7dohRbAe3axzIcaC
 DLL7d7j0AScZGXLx1/xJrFFY/P3gn3dlLR7HnT0t4K7vcW0kP4orMGo6FcGicSOZ
 VzQf88cOkunKf9NTM1Y0LOXVWTHGuACiXAnxook5A6k+l0xQ1t+uewgEKrg/33VK
 6WQ6woe2eYFwghkFwL3ybjttOPM5nxPef6v3TZ3LfwSUBsnSm70F1XiO8xZJH+LM
 fL83P409LGWgohwSaXYRdPJcNM0U7QMNo6i/If9NNBhIkdKb6llhQ/DvI+aXUvqB
 aD9/4t+Q75yki0mXIin6irltjspWsR8OFbaKZOM5IBFp/XrsKvNU+wy++7z9se9z
 qfG1QYmKk3ddI0isoksuIJpfbrlbQqFKCGlNkn8HVi4xCYCijNgb5bUrHQ27Aa4U
 GGisAOhqs9Ktpz96WeNKjvNQBSJZ3ESd6tlLrwCei8DwEdT0Z73jr1aEnulurpPG
 A3kiUhVpRIU+w0cth5Kix2bZj7JGsykzu1x5xORLE+MN8RSgmoveGyY5CBp2MHrl
 NxR/u98SD9I/rWT3DwQIKxM5ZqF4AAnyj4SaSWR/f40kWMU+A+eMEfo8VUeO88fl
 ygeeHTghSf58gbdgganRfDyY8OaQHeYNNKbWK6c7vxyMX98vwHOtOb5JS3nn0p7q
 Fugy/6qf+ZqYHT4nczJO
 =uX+e
 -----END PGP SIGNATURE-----

Merge tag 'mvebu-soc-suspend-3.19' of git://git.infradead.org/linux-mvebu into next/soc

Pull "mvebu SoC suspend changes for v3.19" from Jason Cooper:

 - Armada 370/XP suspend/resume support

 - mvebu SoC driver suspend/resume support
    - irqchip
    - clocksource
    - mbus
    - clk

* tag 'mvebu-soc-suspend-3.19' of git://git.infradead.org/linux-mvebu:
  ARM: mvebu: add SDRAM controller description for Armada XP
  ARM: mvebu: adjust mbus controller description on Armada 370/XP
  ARM: mvebu: add suspend/resume DT information for Armada XP GP
  ARM: mvebu: synchronize secondary CPU clocks on resume
  ARM: mvebu: make sure MMU is disabled in armada_370_xp_cpu_resume
  ARM: mvebu: Armada XP GP specific suspend/resume code
  ARM: mvebu: reserve the first 10 KB of each memory bank for suspend/resume
  ARM: mvebu: implement suspend/resume support for Armada XP
  clk: mvebu: add suspend/resume for gatable clocks
  bus: mvebu-mbus: provide a mechanism to save SDRAM window configuration
  bus: mvebu-mbus: suspend/resume support
  clocksource: time-armada-370-xp: add suspend/resume support
  irqchip: armada-370-xp: Add suspend/resume support
  Documentation: dt-bindings: minimal documentation for MVEBU SDRAM controller

Signed-off-by: Arnd Bergmann <[email protected]>
This commit is contained in:
Arnd Bergmann 2014-12-04 16:46:43 +01:00
commit ab64920c37
18 changed files with 777 additions and 32 deletions

View file

@ -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;