diff options
| author | Meizu OpenSource <patchwork@meizu.com> | 2016-08-15 10:19:42 +0800 |
|---|---|---|
| committer | Meizu OpenSource <patchwork@meizu.com> | 2016-08-15 10:19:42 +0800 |
| commit | d2e1446d81725c351dc73a03b397ce043fb18452 (patch) | |
| tree | 4dbc616b7f92aea39cd697a9084205ddb805e344 /arch/arm/mach-zynq | |
first commit
Diffstat (limited to 'arch/arm/mach-zynq')
| -rw-r--r-- | arch/arm/mach-zynq/Kconfig | 17 | ||||
| -rw-r--r-- | arch/arm/mach-zynq/Makefile | 10 | ||||
| -rw-r--r-- | arch/arm/mach-zynq/Makefile.boot | 3 | ||||
| -rw-r--r-- | arch/arm/mach-zynq/common.c | 114 | ||||
| -rw-r--r-- | arch/arm/mach-zynq/common.h | 40 | ||||
| -rw-r--r-- | arch/arm/mach-zynq/headsmp.S | 24 | ||||
| -rw-r--r-- | arch/arm/mach-zynq/hotplug.c | 104 | ||||
| -rw-r--r-- | arch/arm/mach-zynq/platsmp.c | 136 | ||||
| -rw-r--r-- | arch/arm/mach-zynq/slcr.c | 125 |
9 files changed, 573 insertions, 0 deletions
diff --git a/arch/arm/mach-zynq/Kconfig b/arch/arm/mach-zynq/Kconfig new file mode 100644 index 000000000..c1d61f281 --- /dev/null +++ b/arch/arm/mach-zynq/Kconfig @@ -0,0 +1,17 @@ +config ARCH_ZYNQ + bool "Xilinx Zynq ARM Cortex A9 Platform" if ARCH_MULTI_V7 + select ARM_AMBA + select ARM_GIC + select COMMON_CLK + select CPU_V7 + select GENERIC_CLOCKEVENTS + select HAVE_ARM_SCU if SMP + select HAVE_ARM_TWD if LOCAL_TIMERS + select ICST + select MIGHT_HAVE_CACHE_L2X0 + select USE_OF + select HAVE_SMP + select SPARSE_IRQ + select CADENCE_TTC_TIMER + help + Support for Xilinx Zynq ARM Cortex A9 Platform diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile new file mode 100644 index 000000000..1b25d92eb --- /dev/null +++ b/arch/arm/mach-zynq/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the linux kernel. +# + +# Common support +obj-y := common.o slcr.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 diff --git a/arch/arm/mach-zynq/Makefile.boot b/arch/arm/mach-zynq/Makefile.boot new file mode 100644 index 000000000..760a0efe7 --- /dev/null +++ b/arch/arm/mach-zynq/Makefile.boot @@ -0,0 +1,3 @@ + zreladdr-y += 0x00008000 +params_phys-y := 0x00000100 +initrd_phys-y := 0x00800000 diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c new file mode 100644 index 000000000..5bfe7035b --- /dev/null +++ b/arch/arm/mach-zynq/common.c @@ -0,0 +1,114 @@ +/* + * This file contains common code that is intended to be used across + * boards so that it's not replicated. + * + * Copyright (C) 2011 Xilinx + * + * 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/init.h> +#include <linux/kernel.h> +#include <linux/cpumask.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/clk/zynq.h> +#include <linux/clocksource.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/of.h> +#include <linux/irqchip.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/time.h> +#include <asm/mach-types.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/smp_scu.h> +#include <asm/hardware/cache-l2x0.h> + +#include "common.h" + +void __iomem *zynq_scu_base; + +static struct of_device_id zynq_of_bus_ids[] __initdata = { + { .compatible = "simple-bus", }, + {} +}; + +/** + * zynq_init_machine - System specific initialization, intended to be + * called from board specific initialization. + */ +static void __init zynq_init_machine(void) +{ + /* + * 64KB way size, 8-way associativity, parity disabled + */ + l2x0_of_init(0x02060000, 0xF0F0FFFF); + + of_platform_bus_probe(NULL, zynq_of_bus_ids, NULL); +} + +static void __init zynq_timer_init(void) +{ + zynq_slcr_init(); + clocksource_of_init(); +} + +static struct map_desc zynq_cortex_a9_scu_map __initdata = { + .length = SZ_256, + .type = MT_DEVICE, +}; + +static void __init zynq_scu_map_io(void) +{ + unsigned long base; + + base = scu_a9_get_base(); + zynq_cortex_a9_scu_map.pfn = __phys_to_pfn(base); + /* Expected address is in vmalloc area that's why simple assign here */ + zynq_cortex_a9_scu_map.virtual = base; + iotable_init(&zynq_cortex_a9_scu_map, 1); + zynq_scu_base = (void __iomem *)base; + BUG_ON(!zynq_scu_base); +} + +/** + * zynq_map_io - Create memory mappings needed for early I/O. + */ +static void __init zynq_map_io(void) +{ + debug_ll_io_init(); + zynq_scu_map_io(); +} + +static void zynq_system_reset(char mode, const char *cmd) +{ + zynq_slcr_system_reset(); +} + +static const char * const zynq_dt_match[] = { + "xlnx,zynq-zc702", + "xlnx,zynq-7000", + NULL +}; + +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, + .init_time = zynq_timer_init, + .dt_compat = zynq_dt_match, + .restart = zynq_system_reset, +MACHINE_END diff --git a/arch/arm/mach-zynq/common.h b/arch/arm/mach-zynq/common.h new file mode 100644 index 000000000..fbbd0e21c --- /dev/null +++ b/arch/arm/mach-zynq/common.h @@ -0,0 +1,40 @@ +/* + * This file contains common function prototypes to avoid externs + * in the c files. + * + * Copyright (C) 2011 Xilinx + * + * 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. + */ + +#ifndef __MACH_ZYNQ_COMMON_H__ +#define __MACH_ZYNQ_COMMON_H__ + +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; + +/* Hotplug */ +extern void zynq_platform_cpu_die(unsigned int cpu); + +#endif diff --git a/arch/arm/mach-zynq/headsmp.S b/arch/arm/mach-zynq/headsmp.S new file mode 100644 index 000000000..d183cd234 --- /dev/null +++ b/arch/arm/mach-zynq/headsmp.S @@ -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) diff --git a/arch/arm/mach-zynq/hotplug.c b/arch/arm/mach-zynq/hotplug.c new file mode 100644 index 000000000..c89672bd1 --- /dev/null +++ b/arch/arm/mach-zynq/hotplug.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2012-2013 Xilinx + * + * based on linux/arch/arm/mach-realview/hotplug.c + * + * Copyright (C) 2002 ARM Ltd. + * All Rights Reserved + * + * 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/kernel.h> +#include <linux/errno.h> +#include <linux/smp.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"); +} + +static inline void zynq_cpu_leave_lowpower(void) +{ + unsigned int v; + + asm volatile( + " mrc p15, 0, %0, c1, c0, 0\n" + " orr %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 0\n" + " mrc p15, 0, %0, c1, c0, 1\n" + " orr %0, %0, #0x40\n" + " mcr p15, 0, %0, c1, c0, 1\n" + : "=&r" (v) + : "Ir" (CR_C) + : "cc"); +} + +static inline void zynq_platform_do_lowpower(unsigned int cpu, int *spurious) +{ + /* + * 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 (;;) { + dsb(); + wfi(); + + /* + * Getting here, means that we have come out of WFI without + * having been woken up - this shouldn't happen + * + * Just note it happening - when we're woken, we can report + * its occurrence. + */ + (*spurious)++; + } +} + +/* + * platform-specific code to shutdown a CPU + * + * Called with IRQs disabled + */ +void zynq_platform_cpu_die(unsigned int cpu) +{ + int spurious = 0; + + /* + * we're ready for shutdown now, so do it + */ + zynq_cpu_enter_lowpower(); + zynq_platform_do_lowpower(cpu, &spurious); + + /* + * bring this CPU back into the world of cache + * coherency, and then restore interrupts + */ + zynq_cpu_leave_lowpower(); + + if (spurious) + pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious); +} diff --git a/arch/arm/mach-zynq/platsmp.c b/arch/arm/mach-zynq/platsmp.c new file mode 100644 index 000000000..5fc167e07 --- /dev/null +++ b/arch/arm/mach-zynq/platsmp.c @@ -0,0 +1,136 @@ +/* + * 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; + +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_boot_secondary = zynq_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_die = zynq_platform_cpu_die, +#endif +}; diff --git a/arch/arm/mach-zynq/slcr.c b/arch/arm/mach-zynq/slcr.c new file mode 100644 index 000000000..c70969b9c --- /dev/null +++ b/arch/arm/mach-zynq/slcr.c @@ -0,0 +1,125 @@ +/* + * Xilinx SLCR driver + * + * Copyright (c) 2011-2013 Xilinx Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA + * 02139, USA. + */ + +#include <linux/export.h> +#include <linux/io.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/uaccess.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/clk/zynq.h> +#include "common.h" + +#define SLCR_UNLOCK_MAGIC 0xDF0D +#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; + +/** + * zynq_slcr_system_reset - Reset the entire system. + */ +void zynq_slcr_system_reset(void) +{ + u32 reboot; + + /* + * Unlock the SLCR then reset the system. + * Note that this seems to require raw i/o + * functions or there's a lockup? + */ + writel(SLCR_UNLOCK_MAGIC, zynq_slcr_base + SLCR_UNLOCK); + + /* + * Clear 0x0F000000 bits of reboot status register to workaround + * the FSBL not loading the bitstream after soft-reboot + * This is a temporary solution until we know more. + */ + reboot = readl(zynq_slcr_base + SLCR_REBOOT_STATUS); + writel(reboot & 0xF0FFFFFF, zynq_slcr_base + SLCR_REBOOT_STATUS); + 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. + * + * Called early during boot from platform code to remap SLCR area. + */ +int __init zynq_slcr_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr"); + if (!np) { + pr_err("%s: no slcr node found\n", __func__); + BUG(); + } + + zynq_slcr_base = of_iomap(np, 0); + if (!zynq_slcr_base) { + pr_err("%s: Unable to map I/O memory\n", __func__); + BUG(); + } + + /* unlock the SLCR so that registers can be changed */ + writel(SLCR_UNLOCK_MAGIC, zynq_slcr_base + SLCR_UNLOCK); + + pr_info("%s mapped to %p\n", np->name, zynq_slcr_base); + + xilinx_zynq_clocks_init(zynq_slcr_base); + + of_node_put(np); + + return 0; +} |
