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 <arnd@arndb.de>
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

@ -48,9 +48,12 @@ Required properties:
- compatible: Should be set to "marvell,mbus-controller". - compatible: Should be set to "marvell,mbus-controller".
- reg: Device's register space. - reg: Device's register space.
Two entries are expected (see the examples below): Two or three entries are expected (see the examples below):
the first one controls the devices decoding window and the first one controls the devices decoding window,
the second one controls the SDRAM 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: Example:
@ -67,7 +70,7 @@ Example:
mbusc: mbus-controller@20000 { mbusc: mbus-controller@20000 {
compatible = "marvell,mbus-controller"; compatible = "marvell,mbus-controller";
reg = <0x20000 0x100>, <0x20180 0x20>; reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
}; };
/* more children ...*/ /* more children ...*/
@ -126,7 +129,7 @@ are skipped.
mbusc: mbus-controller@20000 { mbusc: mbus-controller@20000 {
compatible = "marvell,mbus-controller"; compatible = "marvell,mbus-controller";
reg = <0x20000 0x100>, <0x20180 0x20>; reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
}; };
/* more children ...*/ /* more children ...*/
@ -170,7 +173,7 @@ Using this macro, the above example would be:
mbusc: mbus-controller@20000 { mbusc: mbus-controller@20000 {
compatible = "marvell,mbus-controller"; compatible = "marvell,mbus-controller";
reg = <0x20000 0x100>, <0x20180 0x20>; reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
}; };
/* other children */ /* other children */
@ -266,7 +269,7 @@ See the example below, where a more complete device tree is shown:
ranges = <0 MBUS_ID(0xf0, 0x01) 0 0x100000>; ranges = <0 MBUS_ID(0xf0, 0x01) 0 0x100000>;
mbusc: mbus-controller@20000 { mbusc: mbus-controller@20000 {
reg = <0x20000 0x100>, <0x20180 0x20>; reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
}; };
interrupt-controller@20000 { interrupt-controller@20000 {

View File

@ -0,0 +1,21 @@
Device Tree bindings for MVEBU SDRAM controllers
The Marvell EBU SoCs all have a SDRAM controller. The SDRAM controller
differs from one SoC variant to another, but they also share a number
of commonalities.
For now, this Device Tree binding documentation only documents the
Armada XP SDRAM controller.
Required properties:
- compatible: for Armada XP, "marvell,armada-xp-sdram-controller"
- reg: a resource specifier for the register space, which should
include all SDRAM controller registers as per the datasheet.
Example:
sdramc@1400 {
compatible = "marvell,armada-xp-sdram-controller";
reg = <0x1400 0x500>;
};

View File

@ -180,7 +180,8 @@
mbusc: mbus-controller@20000 { mbusc: mbus-controller@20000 {
compatible = "marvell,mbus-controller"; compatible = "marvell,mbus-controller";
reg = <0x20000 0x100>, <0x20180 0x20>; reg = <0x20000 0x100>, <0x20180 0x20>,
<0x20250 0x8>;
}; };
mpic: interrupt-controller@20000 { mpic: interrupt-controller@20000 {

View File

@ -23,6 +23,7 @@
*/ */
/dts-v1/; /dts-v1/;
#include <dt-bindings/gpio/gpio.h>
#include "armada-xp-mv78460.dtsi" #include "armada-xp-mv78460.dtsi"
/ { / {
@ -48,6 +49,14 @@
<0x00000001 0x00000000 0x00000001 0x00000000>; <0x00000001 0x00000000 0x00000001 0x00000000>;
}; };
cpus {
pm_pic {
ctrl-gpios = <&gpio0 16 GPIO_ACTIVE_LOW>,
<&gpio0 17 GPIO_ACTIVE_LOW>,
<&gpio0 18 GPIO_ACTIVE_LOW>;
};
};
soc { soc {
ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xf1000000 0x100000 ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xf1000000 0x100000
MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000 MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000
@ -115,7 +124,15 @@
serial@12300 { serial@12300 {
status = "okay"; status = "okay";
}; };
pinctrl {
pinctrl-0 = <&pic_pins>;
pinctrl-names = "default";
pic_pins: pic-pins-0 {
marvell,pins = "mpp16", "mpp17",
"mpp18";
marvell,function = "gpio";
};
};
sata@a0000 { sata@a0000 {
nr-ports = <2>; nr-ports = <2>;
status = "okay"; status = "okay";

View File

@ -35,6 +35,11 @@
}; };
internal-regs { internal-regs {
sdramc@1400 {
compatible = "marvell,armada-xp-sdram-controller";
reg = <0x1400 0x500>;
};
L2: l2-cache { L2: l2-cache {
compatible = "marvell,aurora-system-cache"; compatible = "marvell,aurora-system-cache";
reg = <0x08000 0x1000>; reg = <0x08000 0x1000>;

View File

@ -7,7 +7,7 @@ CFLAGS_pmsu.o := -march=armv7-a
obj-$(CONFIG_MACH_MVEBU_ANY) += system-controller.o mvebu-soc-id.o obj-$(CONFIG_MACH_MVEBU_ANY) += system-controller.o mvebu-soc-id.o
ifeq ($(CONFIG_MACH_MVEBU_V7),y) ifeq ($(CONFIG_MACH_MVEBU_V7),y)
obj-y += cpu-reset.o board-v7.o coherency.o coherency_ll.o pmsu.o pmsu_ll.o obj-y += cpu-reset.o board-v7.o coherency.o coherency_ll.o pmsu.o pmsu_ll.o pm.o pm-board.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o platsmp-a9.o headsmp-a9.o obj-$(CONFIG_SMP) += platsmp.o headsmp.o platsmp-a9.o headsmp-a9.o
endif endif

View File

@ -16,10 +16,12 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_fdt.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/clocksource.h> #include <linux/clocksource.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/memblock.h>
#include <linux/mbus.h> #include <linux/mbus.h>
#include <linux/signal.h> #include <linux/signal.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -56,6 +58,54 @@ void __iomem *mvebu_get_scu_base(void)
return scu_base; return scu_base;
} }
/*
* When returning from suspend, the platform goes through the
* bootloader, which executes its DDR3 training code. This code has
* the unfortunate idea of using the first 10 KB of each DRAM bank to
* exercise the RAM and calculate the optimal timings. Therefore, this
* area of RAM is overwritten, and shouldn't be used by the kernel if
* suspend/resume is supported.
*/
#ifdef CONFIG_SUSPEND
#define MVEBU_DDR_TRAINING_AREA_SZ (10 * SZ_1K)
static int __init mvebu_scan_mem(unsigned long node, const char *uname,
int depth, void *data)
{
const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
const __be32 *reg, *endp;
int l;
if (type == NULL || strcmp(type, "memory"))
return 0;
reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
if (reg == NULL)
reg = of_get_flat_dt_prop(node, "reg", &l);
if (reg == NULL)
return 0;
endp = reg + (l / sizeof(__be32));
while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
u64 base, size;
base = dt_mem_next_cell(dt_root_addr_cells, &reg);
size = dt_mem_next_cell(dt_root_size_cells, &reg);
memblock_reserve(base, MVEBU_DDR_TRAINING_AREA_SZ);
}
return 0;
}
static void __init mvebu_memblock_reserve(void)
{
of_scan_flat_dt(mvebu_scan_mem, NULL);
}
#else
static void __init mvebu_memblock_reserve(void) {}
#endif
/* /*
* Early versions of Armada 375 SoC have a bug where the BootROM * Early versions of Armada 375 SoC have a bug where the BootROM
* leaves an external data abort pending. The kernel is hit by this * leaves an external data abort pending. The kernel is hit by this
@ -151,6 +201,7 @@ DT_MACHINE_START(ARMADA_370_XP_DT, "Marvell Armada 370/XP (Device Tree)")
.init_machine = mvebu_dt_init, .init_machine = mvebu_dt_init,
.init_irq = mvebu_init_irq, .init_irq = mvebu_init_irq,
.restart = mvebu_restart, .restart = mvebu_restart,
.reserve = mvebu_memblock_reserve,
.dt_compat = armada_370_xp_dt_compat, .dt_compat = armada_370_xp_dt_compat,
MACHINE_END MACHINE_END

View File

@ -25,4 +25,6 @@ int mvebu_system_controller_get_soc_id(u32 *dev, u32 *rev);
void __iomem *mvebu_get_scu_base(void); void __iomem *mvebu_get_scu_base(void);
int mvebu_pm_init(void (*board_pm_enter)(void __iomem *sdram_reg, u32 srcmd));
#endif #endif

View File

@ -35,7 +35,7 @@
#define AXP_BOOTROM_BASE 0xfff00000 #define AXP_BOOTROM_BASE 0xfff00000
#define AXP_BOOTROM_SIZE 0x100000 #define AXP_BOOTROM_SIZE 0x100000
static struct clk *__init get_cpu_clk(int cpu) static struct clk *get_cpu_clk(int cpu)
{ {
struct clk *cpu_clk; struct clk *cpu_clk;
struct device_node *np = of_get_cpu_node(cpu, NULL); struct device_node *np = of_get_cpu_node(cpu, NULL);
@ -48,29 +48,28 @@ static struct clk *__init get_cpu_clk(int cpu)
return cpu_clk; return cpu_clk;
} }
static void __init set_secondary_cpus_clock(void) static void set_secondary_cpu_clock(unsigned int cpu)
{ {
int thiscpu, cpu; int thiscpu;
unsigned long rate; unsigned long rate;
struct clk *cpu_clk; struct clk *cpu_clk;
thiscpu = smp_processor_id(); thiscpu = get_cpu();
cpu_clk = get_cpu_clk(thiscpu); cpu_clk = get_cpu_clk(thiscpu);
if (!cpu_clk) if (!cpu_clk)
return; goto out;
clk_prepare_enable(cpu_clk); clk_prepare_enable(cpu_clk);
rate = clk_get_rate(cpu_clk); rate = clk_get_rate(cpu_clk);
/* set all the other CPU clk to the same rate than the boot CPU */ cpu_clk = get_cpu_clk(cpu);
for_each_possible_cpu(cpu) { if (!cpu_clk)
if (cpu == thiscpu) goto out;
continue; clk_set_rate(cpu_clk, rate);
cpu_clk = get_cpu_clk(cpu); clk_prepare_enable(cpu_clk);
if (!cpu_clk)
return; out:
clk_set_rate(cpu_clk, rate); put_cpu();
clk_prepare_enable(cpu_clk);
}
} }
static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle) static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle)
@ -80,6 +79,7 @@ static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle)
pr_info("Booting CPU %d\n", cpu); pr_info("Booting CPU %d\n", cpu);
hw_cpu = cpu_logical_map(cpu); hw_cpu = cpu_logical_map(cpu);
set_secondary_cpu_clock(hw_cpu);
mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_xp_secondary_startup); mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_xp_secondary_startup);
/* /*
@ -128,7 +128,6 @@ static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus)
struct resource res; struct resource res;
int err; int err;
set_secondary_cpus_clock();
flush_cache_all(); flush_cache_all();
set_cpu_coherent(); set_cpu_coherent();

View File

@ -0,0 +1,141 @@
/*
* Board-level suspend/resume support.
*
* Copyright (C) 2014 Marvell
*
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include "common.h"
#define ARMADA_XP_GP_PIC_NR_GPIOS 3
static void __iomem *gpio_ctrl;
static int pic_gpios[ARMADA_XP_GP_PIC_NR_GPIOS];
static int pic_raw_gpios[ARMADA_XP_GP_PIC_NR_GPIOS];
static void mvebu_armada_xp_gp_pm_enter(void __iomem *sdram_reg, u32 srcmd)
{
u32 reg, ackcmd;
int i;
/* Put 001 as value on the GPIOs */
reg = readl(gpio_ctrl);
for (i = 0; i < ARMADA_XP_GP_PIC_NR_GPIOS; i++)
reg &= ~BIT(pic_raw_gpios[i]);
reg |= BIT(pic_raw_gpios[0]);
writel(reg, gpio_ctrl);
/* Prepare writing 111 to the GPIOs */
ackcmd = readl(gpio_ctrl);
for (i = 0; i < ARMADA_XP_GP_PIC_NR_GPIOS; i++)
ackcmd |= BIT(pic_raw_gpios[i]);
/*
* Wait a while, the PIC needs quite a bit of time between the
* two GPIO commands.
*/
mdelay(3000);
asm volatile (
/* Align to a cache line */
".balign 32\n\t"
/* Enter self refresh */
"str %[srcmd], [%[sdram_reg]]\n\t"
/*
* Wait 100 cycles for DDR to enter self refresh, by
* doing 50 times two instructions.
*/
"mov r1, #50\n\t"
"1: subs r1, r1, #1\n\t"
"bne 1b\n\t"
/* Issue the command ACK */
"str %[ackcmd], [%[gpio_ctrl]]\n\t"
/* Trap the processor */
"b .\n\t"
: : [srcmd] "r" (srcmd), [sdram_reg] "r" (sdram_reg),
[ackcmd] "r" (ackcmd), [gpio_ctrl] "r" (gpio_ctrl) : "r1");
}
static int mvebu_armada_xp_gp_pm_init(void)
{
struct device_node *np;
struct device_node *gpio_ctrl_np;
int ret = 0, i;
if (!of_machine_is_compatible("marvell,axp-gp"))
return -ENODEV;
np = of_find_node_by_name(NULL, "pm_pic");
if (!np)
return -ENODEV;
for (i = 0; i < ARMADA_XP_GP_PIC_NR_GPIOS; i++) {
char *name;
struct of_phandle_args args;
pic_gpios[i] = of_get_named_gpio(np, "ctrl-gpios", i);
if (pic_gpios[i] < 0) {
ret = -ENODEV;
goto out;
}
name = kasprintf(GFP_KERNEL, "pic-pin%d", i);
if (!name) {
ret = -ENOMEM;
goto out;
}
ret = gpio_request(pic_gpios[i], name);
if (ret < 0) {
kfree(name);
goto out;
}
ret = gpio_direction_output(pic_gpios[i], 0);
if (ret < 0) {
gpio_free(pic_gpios[i]);
kfree(name);
goto out;
}
ret = of_parse_phandle_with_fixed_args(np, "ctrl-gpios", 2,
i, &args);
if (ret < 0) {
gpio_free(pic_gpios[i]);
kfree(name);
goto out;
}
gpio_ctrl_np = args.np;
pic_raw_gpios[i] = args.args[0];
}
gpio_ctrl = of_iomap(gpio_ctrl_np, 0);
if (!gpio_ctrl)
return -ENOMEM;
mvebu_pm_init(mvebu_armada_xp_gp_pm_enter);
out:
of_node_put(np);
return ret;
}
late_initcall(mvebu_armada_xp_gp_pm_init);

218
arch/arm/mach-mvebu/pm.c Normal file
View File

@ -0,0 +1,218 @@
/*
* Suspend/resume support. Currently supporting Armada XP only.
*
* Copyright (C) 2014 Marvell
*
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/cpu_pm.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mbus.h>
#include <linux/of_address.h>
#include <linux/suspend.h>
#include <asm/cacheflush.h>
#include <asm/outercache.h>
#include <asm/suspend.h>
#include "coherency.h"
#include "pmsu.h"
#define SDRAM_CONFIG_OFFS 0x0
#define SDRAM_CONFIG_SR_MODE_BIT BIT(24)
#define SDRAM_OPERATION_OFFS 0x18
#define SDRAM_OPERATION_SELF_REFRESH 0x7
#define SDRAM_DLB_EVICTION_OFFS 0x30c
#define SDRAM_DLB_EVICTION_THRESHOLD_MASK 0xff
static void (*mvebu_board_pm_enter)(void __iomem *sdram_reg, u32 srcmd);
static void __iomem *sdram_ctrl;
static int mvebu_pm_powerdown(unsigned long data)
{
u32 reg, srcmd;
flush_cache_all();
outer_flush_all();
/*
* Issue a Data Synchronization Barrier instruction to ensure
* that all state saving has been completed.
*/
dsb();
/* Flush the DLB and wait ~7 usec */
reg = readl(sdram_ctrl + SDRAM_DLB_EVICTION_OFFS);
reg &= ~SDRAM_DLB_EVICTION_THRESHOLD_MASK;
writel(reg, sdram_ctrl + SDRAM_DLB_EVICTION_OFFS);
udelay(7);
/* Set DRAM in battery backup mode */
reg = readl(sdram_ctrl + SDRAM_CONFIG_OFFS);
reg &= ~SDRAM_CONFIG_SR_MODE_BIT;
writel(reg, sdram_ctrl + SDRAM_CONFIG_OFFS);
/* Prepare to go to self-refresh */
srcmd = readl(sdram_ctrl + SDRAM_OPERATION_OFFS);
srcmd &= ~0x1F;
srcmd |= SDRAM_OPERATION_SELF_REFRESH;
mvebu_board_pm_enter(sdram_ctrl + SDRAM_OPERATION_OFFS, srcmd);
return 0;
}
#define BOOT_INFO_ADDR 0x3000
#define BOOT_MAGIC_WORD 0xdeadb002
#define BOOT_MAGIC_LIST_END 0xffffffff
/*
* Those registers are accessed before switching the internal register
* base, which is why we hardcode the 0xd0000000 base address, the one
* used by the SoC out of reset.
*/
#define MBUS_WINDOW_12_CTRL 0xd00200b0
#define MBUS_INTERNAL_REG_ADDRESS 0xd0020080
#define SDRAM_WIN_BASE_REG(x) (0x20180 + (0x8*x))
#define SDRAM_WIN_CTRL_REG(x) (0x20184 + (0x8*x))
static phys_addr_t mvebu_internal_reg_base(void)
{
struct device_node *np;
__be32 in_addr[2];
np = of_find_node_by_name(NULL, "internal-regs");
BUG_ON(!np);
/*
* Ask the DT what is the internal register address on this
* platform. In the mvebu-mbus DT binding, 0xf0010000
* corresponds to the internal register window.
*/
in_addr[0] = cpu_to_be32(0xf0010000);
in_addr[1] = 0x0;
return of_translate_address(np, in_addr);
}
static void mvebu_pm_store_bootinfo(void)
{
u32 *store_addr;
phys_addr_t resume_pc;
store_addr = phys_to_virt(BOOT_INFO_ADDR);
resume_pc = virt_to_phys(armada_370_xp_cpu_resume);
/*
* The bootloader expects the first two words to be a magic
* value (BOOT_MAGIC_WORD), followed by the address of the
* resume code to jump to. Then, it expects a sequence of
* (address, value) pairs, which can be used to restore the
* value of certain registers. This sequence must end with the
* BOOT_MAGIC_LIST_END magic value.
*/
writel(BOOT_MAGIC_WORD, store_addr++);
writel(resume_pc, store_addr++);
/*
* Some platforms remap their internal register base address
* to 0xf1000000. However, out of reset, window 12 starts at
* 0xf0000000 and ends at 0xf7ffffff, which would overlap with
* the internal registers. Therefore, disable window 12.
*/
writel(MBUS_WINDOW_12_CTRL, store_addr++);
writel(0x0, store_addr++);
/*
* Set the internal register base address to the value
* expected by Linux, as read from the Device Tree.
*/
writel(MBUS_INTERNAL_REG_ADDRESS, store_addr++);
writel(mvebu_internal_reg_base(), store_addr++);
/*
* Ask the mvebu-mbus driver to store the SDRAM window
* configuration, which has to be restored by the bootloader
* before re-entering the kernel on resume.
*/
store_addr += mvebu_mbus_save_cpu_target(store_addr);
writel(BOOT_MAGIC_LIST_END, store_addr);
}
static int mvebu_pm_enter(suspend_state_t state)
{
if (state != PM_SUSPEND_MEM)
return -EINVAL;
cpu_pm_enter();
mvebu_pm_store_bootinfo();
cpu_suspend(0, mvebu_pm_powerdown);
outer_resume();
mvebu_v7_pmsu_idle_exit();
set_cpu_coherent();
cpu_pm_exit();
return 0;
}
static const struct platform_suspend_ops mvebu_pm_ops = {
.enter = mvebu_pm_enter,
.valid = suspend_valid_only_mem,
};
int mvebu_pm_init(void (*board_pm_enter)(void __iomem *sdram_reg, u32 srcmd))
{
struct device_node *np;
struct resource res;
if (!of_machine_is_compatible("marvell,armadaxp"))
return -ENODEV;
np = of_find_compatible_node(NULL, NULL,
"marvell,armada-xp-sdram-controller");
if (!np)
return -ENODEV;
if (of_address_to_resource(np, 0, &res)) {
of_node_put(np);
return -ENODEV;
}
if (!request_mem_region(res.start, resource_size(&res),
np->full_name)) {
of_node_put(np);
return -EBUSY;
}
sdram_ctrl = ioremap(res.start, resource_size(&res));
if (!sdram_ctrl) {
release_mem_region(res.start, resource_size(&res));
of_node_put(np);
return -ENOMEM;
}
of_node_put(np);
mvebu_board_pm_enter = board_pm_enter;
suspend_set_ops(&mvebu_pm_ops);
return 0;
}

