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:
Коммит
ab64920c37
|
@ -48,9 +48,12 @@ Required properties:
|
|||
- compatible: Should be set to "marvell,mbus-controller".
|
||||
|
||||
- reg: Device's register space.
|
||||
Two entries are expected (see the examples below):
|
||||
the first one controls the devices decoding window and
|
||||
the second one controls the SDRAM decoding window.
|
||||
Two or three entries are expected (see the examples below):
|
||||
the first one controls the devices decoding window,
|
||||
the second one controls the SDRAM decoding window and
|
||||
the third controls the MBus bridge (only with the
|
||||
marvell,armada370-mbus and marvell,armadaxp-mbus
|
||||
compatible strings)
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -67,7 +70,7 @@ Example:
|
|||
|
||||
mbusc: mbus-controller@20000 {
|
||||
compatible = "marvell,mbus-controller";
|
||||
reg = <0x20000 0x100>, <0x20180 0x20>;
|
||||
reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
|
||||
};
|
||||
|
||||
/* more children ...*/
|
||||
|
@ -126,7 +129,7 @@ are skipped.
|
|||
|
||||
mbusc: mbus-controller@20000 {
|
||||
compatible = "marvell,mbus-controller";
|
||||
reg = <0x20000 0x100>, <0x20180 0x20>;
|
||||
reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
|
||||
};
|
||||
|
||||
/* more children ...*/
|
||||
|
@ -170,7 +173,7 @@ Using this macro, the above example would be:
|
|||
|
||||
mbusc: mbus-controller@20000 {
|
||||
compatible = "marvell,mbus-controller";
|
||||
reg = <0x20000 0x100>, <0x20180 0x20>;
|
||||
reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
|
||||
};
|
||||
|
||||
/* 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>;
|
||||
|
||||
mbusc: mbus-controller@20000 {
|
||||
reg = <0x20000 0x100>, <0x20180 0x20>;
|
||||
reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
|
||||
};
|
||||
|
||||
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 {
|
||||
compatible = "marvell,mbus-controller";
|
||||
reg = <0x20000 0x100>, <0x20180 0x20>;
|
||||
reg = <0x20000 0x100>, <0x20180 0x20>,
|
||||
<0x20250 0x8>;
|
||||
};
|
||||
|
||||
mpic: interrupt-controller@20000 {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include "armada-xp-mv78460.dtsi"
|
||||
|
||||
/ {
|
||||
|
@ -48,6 +49,14 @@
|
|||
<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 {
|
||||
ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xf1000000 0x100000
|
||||
MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000
|
||||
|
@ -115,7 +124,15 @@
|
|||
serial@12300 {
|
||||
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 {
|
||||
nr-ports = <2>;
|
||||
status = "okay";
|
||||
|
|
|
@ -35,6 +35,11 @@
|
|||
};
|
||||
|
||||
internal-regs {
|
||||
sdramc@1400 {
|
||||
compatible = "marvell,armada-xp-sdram-controller";
|
||||
reg = <0x1400 0x500>;
|
||||
};
|
||||
|
||||
L2: l2-cache {
|
||||
compatible = "marvell,aurora-system-cache";
|
||||
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
|
||||
|
||||
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
|
||||
endif
|
||||
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/mbus.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -56,6 +58,54 @@ void __iomem *mvebu_get_scu_base(void)
|
|||
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
|
||||
* 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_irq = mvebu_init_irq,
|
||||
.restart = mvebu_restart,
|
||||
.reserve = mvebu_memblock_reserve,
|
||||
.dt_compat = armada_370_xp_dt_compat,
|
||||
MACHINE_END
|
||||
|
||||
|
|
|
@ -25,4 +25,6 @@ int mvebu_system_controller_get_soc_id(u32 *dev, u32 *rev);
|
|||
|
||||
void __iomem *mvebu_get_scu_base(void);
|
||||
|
||||
int mvebu_pm_init(void (*board_pm_enter)(void __iomem *sdram_reg, u32 srcmd));
|
||||
|
||||
#endif
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#define AXP_BOOTROM_BASE 0xfff00000
|
||||
#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 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;
|
||||
}
|
||||
|
||||
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;
|
||||
struct clk *cpu_clk;
|
||||
|
||||
thiscpu = smp_processor_id();
|
||||
thiscpu = get_cpu();
|
||||
|
||||
cpu_clk = get_cpu_clk(thiscpu);
|
||||
if (!cpu_clk)
|
||||
return;
|
||||
goto out;
|
||||
clk_prepare_enable(cpu_clk);
|
||||
rate = clk_get_rate(cpu_clk);
|
||||
|
||||
/* set all the other CPU clk to the same rate than the boot CPU */
|
||||
for_each_possible_cpu(cpu) {
|
||||
if (cpu == thiscpu)
|
||||
continue;
|
||||
cpu_clk = get_cpu_clk(cpu);
|
||||
if (!cpu_clk)
|
||||
return;
|
||||
clk_set_rate(cpu_clk, rate);
|
||||
clk_prepare_enable(cpu_clk);
|
||||
}
|
||||
cpu_clk = get_cpu_clk(cpu);
|
||||
if (!cpu_clk)
|
||||
goto out;
|
||||
clk_set_rate(cpu_clk, rate);
|
||||
clk_prepare_enable(cpu_clk);
|
||||
|
||||
out:
|
||||
put_cpu();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
hw_cpu = cpu_logical_map(cpu);
|
||||
set_secondary_cpu_clock(hw_cpu);
|
||||
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;
|
||||
int err;
|
||||
|
||||
set_secondary_cpus_clock();
|
||||
flush_cache_all();
|
||||
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);
|
||||
|
||||
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_38x_do_cpu_suspend(unsigned long deepidle);
|
||||
|
|
|
@ -30,6 +30,14 @@ ENDPROC(armada_38x_scu_power_up)
|
|||
*/
|
||||
ENTRY(armada_370_xp_cpu_resume)
|
||||
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_enable_coherency
|
||||
b cpu_resume
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
#include <linux/of_address.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
/*
|
||||
* DDR target is the same on all platforms.
|
||||
|
@ -94,20 +95,42 @@
|
|||
|
||||
#define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4)
|
||||
|
||||
/* Relative to mbusbridge_base */
|
||||
#define MBUS_BRIDGE_CTRL_OFF 0x0
|
||||
#define MBUS_BRIDGE_BASE_OFF 0x4
|
||||
|
||||
/* Maximum number of windows, for all known platforms */
|
||||
#define MBUS_WINS_MAX 20
|
||||
|
||||
struct mvebu_mbus_state;
|
||||
|
||||
struct mvebu_mbus_soc_data {
|
||||
unsigned int num_wins;
|
||||
unsigned int num_remappable_wins;
|
||||
bool has_mbus_bridge;
|
||||
unsigned int (*win_cfg_offset)(const int win);
|
||||
void (*setup_cpu_target)(struct mvebu_mbus_state *s);
|
||||
int (*save_cpu_target)(struct mvebu_mbus_state *s,
|
||||
u32 *store_addr);
|
||||
int (*show_cpu_target)(struct mvebu_mbus_state *s,
|
||||
struct seq_file *seq, void *v);
|
||||
};
|
||||
|
||||
/*
|
||||
* Used to store the state of one MBus window accross suspend/resume.
|
||||
*/
|
||||
struct mvebu_mbus_win_data {
|
||||
u32 ctrl;
|
||||
u32 base;
|
||||
u32 remap_lo;
|
||||
u32 remap_hi;
|
||||
};
|
||||
|
||||
struct mvebu_mbus_state {
|
||||
void __iomem *mbuswins_base;
|
||||
void __iomem *sdramwins_base;
|
||||
void __iomem *mbusbridge_base;
|
||||
phys_addr_t sdramwins_phys_base;
|
||||
struct dentry *debugfs_root;
|
||||
struct dentry *debugfs_sdram;
|
||||
struct dentry *debugfs_devs;
|
||||
|
@ -115,6 +138,11 @@ struct mvebu_mbus_state {
|
|||
struct resource pcie_io_aperture;
|
||||
const struct mvebu_mbus_soc_data *soc;
|
||||
int hw_io_coherency;
|
||||
|
||||
/* Used during suspend/resume */
|
||||
u32 mbus_bridge_ctrl;
|
||||
u32 mbus_bridge_base;
|
||||
struct mvebu_mbus_win_data wins[MBUS_WINS_MAX];
|
||||
};
|
||||
|
||||
static struct mvebu_mbus_state mbus_state;
|
||||
|
@ -516,6 +544,28 @@ mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
|
|||
mvebu_mbus_dram_info.num_cs = cs;
|
||||
}
|
||||
|
||||
static int
|
||||
mvebu_mbus_default_save_cpu_target(struct mvebu_mbus_state *mbus,
|
||||
u32 *store_addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i));
|
||||
u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i));
|
||||
|
||||
writel(mbus->sdramwins_phys_base + DDR_BASE_CS_OFF(i),
|
||||
store_addr++);
|
||||
writel(base, store_addr++);
|
||||
writel(mbus->sdramwins_phys_base + DDR_SIZE_CS_OFF(i),
|
||||
store_addr++);
|
||||
writel(size, store_addr++);
|
||||
}
|
||||
|
||||
/* We've written 16 words to the store address */
|
||||
return 16;
|
||||
}
|
||||
|
||||
static void __init
|
||||
mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus)
|
||||
{
|
||||
|
@ -546,10 +596,35 @@ mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus)
|
|||
mvebu_mbus_dram_info.num_cs = cs;
|
||||
}
|
||||
|
||||
static int
|
||||
mvebu_mbus_dove_save_cpu_target(struct mvebu_mbus_state *mbus,
|
||||
u32 *store_addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
u32 map = readl(mbus->sdramwins_base + DOVE_DDR_BASE_CS_OFF(i));
|
||||
|
||||
writel(mbus->sdramwins_phys_base + DOVE_DDR_BASE_CS_OFF(i),
|
||||
store_addr++);
|
||||
writel(map, store_addr++);
|
||||
}
|
||||
|
||||
/* We've written 4 words to the store address */
|
||||
return 4;
|
||||
}
|
||||
|
||||
int mvebu_mbus_save_cpu_target(u32 *store_addr)
|
||||
{
|
||||
return mbus_state.soc->save_cpu_target(&mbus_state, store_addr);
|
||||
}
|
||||
|
||||
static const struct mvebu_mbus_soc_data armada_370_xp_mbus_data = {
|
||||
.num_wins = 20,
|
||||
.num_remappable_wins = 8,
|
||||
.has_mbus_bridge = true,
|
||||
.win_cfg_offset = armada_370_xp_mbus_win_offset,
|
||||
.save_cpu_target = mvebu_mbus_default_save_cpu_target,
|
||||
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
|
||||
.show_cpu_target = mvebu_sdram_debug_show_orion,
|
||||
};
|
||||
|
@ -558,6 +633,7 @@ static const struct mvebu_mbus_soc_data kirkwood_mbus_data = {
|
|||
.num_wins = 8,
|
||||
.num_remappable_wins = 4,
|
||||
.win_cfg_offset = orion_mbus_win_offset,
|
||||
.save_cpu_target = mvebu_mbus_default_save_cpu_target,
|
||||
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
|
||||
.show_cpu_target = mvebu_sdram_debug_show_orion,
|
||||
};
|
||||
|
@ -566,6 +642,7 @@ static const struct mvebu_mbus_soc_data dove_mbus_data = {
|
|||
.num_wins = 8,
|
||||
.num_remappable_wins = 4,
|
||||
.win_cfg_offset = orion_mbus_win_offset,
|
||||
.save_cpu_target = mvebu_mbus_dove_save_cpu_target,
|
||||
.setup_cpu_target = mvebu_mbus_dove_setup_cpu_target,
|
||||
.show_cpu_target = mvebu_sdram_debug_show_dove,
|
||||
};
|
||||
|
@ -578,6 +655,7 @@ static const struct mvebu_mbus_soc_data orion5x_4win_mbus_data = {
|
|||
.num_wins = 8,
|
||||
.num_remappable_wins = 4,
|
||||
.win_cfg_offset = orion_mbus_win_offset,
|
||||
.save_cpu_target = mvebu_mbus_default_save_cpu_target,
|
||||
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
|
||||
.show_cpu_target = mvebu_sdram_debug_show_orion,
|
||||
};
|
||||
|
@ -586,6 +664,7 @@ static const struct mvebu_mbus_soc_data orion5x_2win_mbus_data = {
|
|||
.num_wins = 8,
|
||||
.num_remappable_wins = 2,
|
||||
.win_cfg_offset = orion_mbus_win_offset,
|
||||
.save_cpu_target = mvebu_mbus_default_save_cpu_target,
|
||||
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
|
||||
.show_cpu_target = mvebu_sdram_debug_show_orion,
|
||||
};
|
||||
|
@ -594,6 +673,7 @@ static const struct mvebu_mbus_soc_data mv78xx0_mbus_data = {
|
|||
.num_wins = 14,
|
||||
.num_remappable_wins = 8,
|
||||
.win_cfg_offset = mv78xx0_mbus_win_offset,
|
||||
.save_cpu_target = mvebu_mbus_default_save_cpu_target,
|
||||
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
|
||||
.show_cpu_target = mvebu_sdram_debug_show_orion,
|
||||
};
|
||||
|
@ -698,11 +778,73 @@ static __init int mvebu_mbus_debugfs_init(void)
|
|||
}
|
||||
fs_initcall(mvebu_mbus_debugfs_init);
|
||||
|
||||
static int mvebu_mbus_suspend(void)
|
||||
{
|
||||
struct mvebu_mbus_state *s = &mbus_state;
|
||||
int win;
|
||||
|
||||
if (!s->mbusbridge_base)
|
||||
return -ENODEV;
|
||||
|
||||
for (win = 0; win < s->soc->num_wins; win++) {
|
||||
void __iomem *addr = s->mbuswins_base +
|
||||
s->soc->win_cfg_offset(win);
|
||||
|
||||
s->wins[win].base = readl(addr + WIN_BASE_OFF);
|
||||
s->wins[win].ctrl = readl(addr + WIN_CTRL_OFF);
|
||||
|
||||
if (win >= s->soc->num_remappable_wins)
|
||||
continue;
|
||||
|
||||
s->wins[win].remap_lo = readl(addr + WIN_REMAP_LO_OFF);
|
||||
s->wins[win].remap_hi = readl(addr + WIN_REMAP_HI_OFF);
|
||||
}
|
||||
|
||||
s->mbus_bridge_ctrl = readl(s->mbusbridge_base +
|
||||
MBUS_BRIDGE_CTRL_OFF);
|
||||
s->mbus_bridge_base = readl(s->mbusbridge_base +
|
||||
MBUS_BRIDGE_BASE_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mvebu_mbus_resume(void)
|
||||
{
|
||||
struct mvebu_mbus_state *s = &mbus_state;
|
||||
int win;
|
||||
|
||||
writel(s->mbus_bridge_ctrl,
|
||||
s->mbusbridge_base + MBUS_BRIDGE_CTRL_OFF);
|
||||
writel(s->mbus_bridge_base,
|
||||
s->mbusbridge_base + MBUS_BRIDGE_BASE_OFF);
|
||||
|
||||
for (win = 0; win < s->soc->num_wins; win++) {
|
||||
void __iomem *addr = s->mbuswins_base +
|
||||
s->soc->win_cfg_offset(win);
|
||||
|
||||
writel(s->wins[win].base, addr + WIN_BASE_OFF);
|
||||
writel(s->wins[win].ctrl, addr + WIN_CTRL_OFF);
|
||||
|
||||
if (win >= s->soc->num_remappable_wins)
|
||||
continue;
|
||||
|
||||
writel(s->wins[win].remap_lo, addr + WIN_REMAP_LO_OFF);
|
||||
writel(s->wins[win].remap_hi, addr + WIN_REMAP_HI_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
struct syscore_ops mvebu_mbus_syscore_ops = {
|
||||
.suspend = mvebu_mbus_suspend,
|
||||
.resume = mvebu_mbus_resume,
|
||||
};
|
||||
|
||||
static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
|
||||
phys_addr_t mbuswins_phys_base,
|
||||
size_t mbuswins_size,
|
||||
phys_addr_t sdramwins_phys_base,
|
||||
size_t sdramwins_size)
|
||||
size_t sdramwins_size,
|
||||
phys_addr_t mbusbridge_phys_base,
|
||||
size_t mbusbridge_size)
|
||||
{
|
||||
int win;
|
||||
|
||||
|
@ -716,11 +858,26 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mbus->sdramwins_phys_base = sdramwins_phys_base;
|
||||
|
||||
if (mbusbridge_phys_base) {
|
||||
mbus->mbusbridge_base = ioremap(mbusbridge_phys_base,
|
||||
mbusbridge_size);
|
||||
if (!mbus->mbusbridge_base) {
|
||||
iounmap(mbus->sdramwins_base);
|
||||
iounmap(mbus->mbuswins_base);
|
||||
return -ENOMEM;
|
||||
}
|
||||
} else
|
||||
mbus->mbusbridge_base = NULL;
|
||||
|
||||
for (win = 0; win < mbus->soc->num_wins; win++)
|
||||
mvebu_mbus_disable_window(mbus, win);
|
||||
|
||||
mbus->soc->setup_cpu_target(mbus);
|
||||
|
||||
register_syscore_ops(&mvebu_mbus_syscore_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -746,7 +903,7 @@ int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base,
|
|||
mbuswins_phys_base,
|
||||
mbuswins_size,
|
||||
sdramwins_phys_base,
|
||||
sdramwins_size);
|
||||
sdramwins_size, 0, 0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
@ -887,7 +1044,7 @@ static void __init mvebu_mbus_get_pcie_resources(struct device_node *np,
|
|||
|
||||
int __init mvebu_mbus_dt_init(bool is_coherent)
|
||||
{
|
||||
struct resource mbuswins_res, sdramwins_res;
|
||||
struct resource mbuswins_res, sdramwins_res, mbusbridge_res;
|
||||
struct device_node *np, *controller;
|
||||
const struct of_device_id *of_id;
|
||||
const __be32 *prop;
|
||||
|
@ -923,6 +1080,19 @@ int __init mvebu_mbus_dt_init(bool is_coherent)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the resource to 0 so that it can be left unmapped by
|
||||
* mvebu_mbus_common_init() if the DT doesn't carry the
|
||||
* necessary information. This is needed to preserve backward
|
||||
* compatibility.
|
||||
*/
|
||||
memset(&mbusbridge_res, 0, sizeof(mbusbridge_res));
|
||||
|
||||
if (mbus_state.soc->has_mbus_bridge) {
|
||||
if (of_address_to_resource(controller, 2, &mbusbridge_res))
|
||||
pr_warn(FW_WARN "deprecated mbus-mvebu Device Tree, suspend/resume will not work\n");
|
||||
}
|
||||
|
||||
mbus_state.hw_io_coherency = is_coherent;
|
||||
|
||||
/* Get optional pcie-{mem,io}-aperture properties */
|
||||
|
@ -933,7 +1103,9 @@ int __init mvebu_mbus_dt_init(bool is_coherent)
|
|||
mbuswins_res.start,
|
||||
resource_size(&mbuswins_res),
|
||||
sdramwins_res.start,
|
||||
resource_size(&sdramwins_res));
|
||||
resource_size(&sdramwins_res),
|
||||
mbusbridge_res.start,
|
||||
resource_size(&mbusbridge_res));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
|
@ -177,14 +178,17 @@ struct clk_gating_ctrl {
|
|||
spinlock_t *lock;
|
||||
struct clk **gates;
|
||||
int num_gates;
|
||||
void __iomem *base;
|
||||
u32 saved_reg;
|
||||
};
|
||||
|
||||
#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(
|
||||
struct of_phandle_args *clkspec, void *data)
|
||||
{
|
||||
struct clk_gating_ctrl *ctrl = (struct clk_gating_ctrl *)data;
|
||||
int n;
|
||||
|
||||
if (clkspec->args_count < 1)
|
||||
|
@ -199,15 +203,35 @@ static struct clk *clk_gating_get_src(
|
|||
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,
|
||||
const struct clk_gating_soc_desc *desc)
|
||||
{
|
||||
struct clk_gating_ctrl *ctrl;
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
const char *default_parent = NULL;
|
||||
int n;
|
||||
|
||||
if (ctrl) {
|
||||
pr_err("mvebu-clk-gating: cannot instantiate more than one gatable clock device\n");
|
||||
return;
|
||||
}
|
||||
|
||||
base = of_iomap(np, 0);
|
||||
if (WARN_ON(!base))
|
||||
return;
|
||||
|
@ -225,6 +249,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np,
|
|||
/* lock must already be initialized */
|
||||
ctrl->lock = &ctrl_gating_lock;
|
||||
|
||||
ctrl->base = base;
|
||||
|
||||
/* Count, allocate, and register clock gates */
|
||||
for (n = 0; desc[n].name;)
|
||||
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);
|
||||
|
||||
register_syscore_ops(&clk_gate_syscore_ops);
|
||||
|
||||
return;
|
||||
gates_out:
|
||||
kfree(ctrl);
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/sched_clock.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
/*
|
||||
* 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,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
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 */
|
||||
if (!res)
|
||||
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)
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/of_pci.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <linux/msi.h>
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/exception.h>
|
||||
|
@ -66,6 +67,7 @@
|
|||
static void __iomem *per_cpu_int_base;
|
||||
static void __iomem *main_int_base;
|
||||
static struct irq_domain *armada_370_xp_mpic_domain;
|
||||
static u32 doorbell_mask_reg;
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
static struct irq_domain *armada_370_xp_msi_domain;
|
||||
static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
|
||||
|
@ -474,6 +476,54 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
|
|||
} 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,
|
||||
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);
|
||||
}
|
||||
|
||||
register_syscore_ops(&armada_370_xp_mpic_syscore_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ static inline const struct mbus_dram_target_info *mv_mbus_dram_info(void)
|
|||
}
|
||||
#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_io_aperture(struct resource *res);
|
||||
int mvebu_mbus_add_window_remap_by_id(unsigned int target,
|
||||
|
|
Загрузка…
Ссылка в новой задаче