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:
Arnd Bergmann 2014-09-25 17:42:57 +02:00
commit e36087998a
10 changed files with 207 additions and 63 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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();
}

View File

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

83
arch/arm/mach-zynq/pm.c Normal file
View File

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

View File

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

View File

@ -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",
},