View File

@ -17,6 +17,7 @@ int mvebu_setup_boot_addr_wa(unsigned int crypto_eng_target,
phys_addr_t resume_addr_reg); phys_addr_t resume_addr_reg);
void mvebu_v7_pmsu_idle_exit(void); void mvebu_v7_pmsu_idle_exit(void);
void armada_370_xp_cpu_resume(void);
int armada_370_xp_pmsu_idle_enter(unsigned long deepidle); int armada_370_xp_pmsu_idle_enter(unsigned long deepidle);
int armada_38x_do_cpu_suspend(unsigned long deepidle); int armada_38x_do_cpu_suspend(unsigned long deepidle);

View File

@ -30,6 +30,14 @@ ENDPROC(armada_38x_scu_power_up)
*/ */
ENTRY(armada_370_xp_cpu_resume) ENTRY(armada_370_xp_cpu_resume)
ARM_BE8(setend be ) @ go BE8 if entered LE ARM_BE8(setend be ) @ go BE8 if entered LE
/*
* Disable the MMU that might have been enabled in BootROM if
* this code is used in the resume path of a suspend/resume
* cycle.
*/
mrc p15, 0, r1, c1, c0, 0
bic r1, #1
mcr p15, 0, r1, c1, c0, 0
bl ll_add_cpu_to_smp_group bl ll_add_cpu_to_smp_group
bl ll_enable_coherency bl ll_enable_coherency
b cpu_resume b cpu_resume

