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:
commit
ab64920c37
|
@ -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 {
|
||||||
|
|
|
@ -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>;
|
||||||
|
};
|
|
@ -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 {
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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, ®);
|
||||||
|
size = dt_mem_next_cell(dt_root_size_cells, ®);
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue