Skip to content

Commit

Permalink
arm: zynq: Add smp support
Browse files Browse the repository at this point in the history
Zynq is dual core Cortex A9 which starts always
at zero. Using simple trampoline ensure long jump
to secondary_startup code.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
  • Loading branch information
Michal Simek committed Apr 4, 2013
1 parent 2f34e0a commit aa7eb2b
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 0 deletions.
1 change: 1 addition & 0 deletions arch/arm/mach-zynq/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ config ARCH_ZYNQ
select ICST
select MIGHT_HAVE_CACHE_L2X0
select USE_OF
select HAVE_SMP
select SPARSE_IRQ
select CADENCE_TTC_TIMER
help
Expand Down
1 change: 1 addition & 0 deletions arch/arm/mach-zynq/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@

# Common support
obj-y := common.o slcr.o
obj-$(CONFIG_SMP) += headsmp.o platsmp.o
1 change: 1 addition & 0 deletions arch/arm/mach-zynq/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ static const char * const zynq_dt_match[] = {
};

MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
.smp = smp_ops(zynq_smp_ops),
.map_io = zynq_map_io,
.init_irq = irqchip_init,
.init_machine = zynq_init_machine,
Expand Down
11 changes: 11 additions & 0 deletions arch/arm/mach-zynq/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@

extern int zynq_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);

#ifdef CONFIG_SMP
extern void secondary_startup(void);
extern char zynq_secondary_trampoline;
extern char zynq_secondary_trampoline_jump;
extern char zynq_secondary_trampoline_end;
extern int __cpuinit zynq_cpun_start(u32 address, int cpu);
extern struct smp_operations zynq_smp_ops __initdata;
#endif

extern void __iomem *zynq_slcr_base;
extern void __iomem *zynq_scu_base;
Expand Down
24 changes: 24 additions & 0 deletions arch/arm/mach-zynq/headsmp.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2013 Steffen Trumtrar <s.trumtrar@pengutronix.de>
* Copyright (c) 2012-2013 Xilinx
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/linkage.h>
#include <linux/init.h>

__CPUINIT

ENTRY(zynq_secondary_trampoline)
ldr r0, [pc]
bx r0
.globl zynq_secondary_trampoline_jump
zynq_secondary_trampoline_jump:
/* Space for jumping address */
.word /* cpu 1 */
.globl zynq_secondary_trampoline_end
zynq_secondary_trampoline_end:

ENDPROC(zynq_secondary_trampoline)
149 changes: 149 additions & 0 deletions arch/arm/mach-zynq/platsmp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* This file contains Xilinx specific SMP code, used to start up
* the second processor.
*
* Copyright (C) 2011-2013 Xilinx
*
* based on linux/arch/arm/mach-realview/platsmp.c
*
* Copyright (C) 2002 ARM Ltd.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*/

#include <linux/export.h>
#include <linux/jiffies.h>
#include <linux/init.h>
#include <linux/io.h>
#include <asm/cacheflush.h>
#include <asm/smp_scu.h>
#include <linux/irqchip/arm-gic.h>
#include "common.h"

/*
* Store number of cores in the system
* Because of scu_get_core_count() must be in __init section and can't
* be called from zynq_cpun_start() because it is in __cpuinit section.
*/
static int ncores;

/* Secondary CPU kernel startup is a 2 step process. The primary CPU
* starts the secondary CPU by giving it the address of the kernel and
* then sending it an event to wake it up. The secondary CPU then
* starts the kernel and tells the primary CPU it's up and running.
*/
static void __cpuinit zynq_secondary_init(unsigned int cpu)
{
/*
* if any interrupts are already enabled for the primary
* core (e.g. timer irq), then they will not have been enabled
* for us: do so
*/
gic_secondary_init(0);
}

int __cpuinit zynq_cpun_start(u32 address, int cpu)
{
u32 trampoline_code_size = &zynq_secondary_trampoline_end -
&zynq_secondary_trampoline;

if (cpu > ncores) {
pr_warn("CPU No. is not available in the system\n");
return -1;
}

/* MS: Expectation that SLCR are directly map and accessible */
/* Not possible to jump to non aligned address */
if (!(address & 3) && (!address || (address >= trampoline_code_size))) {
/* Store pointer to ioremap area which points to address 0x0 */
static u8 __iomem *zero;
u32 trampoline_size = &zynq_secondary_trampoline_jump -
&zynq_secondary_trampoline;

zynq_slcr_cpu_stop(cpu);

if (__pa(PAGE_OFFSET)) {
zero = ioremap(0, trampoline_code_size);
if (!zero) {
pr_warn("BOOTUP jump vectors not accessible\n");
return -1;
}
} else {
zero = (__force u8 __iomem *)PAGE_OFFSET;
}

/*
* This is elegant way how to jump to any address
* 0x0: Load address at 0x8 to r0
* 0x4: Jump by mov instruction
* 0x8: Jumping address
*/
memcpy((__force void *)zero, &zynq_secondary_trampoline,
trampoline_size);
writel(address, zero + trampoline_size);

flush_cache_all();
outer_flush_range(0, trampoline_code_size);
smp_wmb();

if (__pa(PAGE_OFFSET))
iounmap(zero);

zynq_slcr_cpu_start(cpu);

return 0;
}

pr_warn("Can't start CPU%d: Wrong starting address %x\n", cpu, address);

return -1;
}
EXPORT_SYMBOL(zynq_cpun_start);

static int __cpuinit zynq_boot_secondary(unsigned int cpu,
struct task_struct *idle)
{
return zynq_cpun_start(virt_to_phys(secondary_startup), cpu);
}

/*
* Initialise the CPU possible map early - this describes the CPUs
* which may be present or become present in the system.
*/
static void __init zynq_smp_init_cpus(void)
{
int i;

ncores = scu_get_core_count(zynq_scu_base);

for (i = 0; i < ncores && i < CONFIG_NR_CPUS; i++)
set_cpu_possible(i, true);
}

static void __init zynq_smp_prepare_cpus(unsigned int max_cpus)
{
int i;

/*
* Initialise the present map, which describes the set of CPUs
* actually populated at the present time.
*/
for (i = 0; i < max_cpus; i++)
set_cpu_present(i, true);

scu_enable(zynq_scu_base);
}

struct smp_operations zynq_smp_ops __initdata = {
.smp_init_cpus = zynq_smp_init_cpus,
.smp_prepare_cpus = zynq_smp_prepare_cpus,
.smp_secondary_init = zynq_secondary_init,
.smp_boot_secondary = zynq_boot_secondary,
};
29 changes: 29 additions & 0 deletions arch/arm/mach-zynq/slcr.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
#define SLCR_UNLOCK 0x8 /* SCLR unlock register */

#define SLCR_PS_RST_CTRL_OFFSET 0x200 /* PS Software Reset Control */

#define SLCR_A9_CPU_CLKSTOP 0x10
#define SLCR_A9_CPU_RST 0x1

#define SLCR_A9_CPU_RST_CTRL 0x244 /* CPU Software Reset Control */
#define SLCR_REBOOT_STATUS 0x258 /* PS Reboot Status */

void __iomem *zynq_slcr_base;
Expand Down Expand Up @@ -61,6 +66,30 @@ void zynq_slcr_system_reset(void)
writel(1, zynq_slcr_base + SLCR_PS_RST_CTRL_OFFSET);
}

/**
* zynq_slcr_cpu_start - Start cpu
* @cpu: cpu number
*/
void zynq_slcr_cpu_start(int cpu)
{
/* enable CPUn */
writel(SLCR_A9_CPU_CLKSTOP << cpu,
zynq_slcr_base + SLCR_A9_CPU_RST_CTRL);
/* enable CLK for CPUn */
writel(0x0 << cpu, zynq_slcr_base + SLCR_A9_CPU_RST_CTRL);
}

/**
* zynq_slcr_cpu_stop - Stop cpu
* @cpu: cpu number
*/
void zynq_slcr_cpu_stop(int cpu)
{
/* stop CLK and reset CPUn */
writel((SLCR_A9_CPU_CLKSTOP | SLCR_A9_CPU_RST) << cpu,
zynq_slcr_base + SLCR_A9_CPU_RST_CTRL);
}

/**
* zynq_slcr_init
* Returns 0 on success, negative errno otherwise.
Expand Down

0 comments on commit aa7eb2b

Please sign in to comment.