View File

@ -57,6 +57,7 @@
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/log2.h> #include <linux/log2.h>
#include <linux/syscore_ops.h>
/* /*
* DDR target is the same on all platforms. * DDR target is the same on all platforms.
@ -94,20 +95,42 @@
#define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4) #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_state;
struct mvebu_mbus_soc_data { struct mvebu_mbus_soc_data {
unsigned int num_wins; unsigned int num_wins;
unsigned int num_remappable_wins; unsigned int num_remappable_wins;
bool has_mbus_bridge;
unsigned int (*win_cfg_offset)(const int win); unsigned int (*win_cfg_offset)(const int win);
void (*setup_cpu_target)(struct mvebu_mbus_state *s); 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, int (*show_cpu_target)(struct mvebu_mbus_state *s,
struct seq_file *seq, void *v); 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 { struct mvebu_mbus_state {
void __iomem *mbuswins_base; void __iomem *mbuswins_base;
void __iomem *sdramwins_base; void __iomem *sdramwins_base;
void __iomem *mbusbridge_base;
phys_addr_t sdramwins_phys_base;
struct dentry *debugfs_root; struct dentry *debugfs_root;
struct dentry *debugfs_sdram; struct dentry *debugfs_sdram;
struct dentry *debugfs_devs; struct dentry *debugfs_devs;
@ -115,6 +138,11 @@ struct mvebu_mbus_state {
struct resource pcie_io_aperture; struct resource pcie_io_aperture;
const struct mvebu_mbus_soc_data *soc; const struct mvebu_mbus_soc_data *soc;
int hw_io_coherency; 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 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; 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 static void __init
mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus) 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; 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 = { static const struct mvebu_mbus_soc_data armada_370_xp_mbus_data = {
.num_wins = 20, .num_wins = 20,
.num_remappable_wins = 8, .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_offset,
.save_cpu_target = mvebu_mbus_default_save_cpu_target,
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion, .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_wins = 8,
.num_remappable_wins = 4, .num_remappable_wins = 4,
.win_cfg_offset = orion_mbus_win_offset, .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, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion, .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_wins = 8,
.num_remappable_wins = 4, .num_remappable_wins = 4,
.win_cfg_offset = orion_mbus_win_offset, .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, .setup_cpu_target = mvebu_mbus_dove_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_dove, .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_wins = 8,
.num_remappable_wins = 4, .num_remappable_wins = 4,
.win_cfg_offset = orion_mbus_win_offset, .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, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion, .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_wins = 8,
.num_remappable_wins = 2, .num_remappable_wins = 2,
.win_cfg_offset = orion_mbus_win_offset, .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, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion, .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_wins = 14,
.num_remappable_wins = 8, .num_remappable_wins = 8,
.win_cfg_offset = mv78xx0_mbus_win_offset, .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, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion, .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); 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, static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
phys_addr_t mbuswins_phys_base, phys_addr_t mbuswins_phys_base,
size_t mbuswins_size, size_t mbuswins_size,
phys_addr_t sdramwins_phys_base, 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; int win;
@ -716,11 +858,26 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
return -ENOMEM; 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++) for (win = 0; win < mbus->soc->num_wins; win++)
mvebu_mbus_disable_window(mbus, win); mvebu_mbus_disable_window(mbus, win);
mbus->soc->setup_cpu_target(mbus); mbus->soc->setup_cpu_target(mbus);
register_syscore_ops(&mvebu_mbus_syscore_ops);
return 0; return 0;
} }
@ -746,7 +903,7 @@ int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base,
mbuswins_phys_base, mbuswins_phys_base,
mbuswins_size, mbuswins_size,
sdramwins_phys_base, sdramwins_phys_base,
sdramwins_size); sdramwins_size, 0, 0);
} }
#ifdef CONFIG_OF #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) 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; struct device_node *np, *controller;
const struct of_device_id *of_id; const struct of_device_id *of_id;
const __be32 *prop; const __be32 *prop;
@ -923,6 +1080,19 @@ int __init mvebu_mbus_dt_init(bool is_coherent)
return -EINVAL; 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; mbus_state.hw_io_coherency = is_coherent;
/* Get optional pcie-{mem,io}-aperture properties */ /* Get optional pcie-{mem,io}-aperture properties */
@ -933,7 +1103,9 @@ int __init mvebu_mbus_dt_init(bool is_coherent)
mbuswins_res.start, mbuswins_res.start,
resource_size(&mbuswins_res), resource_size(&mbuswins_res),
sdramwins_res.start, sdramwins_res.start,
resource_size(&sdramwins_res)); resource_size(&sdramwins_res),
mbusbridge_res.start,
resource_size(&mbusbridge_res));
if (ret) if (ret)
return ret; return ret;

View File

@ -19,6 +19,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/syscore_ops.h>
#include "common.h" #include "common.h"
@ -177,14 +178,17 @@ struct clk_gating_ctrl {
spinlock_t *lock; spinlock_t *lock;
struct clk **gates; struct clk **gates;
int num_gates; int num_gates;
void __iomem *base;
u32 saved_reg;
}; };
#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) #define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
static struct clk_gating_ctrl *ctrl;
static struct clk *clk_gating_get_src( static struct clk *clk_gating_get_src(
struct of_phandle_args *clkspec, void *data) struct of_phandle_args *clkspec, void *data)
{ {
struct clk_gating_ctrl *ctrl = (struct clk_gating_ctrl *)data;
int n; int n;
if (clkspec->args_count < 1) if (clkspec->args_count < 1)
@ -199,15 +203,35 @@ static struct clk *clk_gating_get_src(
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
} }
static int mvebu_clk_gating_suspend(void)
{
ctrl->saved_reg = readl(ctrl->base);
return 0;
}
static void mvebu_clk_gating_resume(void)
{
writel(ctrl->saved_reg, ctrl->base);
}
static struct syscore_ops clk_gate_syscore_ops = {
.suspend = mvebu_clk_gating_suspend,
.resume = mvebu_clk_gating_resume,
};
void __init mvebu_clk_gating_setup(struct device_node *np, void __init mvebu_clk_gating_setup(struct device_node *np,
const struct clk_gating_soc_desc *desc) const struct clk_gating_soc_desc *desc)
{ {
struct clk_gating_ctrl *ctrl;
struct clk *clk; struct clk *clk;
void __iomem *base; void __iomem *base;
const char *default_parent = NULL; const char *default_parent = NULL;
int n; int n;
if (ctrl) {
pr_err("mvebu-clk-gating: cannot instantiate more than one gatable clock device\n");
return;
}
base = of_iomap(np, 0); base = of_iomap(np, 0);
if (WARN_ON(!base)) if (WARN_ON(!base))
return; return;
@ -225,6 +249,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np,
/* lock must already be initialized */ /* lock must already be initialized */
ctrl->lock = &ctrl_gating_lock; ctrl->lock = &ctrl_gating_lock;
ctrl->base = base;
/* Count, allocate, and register clock gates */ /* Count, allocate, and register clock gates */
for (n = 0; desc[n].name;) for (n = 0; desc[n].name;)
n++; n++;
@ -246,6 +272,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np,
of_clk_add_provider(np, clk_gating_get_src, ctrl); of_clk_add_provider(np, clk_gating_get_src, ctrl);
register_syscore_ops(&clk_gate_syscore_ops);
return; return;
gates_out: gates_out:
kfree(ctrl); kfree(ctrl);

View File

@ -43,6 +43,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/sched_clock.h> #include <linux/sched_clock.h>
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/syscore_ops.h>
/* /*
* Timer block registers. * Timer block registers.
@ -223,6 +224,28 @@ static struct notifier_block armada_370_xp_timer_cpu_nb = {
.notifier_call = armada_370_xp_timer_cpu_notify, .notifier_call = armada_370_xp_timer_cpu_notify,
}; };
static u32 timer0_ctrl_reg, timer0_local_ctrl_reg;
static int armada_370_xp_timer_suspend(void)
{
timer0_ctrl_reg = readl(timer_base + TIMER_CTRL_OFF);
timer0_local_ctrl_reg = readl(local_base + TIMER_CTRL_OFF);
return 0;
}
static void armada_370_xp_timer_resume(void)
{
writel(0xffffffff, timer_base + TIMER0_VAL_OFF);
writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF);
writel(timer0_ctrl_reg, timer_base + TIMER_CTRL_OFF);
writel(timer0_local_ctrl_reg, local_base + TIMER_CTRL_OFF);
}
struct syscore_ops armada_370_xp_timer_syscore_ops = {
.suspend = armada_370_xp_timer_suspend,
.resume = armada_370_xp_timer_resume,
};
static void __init armada_370_xp_timer_common_init(struct device_node *np) static void __init armada_370_xp_timer_common_init(struct device_node *np)
{ {
u32 clr = 0, set = 0; u32 clr = 0, set = 0;
@ -285,6 +308,8 @@ static void __init armada_370_xp_timer_common_init(struct device_node *np)
/* Immediately configure the timer on the boot CPU */ /* Immediately configure the timer on the boot CPU */
if (!res) if (!res)
armada_370_xp_timer_setup(this_cpu_ptr(armada_370_xp_evt)); armada_370_xp_timer_setup(this_cpu_ptr(armada_370_xp_evt));
register_syscore_ops(&armada_370_xp_timer_syscore_ops);
} }
static void __init armada_xp_timer_init(struct device_node *np) static void __init armada_xp_timer_init(struct device_node *np)

View File

@ -26,6 +26,7 @@
#include <linux/of_pci.h> #include <linux/of_pci.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/syscore_ops.h>
#include <linux/msi.h> #include <linux/msi.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/exception.h> #include <asm/exception.h>
@ -66,6 +67,7 @@
static void __iomem *per_cpu_int_base; static void __iomem *per_cpu_int_base;
static void __iomem *main_int_base; static void __iomem *main_int_base;
static struct irq_domain *armada_370_xp_mpic_domain; static struct irq_domain *armada_370_xp_mpic_domain;
static u32 doorbell_mask_reg;
#ifdef CONFIG_PCI_MSI #ifdef CONFIG_PCI_MSI
static struct irq_domain *armada_370_xp_msi_domain; static struct irq_domain *armada_370_xp_msi_domain;
static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
@ -474,6 +476,54 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
} while (1); } while (1);
} }
static int armada_370_xp_mpic_suspend(void)
{
doorbell_mask_reg = readl(per_cpu_int_base +
ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
return 0;
}
static void armada_370_xp_mpic_resume(void)
{
int nirqs;
irq_hw_number_t irq;
/* Re-enable interrupts */
nirqs = (readl(main_int_base + ARMADA_370_XP_INT_CONTROL) >> 2) & 0x3ff;
for (irq = 0; irq < nirqs; irq++) {
struct irq_data *data;
int virq;
virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq);
if (virq == 0)
continue;
if (irq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ)
writel(irq, per_cpu_int_base +
ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
else
writel(irq, main_int_base +
ARMADA_370_XP_INT_SET_ENABLE_OFFS);
data = irq_get_irq_data(virq);
if (!irqd_irq_disabled(data))
armada_370_xp_irq_unmask(data);
}
/* Reconfigure doorbells for IPIs and MSIs */
writel(doorbell_mask_reg,
per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
if (doorbell_mask_reg & IPI_DOORBELL_MASK)
writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK)
writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
}
struct syscore_ops armada_370_xp_mpic_syscore_ops = {
.suspend = armada_370_xp_mpic_suspend,
.resume = armada_370_xp_mpic_resume,
};
static int __init armada_370_xp_mpic_of_init(struct device_node *node, static int __init armada_370_xp_mpic_of_init(struct device_node *node,
struct device_node *parent) struct device_node *parent)
{ {
@ -530,6 +580,8 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
armada_370_xp_mpic_handle_cascade_irq); armada_370_xp_mpic_handle_cascade_irq);
} }
register_syscore_ops(&armada_370_xp_mpic_syscore_ops);
return 0; return 0;
} }

View File

@ -61,6 +61,7 @@ static inline const struct mbus_dram_target_info *mv_mbus_dram_info(void)
} }
#endif #endif
int mvebu_mbus_save_cpu_target(u32 *store_addr);
void mvebu_mbus_get_pcie_mem_aperture(struct resource *res); void mvebu_mbus_get_pcie_mem_aperture(struct resource *res);
void mvebu_mbus_get_pcie_io_aperture(struct resource *res); void mvebu_mbus_get_pcie_io_aperture(struct resource *res);
int mvebu_mbus_add_window_remap_by_id(unsigned int target, int mvebu_mbus_add_window_remap_by_id(unsigned int target,