mirror of https://gitee.com/openkylin/linux.git
arm: Xilinx Zynq cleanup patches for v3.18
- PM support - Fix L2 useless setting -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (GNU/Linux) iEYEABECAAYFAlQYGFwACgkQykllyylKDCFI/ACeMQecelDM7sN3pOXVDA8unho5 eqEAnjwQL/kw2p8jru1+t13V1SxThxKa =xfgX -----END PGP SIGNATURE----- Merge tag 'zynq-cleanup-for-3.18' of git://git.xilinx.com/linux-xlnx into next/soc Pull "arm: Xilinx Zynq cleanup patches for v3.18" from Michal Simek: - PM support - Fix L2 useless setting Signed-off-by: Arnd Bergmann <arnd@arndb.de> * tag 'zynq-cleanup-for-3.18' of git://git.xilinx.com/linux-xlnx: ARM: zynq: Remove useless L2C AUX setting ARM: zynq: Rename 'zynq_platform_cpu_die' ARM: zynq: Remove hotplug.c ARM: zynq: Synchronise zynq_cpu_die/kill ARM: zynq: cpuidle: Remove pointless code ARM: zynq: Remove invalidate cache for cpu die ARM: zynq: PM: Enable DDR clock stop ARM: zynq: DT: Add DDRC node Documentation: devicetree: Add binding for Synopsys DDR controller ARM: zynq: PM: Enable A9 internal clock gating feature
This commit is contained in:
commit
e36087998a
|
@ -0,0 +1,11 @@
|
|||
Binding for Synopsys IntelliDDR Multi Protocol Memory Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be 'xlnx,zynq-ddrc-a05'
|
||||
- reg: Base address and size of the controllers memory area
|
||||
|
||||
Example:
|
||||
memory-controller@f8006000 {
|
||||
compatible = "xlnx,zynq-ddrc-a05";
|
||||
reg = <0xf8006000 0x1000>;
|
||||
};
|
|
@ -146,6 +146,11 @@ L2: cache-controller {
|
|||
cache-level = <2>;
|
||||
};
|
||||
|
||||
memory-controller@f8006000 {
|
||||
compatible = "xlnx,zynq-ddrc-a05";
|
||||
reg = <0xf8006000 0x1000>;
|
||||
} ;
|
||||
|
||||
uart0: serial@e0000000 {
|
||||
compatible = "xlnx,xuartps", "cdns,uart-r1p8";
|
||||
status = "disabled";
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
#
|
||||
|
||||
# Common support
|
||||
obj-y := common.o slcr.o
|
||||
obj-y := common.o slcr.o pm.o
|
||||
CFLAGS_REMOVE_hotplug.o =-march=armv6k
|
||||
CFLAGS_hotplug.o =-Wa,-march=armv7-a -mcpu=cortex-a9
|
||||
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
|
||||
obj-$(CONFIG_SMP) += headsmp.o platsmp.o
|
||||
|
|
|
@ -98,6 +98,12 @@ static int __init zynq_get_revision(void)
|
|||
return revision;
|
||||
}
|
||||
|
||||
static void __init zynq_init_late(void)
|
||||
{
|
||||
zynq_core_pm_init();
|
||||
zynq_pm_late_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* zynq_init_machine - System specific initialization, intended to be
|
||||
* called from board specific initialization.
|
||||
|
@ -198,12 +204,13 @@ static const char * const zynq_dt_match[] = {
|
|||
|
||||
DT_MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
|
||||
/* 64KB way size, 8-way associativity, parity disabled */
|
||||
.l2c_aux_val = 0x02000000,
|
||||
.l2c_aux_mask = 0xf0ffffff,
|
||||
.l2c_aux_val = 0x00000000,
|
||||
.l2c_aux_mask = 0xffffffff,
|
||||
.smp = smp_ops(zynq_smp_ops),
|
||||
.map_io = zynq_map_io,
|
||||
.init_irq = zynq_irq_init,
|
||||
.init_machine = zynq_init_machine,
|
||||
.init_late = zynq_init_late,
|
||||
.init_time = zynq_timer_init,
|
||||
.dt_compat = zynq_dt_match,
|
||||
.reserve = zynq_memory_init,
|
||||
|
|
|
@ -24,6 +24,8 @@ extern int zynq_early_slcr_init(void);
|
|||
extern void zynq_slcr_system_reset(void);
|
||||
extern void zynq_slcr_cpu_stop(int cpu);
|
||||
extern void zynq_slcr_cpu_start(int cpu);
|
||||
extern bool zynq_slcr_cpu_state_read(int cpu);
|
||||
extern void zynq_slcr_cpu_state_write(int cpu, bool die);
|
||||
extern u32 zynq_slcr_get_device_id(void);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
@ -37,7 +39,17 @@ extern struct smp_operations zynq_smp_ops __initdata;
|
|||
|
||||
extern void __iomem *zynq_scu_base;
|
||||
|
||||
/* Hotplug */
|
||||
extern void zynq_platform_cpu_die(unsigned int cpu);
|
||||
void zynq_pm_late_init(void);
|
||||
|
||||
static inline void zynq_core_pm_init(void)
|
||||
{
|
||||
/* A9 clock gating */
|
||||
asm volatile ("mrc p15, 0, r12, c15, c0, 0\n"
|
||||
"orr r12, r12, #1\n"
|
||||
"mcr p15, 0, r12, c15, c0, 0\n"
|
||||
: /* no outputs */
|
||||
: /* no inputs */
|
||||
: "r12");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -10,50 +10,5 @@
|
|||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/smp.h>
|
||||
#include <asm/proc-fns.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cp15.h>
|
||||
#include "common.h"
|
||||
|
||||
static inline void zynq_cpu_enter_lowpower(void)
|
||||
{
|
||||
unsigned int v;
|
||||
|
||||
flush_cache_all();
|
||||
asm volatile(
|
||||
" mcr p15, 0, %1, c7, c5, 0\n"
|
||||
" dsb\n"
|
||||
/*
|
||||
* Turn off coherency
|
||||
*/
|
||||
" mrc p15, 0, %0, c1, c0, 1\n"
|
||||
" bic %0, %0, #0x40\n"
|
||||
" mcr p15, 0, %0, c1, c0, 1\n"
|
||||
" mrc p15, 0, %0, c1, c0, 0\n"
|
||||
" bic %0, %0, %2\n"
|
||||
" mcr p15, 0, %0, c1, c0, 0\n"
|
||||
: "=&r" (v)
|
||||
: "r" (0), "Ir" (CR_C)
|
||||
: "cc");
|
||||
}
|
||||
|
||||
/*
|
||||
* platform-specific code to shutdown a CPU
|
||||
*
|
||||
* Called with IRQs disabled
|
||||
*/
|
||||
void zynq_platform_cpu_die(unsigned int cpu)
|
||||
{
|
||||
zynq_cpu_enter_lowpower();
|
||||
|
||||
/*
|
||||
* there is no power-control hardware on this platform, so all
|
||||
* we can do is put the core into WFI; this is safe as the calling
|
||||
* code will have already disabled interrupts
|
||||
*/
|
||||
for (;;)
|
||||
cpu_do_idle();
|
||||
}
|
||||
|
|
|
@ -112,20 +112,59 @@ static void __init zynq_smp_prepare_cpus(unsigned int max_cpus)
|
|||
scu_enable(zynq_scu_base);
|
||||
}
|
||||
|
||||
/**
|
||||
* zynq_secondary_init - Initialize secondary CPU cores
|
||||
* @cpu: CPU that is initialized
|
||||
*
|
||||
* This function is in the hotplug path. Don't move it into the
|
||||
* init section!!
|
||||
*/
|
||||
static void zynq_secondary_init(unsigned int cpu)
|
||||
{
|
||||
zynq_core_pm_init();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
static int zynq_cpu_kill(unsigned cpu)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(50);
|
||||
|
||||
while (zynq_slcr_cpu_state_read(cpu))
|
||||
if (time_after(jiffies, timeout))
|
||||
return 0;
|
||||
|
||||
zynq_slcr_cpu_stop(cpu);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* zynq_cpu_die - Let a CPU core die
|
||||
* @cpu: Dying CPU
|
||||
*
|
||||
* Platform-specific code to shutdown a CPU.
|
||||
* Called with IRQs disabled on the dying CPU.
|
||||
*/
|
||||
static void zynq_cpu_die(unsigned int cpu)
|
||||
{
|
||||
zynq_slcr_cpu_state_write(cpu, true);
|
||||
|
||||
/*
|
||||
* there is no power-control hardware on this platform, so all
|
||||
* we can do is put the core into WFI; this is safe as the calling
|
||||
* code will have already disabled interrupts
|
||||
*/
|
||||
for (;;)
|
||||
cpu_do_idle();
|
||||
}
|
||||
#endif
|
||||
|
||||
struct smp_operations zynq_smp_ops __initdata = {
|
||||
.smp_init_cpus = zynq_smp_init_cpus,
|
||||
.smp_prepare_cpus = zynq_smp_prepare_cpus,
|
||||
.smp_boot_secondary = zynq_boot_secondary,
|
||||
.smp_secondary_init = zynq_secondary_init,
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
.cpu_die = zynq_platform_cpu_die,
|
||||
.cpu_die = zynq_cpu_die,
|
||||
.cpu_kill = zynq_cpu_kill,
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Zynq power management
|
||||
*
|
||||
* Copyright (C) 2012 - 2014 Xilinx
|
||||
*
|
||||
* Sören Brinkmann <soren.brinkmann@xilinx.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include "common.h"
|
||||
|
||||
/* register offsets */
|
||||
#define DDRC_CTRL_REG1_OFFS 0x60
|
||||
#define DDRC_DRAM_PARAM_REG3_OFFS 0x20
|
||||
|
||||
/* bitfields */
|
||||
#define DDRC_CLOCKSTOP_MASK BIT(23)
|
||||
#define DDRC_SELFREFRESH_MASK BIT(12)
|
||||
|
||||
static void __iomem *ddrc_base;
|
||||
|
||||
/**
|
||||
* zynq_pm_ioremap() - Create IO mappings
|
||||
* @comp: DT compatible string
|
||||
* Return: Pointer to the mapped memory or NULL.
|
||||
*
|
||||
* Remap the memory region for a compatible DT node.
|
||||
*/
|
||||
static void __iomem *zynq_pm_ioremap(const char *comp)
|
||||
{
|
||||
struct device_node *np;
|
||||
void __iomem *base = NULL;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, comp);
|
||||
if (np) {
|
||||
base = of_iomap(np, 0);
|
||||
of_node_put(np);
|
||||
} else {
|
||||
pr_warn("%s: no compatible node found for '%s'\n", __func__,
|
||||
comp);
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
/**
|
||||
* zynq_pm_late_init() - Power management init
|
||||
*
|
||||
* Initialization of power management related featurs and infrastructure.
|
||||
*/
|
||||
void __init zynq_pm_late_init(void)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
ddrc_base = zynq_pm_ioremap("xlnx,zynq-ddrc-a05");
|
||||
if (!ddrc_base) {
|
||||
pr_warn("%s: Unable to map DDRC IO memory.\n", __func__);
|
||||
} else {
|
||||
/*
|
||||
* Enable DDRC clock stop feature. The HW takes care of
|
||||
* entering/exiting the correct mode depending
|
||||
* on activity state.
|
||||
*/
|
||||
reg = readl(ddrc_base + DDRC_DRAM_PARAM_REG3_OFFS);
|
||||
reg |= DDRC_CLOCKSTOP_MASK;
|
||||
writel(reg, ddrc_base + DDRC_DRAM_PARAM_REG3_OFFS);
|
||||
}
|
||||
}
|
|
@ -138,6 +138,8 @@ void zynq_slcr_cpu_start(int cpu)
|
|||
zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
|
||||
reg &= ~(SLCR_A9_CPU_CLKSTOP << cpu);
|
||||
zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
|
||||
|
||||
zynq_slcr_cpu_state_write(cpu, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -154,8 +156,47 @@ void zynq_slcr_cpu_stop(int cpu)
|
|||
}
|
||||
|
||||
/**
|
||||
* zynq_slcr_init - Regular slcr driver init
|
||||
* zynq_slcr_cpu_state - Read/write cpu state
|
||||
* @cpu: cpu number
|
||||
*
|
||||
* SLCR_REBOOT_STATUS save upper 2 bits (31/30 cpu states for cpu0 and cpu1)
|
||||
* 0 means cpu is running, 1 cpu is going to die.
|
||||
*
|
||||
* Return: true if cpu is running, false if cpu is going to die
|
||||
*/
|
||||
bool zynq_slcr_cpu_state_read(int cpu)
|
||||
{
|
||||
u32 state;
|
||||
|
||||
state = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
|
||||
state &= 1 << (31 - cpu);
|
||||
|
||||
return !state;
|
||||
}
|
||||
|
||||
/**
|
||||
* zynq_slcr_cpu_state - Read/write cpu state
|
||||
* @cpu: cpu number
|
||||
* @die: cpu state - true if cpu is going to die
|
||||
*
|
||||
* SLCR_REBOOT_STATUS save upper 2 bits (31/30 cpu states for cpu0 and cpu1)
|
||||
* 0 means cpu is running, 1 cpu is going to die.
|
||||
*/
|
||||
void zynq_slcr_cpu_state_write(int cpu, bool die)
|
||||
{
|
||||
u32 state, mask;
|
||||
|
||||
state = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
|
||||
mask = 1 << (31 - cpu);
|
||||
if (die)
|
||||
state |= mask;
|
||||
else
|
||||
state &= ~mask;
|
||||
writel(state, zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* zynq_slcr_init - Regular slcr driver init
|
||||
* Return: 0 on success, negative errno otherwise.
|
||||
*
|
||||
* Called early during boot from platform code to remap SLCR area.
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <asm/proc-fns.h>
|
||||
|
@ -38,15 +37,9 @@
|
|||
static int zynq_enter_idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
{
|
||||
/* Devices must be stopped here */
|
||||
cpu_pm_enter();
|
||||
|
||||
/* Add code for DDR self refresh start */
|
||||
cpu_do_idle();
|
||||
|
||||
/* Add code for DDR self refresh stop */
|
||||
cpu_pm_exit();
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
@ -59,8 +52,7 @@ static struct cpuidle_driver zynq_idle_driver = {
|
|||
.enter = zynq_enter_idle,
|
||||
.exit_latency = 10,
|
||||
.target_residency = 10000,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID |
|
||||
CPUIDLE_FLAG_TIMER_STOP,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID,
|
||||
.name = "RAM_SR",
|
||||
.desc = "WFI and RAM Self Refresh",
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue