Commit c985d7e3 authored by Arnd Bergmann's avatar Arnd Bergmann

Merge branch 'zynq/core-smp' of git://git.xilinx.com/linux-xlnx into next/soc2

From Michal Simek <michal.simek@xilinx.com>:

This branch is based on zynq/clksrc/cleanup parts because
there are some dependencies on moving timer to generic location.

I could based it on standard Linux tagged version but you will get
several conflicts you will have to resolve.
If you are OK to resolving these problems, please let me know
I will create another branch with core-smp changes which are not based
on zynq/clksrc/cleanup branch.

* 'zynq/core-smp' of git://git.xilinx.com/linux-xlnx:
  arm: zynq: Add hotplug support
  arm: zynq: Add smp support
  arm: zynq: Add smp_twd timer
  arm: zynq: Get rid of xilinx function prefix
  arm: zynq: Add support for system reset
  arm: zynq: Move slcr initialization to separate file
  arm: zynq: Load scu baseaddress at run time
Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents e382328a c7c28b0f
Cadence TTC - Triple Timer Counter
Required properties:
- compatible : Should be "cdns,ttc".
- reg : Specifies base physical address and size of the registers.
- interrupts : A list of 3 interrupts; one per timer channel.
- clocks: phandle to the source clock
Example:
ttc0: ttc0@f8001000 {
interrupt-parent = <&intc>;
interrupts = < 0 10 4 0 11 4 0 12 4 >;
compatible = "cdns,ttc";
reg = <0xF8001000 0x1000>;
clocks = <&cpu_clk 3>;
};
......@@ -1593,6 +1593,7 @@ config HAVE_ARM_ARCH_TIMER
config HAVE_ARM_TWD
bool
depends on SMP
select CLKSRC_OF if OF
help
This options enables support for the ARM timer and watchdog unit
......
......@@ -111,56 +111,30 @@ cpu_clk: cpu_clk {
};
ttc0: ttc0@f8001000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "xlnx,ttc";
interrupt-parent = <&intc>;
interrupts = < 0 10 4 0 11 4 0 12 4 >;
compatible = "cdns,ttc";
reg = <0xF8001000 0x1000>;
clocks = <&cpu_clk 3>;
clock-names = "cpu_1x";
clock-ranges;
ttc0_0: ttc0.0 {
status = "disabled";
reg = <0>;
interrupts = <0 10 4>;
};
ttc0_1: ttc0.1 {
status = "disabled";
reg = <1>;
interrupts = <0 11 4>;
};
ttc0_2: ttc0.2 {
status = "disabled";
reg = <2>;
interrupts = <0 12 4>;
};
};
ttc1: ttc1@f8002000 {
#interrupt-parent = <&intc>;
#address-cells = <1>;
#size-cells = <0>;
compatible = "xlnx,ttc";
interrupt-parent = <&intc>;
interrupts = < 0 37 4 0 38 4 0 39 4 >;
compatible = "cdns,ttc";
reg = <0xF8002000 0x1000>;
clocks = <&cpu_clk 3>;
clock-names = "cpu_1x";
clock-ranges;
ttc1_0: ttc1.0 {
status = "disabled";
reg = <0>;
interrupts = <0 37 4>;
};
ttc1_1: ttc1.1 {
status = "disabled";
reg = <1>;
interrupts = <0 38 4>;
};
ttc1_2: ttc1.2 {
status = "disabled";
reg = <2>;
interrupts = <0 39 4>;
};
};
scutimer: scutimer@f8f00600 {
interrupt-parent = <&intc>;
interrupts = < 1 13 0x301 >;
compatible = "arm,cortex-a9-twd-timer";
reg = < 0xf8f00600 0x20 >;
clocks = <&cpu_clk 1>;
} ;
};
};
......@@ -32,13 +32,3 @@ chosen {
&ps_clk {
clock-frequency = <33333330>;
};
&ttc0_0 {
status = "ok";
compatible = "xlnx,ttc-counter-clocksource";
};
&ttc0_1 {
status = "ok";
compatible = "xlnx,ttc-counter-clockevent";
};
......@@ -34,12 +34,4 @@ struct twd_local_timer name __initdata = { \
int twd_local_timer_register(struct twd_local_timer *);
#ifdef CONFIG_HAVE_ARM_TWD
void twd_local_timer_of_register(void);
#else
static inline void twd_local_timer_of_register(void)
{
}
#endif
#endif
......@@ -362,25 +362,13 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt)
}
#ifdef CONFIG_OF
const static struct of_device_id twd_of_match[] __initconst = {
{ .compatible = "arm,cortex-a9-twd-timer", },
{ .compatible = "arm,cortex-a5-twd-timer", },
{ .compatible = "arm,arm11mp-twd-timer", },
{ },
};
void __init twd_local_timer_of_register(void)
static void __init twd_local_timer_of_register(struct device_node *np)
{
struct device_node *np;
int err;
if (!is_smp() || !setup_max_cpus)
return;
np = of_find_matching_node(NULL, twd_of_match);
if (!np)
return;
twd_ppi = irq_of_parse_and_map(np, 0);
if (!twd_ppi) {
err = -EINVAL;
......@@ -398,4 +386,7 @@ void __init twd_local_timer_of_register(void)
out:
WARN(err, "twd_local_timer_of_register failed (%d)\n", err);
}
CLOCKSOURCE_OF_DECLARE(arm_twd_a9, "arm,cortex-a9-twd-timer", twd_local_timer_of_register);
CLOCKSOURCE_OF_DECLARE(arm_twd_a5, "arm,cortex-a5-twd-timer", twd_local_timer_of_register);
CLOCKSOURCE_OF_DECLARE(arm_twd_11mp, "arm,arm11mp-twd-timer", twd_local_timer_of_register);
#endif
......@@ -32,7 +32,6 @@
#include <asm/cacheflush.h>
#include <asm/cputype.h>
#include <asm/smp_plat.h>
#include <asm/smp_twd.h>
#include <asm/hardware/arm_timer.h>
#include <asm/hardware/timer-sp.h>
#include <asm/hardware/cache-l2x0.h>
......@@ -119,10 +118,10 @@ static void __init highbank_timer_init(void)
sp804_clocksource_and_sched_clock_init(timer_base + 0x20, "timer1");
sp804_clockevents_init(timer_base, irq, "timer0");
twd_local_timer_of_register();
arch_timer_of_register();
arch_timer_sched_clock_init();
clocksource_of_init();
}
static void highbank_power_off(void)
......
......@@ -12,6 +12,7 @@
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clocksource.h>
#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/export.h>
......@@ -28,11 +29,9 @@
#include <linux/regmap.h>
#include <linux/micrel_phy.h>
#include <linux/mfd/syscon.h>
#include <asm/smp_twd.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
#include <asm/system_misc.h>
#include "common.h"
......@@ -292,7 +291,7 @@ static void __init imx6q_init_irq(void)
static void __init imx6q_timer_init(void)
{
mx6q_clocks_init();
twd_local_timer_of_register();
clocksource_of_init();
imx_print_silicon_rev("i.MX6Q", imx6q_revision());
}
......
......@@ -597,7 +597,7 @@ void __init omap4_local_timer_init(void)
int err;
if (of_have_populated_dt()) {
twd_local_timer_of_register();
clocksource_of_init();
return;
}
......
......@@ -15,12 +15,12 @@
#include <linux/amba/pl022.h>
#include <linux/clk.h>
#include <linux/clocksource.h>
#include <linux/dw_dmac.h>
#include <linux/err.h>
#include <linux/of.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/mach/map.h>
#include <asm/smp_twd.h>
#include <mach/dma.h>
#include <mach/generic.h>
#include <mach/spear.h>
......@@ -179,5 +179,5 @@ void __init spear13xx_timer_init(void)
clk_put(pclk);
spear_setup_of_timer();
twd_local_timer_of_register();
clocksource_of_init();
}
......@@ -7,6 +7,7 @@
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/clksrc-dbx500-prcmu.h>
#include <linux/clocksource.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_data/clocksource-nomadik-mtu.h>
......@@ -32,7 +33,7 @@ static void __init ux500_twd_init(void)
twd_local_timer = &u8500_twd_local_timer;
if (of_have_populated_dt())
twd_local_timer_of_register();
clocksource_of_init();
else {
err = twd_local_timer_register(twd_local_timer);
if (err)
......
......@@ -5,6 +5,7 @@
#include <linux/amba/bus.h>
#include <linux/amba/mmci.h>
#include <linux/io.h>
#include <linux/clocksource.h>
#include <linux/smp.h>
#include <linux/init.h>
#include <linux/irqchip.h>
......@@ -25,7 +26,6 @@
#include <asm/arch_timer.h>
#include <asm/mach-types.h>
#include <asm/sizes.h>
#include <asm/smp_twd.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
......@@ -435,6 +435,7 @@ static void __init v2m_dt_timer_init(void)
vexpress_clk_of_init();
clocksource_of_init();
do {
node = of_find_compatible_node(node, NULL, "arm,sp804");
} while (node && vexpress_get_site_by_node(node) != VEXPRESS_SITE_MB);
......@@ -445,8 +446,7 @@ static void __init v2m_dt_timer_init(void)
irq_of_parse_and_map(node, 0));
}
if (arch_timer_of_register() != 0)
twd_local_timer_of_register();
arch_timer_of_register();
if (arch_timer_sched_clock_init() != 0)
versatile_sched_clock_init(vexpress_get_24mhz_clock_base(),
......
......@@ -8,6 +8,8 @@ config ARCH_ZYNQ
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
......@@ -3,4 +3,8 @@
#
# Common support
obj-y := common.o timer.o
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
......@@ -20,6 +20,7 @@
#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>
......@@ -32,20 +33,23 @@
#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", },
{}
};
/**
* xilinx_init_machine() - System specific initialization, intended to be
* called from board specific initialization.
* zynq_init_machine - System specific initialization, intended to be
* called from board specific initialization.
*/
static void __init xilinx_init_machine(void)
static void __init zynq_init_machine(void)
{
/*
* 64KB way size, 8-way associativity, parity disabled
......@@ -55,50 +59,56 @@ static void __init xilinx_init_machine(void)
of_platform_bus_probe(NULL, zynq_of_bus_ids, NULL);
}
#define SCU_PERIPH_PHYS 0xF8F00000
#define SCU_PERIPH_SIZE SZ_8K
#define SCU_PERIPH_VIRT (VMALLOC_END - SCU_PERIPH_SIZE)
static void __init zynq_timer_init(void)
{
zynq_slcr_init();
clocksource_of_init();
}
static struct map_desc scu_desc __initdata = {
.virtual = SCU_PERIPH_VIRT,
.pfn = __phys_to_pfn(SCU_PERIPH_PHYS),
.length = SCU_PERIPH_SIZE,
.type = MT_DEVICE,
static struct map_desc zynq_cortex_a9_scu_map __initdata = {
.length = SZ_256,
.type = MT_DEVICE,
};
static void __init xilinx_zynq_timer_init(void)
static void __init zynq_scu_map_io(void)
{
struct device_node *np;
void __iomem *slcr;
np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr");
slcr = of_iomap(np, 0);
WARN_ON(!slcr);
unsigned long base;
xilinx_zynq_clocks_init(slcr);
xttcps_timer_init();
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);
}
/**
* xilinx_map_io() - Create memory mappings needed for early I/O.
* zynq_map_io - Create memory mappings needed for early I/O.
*/
static void __init xilinx_map_io(void)
static void __init zynq_map_io(void)
{
debug_ll_io_init();
iotable_init(&scu_desc, 1);
zynq_scu_map_io();
}
static void zynq_system_reset(char mode, const char *cmd)
{
zynq_slcr_system_reset();
}
static const char *xilinx_dt_match[] = {
static const char * const zynq_dt_match[] = {
"xlnx,zynq-zc702",
"xlnx,zynq-7000",
NULL
};
MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
.map_io = xilinx_map_io,
.smp = smp_ops(zynq_smp_ops),
.map_io = zynq_map_io,
.init_irq = irqchip_init,
.init_machine = xilinx_init_machine,
.init_time = xilinx_zynq_timer_init,
.dt_compat = xilinx_dt_match,
.init_machine = zynq_init_machine,
.init_time = zynq_timer_init,
.dt_compat = zynq_dt_match,
.restart = zynq_system_reset,
MACHINE_END
......@@ -17,6 +17,24 @@
#ifndef __MACH_ZYNQ_COMMON_H__
#define __MACH_ZYNQ_COMMON_H__
void __init xttcps_timer_init(void);
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
/*
* 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)
/*
* 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);
}
/*
* 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,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_die = zynq_platform_cpu_die,
#endif
};
/*
* 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;
}
/*
* This file contains driver for the Xilinx PS Timer Counter IP.
*
* Copyright (C) 2011 Xilinx
*
* based on arch/mips/kernel/time.c timer driver
*
* 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/interrupt.h>
#include <linux/clockchips.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include "common.h"
/*
* Timer Register Offset Definitions of Timer 1, Increment base address by 4
* and use same offsets for Timer 2
*/
#define XTTCPS_CLK_CNTRL_OFFSET 0x00 /* Clock Control Reg, RW */
#define XTTCPS_CNT_CNTRL_OFFSET 0x0C /* Counter Control Reg, RW */
#define XTTCPS_COUNT_VAL_OFFSET 0x18 /* Counter Value Reg, RO */
#define XTTCPS_INTR_VAL_OFFSET 0x24 /* Interval Count Reg, RW */
#define XTTCPS_ISR_OFFSET 0x54 /* Interrupt Status Reg, RO */
#define XTTCPS_IER_OFFSET 0x60 /* Interrupt Enable Reg, RW */
#define XTTCPS_CNT_CNTRL_DISABLE_MASK 0x1
/*
* Setup the timers to use pre-scaling, using a fixed value for now that will
* work across most input frequency, but it may need to be more dynamic
*/
#define PRESCALE_EXPONENT 11 /* 2 ^ PRESCALE_EXPONENT = PRESCALE */
#define PRESCALE 2048 /* The exponent must match this */
#define CLK_CNTRL_PRESCALE ((PRESCALE_EXPONENT - 1) << 1)
#define CLK_CNTRL_PRESCALE_EN 1
#define CNT_CNTRL_RESET (1<<4)
/**
* struct xttcps_timer - This definition defines local timer structure
*
* @base_addr: Base address of timer
**/
struct xttcps_timer {
void __iomem *base_addr;
};
struct xttcps_timer_clocksource {
struct xttcps_timer xttc;
struct clocksource cs;
};
#define to_xttcps_timer_clksrc(x) \
container_of(x, struct xttcps_timer_clocksource, cs)
struct xttcps_timer_clockevent {
struct xttcps_timer xttc;
struct clock_event_device ce;
struct clk *clk;
};
#define to_xttcps_timer_clkevent(x) \
container_of(x, struct xttcps_timer_clockevent, ce)
/**
* xttcps_set_interval - Set the timer interval value
*
* @timer: Pointer to the timer instance
* @cycles: Timer interval ticks
**/
static void xttcps_set_interval(struct xttcps_timer *timer,
unsigned long cycles)
{
u32 ctrl_reg;
/* Disable the counter, set the counter value and re-enable counter */
ctrl_reg = __raw_readl(timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK;
__raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
__raw_writel(cycles, timer->base_addr + XTTCPS_INTR_VAL_OFFSET);
/*
* Reset the counter (0x10) so that it starts from 0, one-shot
* mode makes this needed for timing to be right.
*/
ctrl_reg |= CNT_CNTRL_RESET;
ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK;
__raw_writel(ctrl_reg, timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
}
/**
* xttcps_clock_event_interrupt - Clock event timer interrupt handler
*
* @irq: IRQ number of the Timer
* @dev_id: void pointer to the xttcps_timer instance
*
* returns: Always IRQ_HANDLED - success
**/
static irqreturn_t xttcps_clock_event_interrupt(int irq, void *dev_id)
{
struct xttcps_timer_clockevent *xttce = dev_id;
struct xttcps_timer *timer = &xttce->xttc;
/* Acknowledge the interrupt and call event handler */
__raw_readl(timer->base_addr + XTTCPS_ISR_OFFSET);
xttce->ce.event_handler(&xttce->ce);
return IRQ_HANDLED;
}
/**
* __xttc_clocksource_read - Reads the timer counter register
*
* returns: Current timer counter register value
**/
static cycle_t __xttc_clocksource_read(struct clocksource *cs)
{
struct xttcps_timer *timer = &to_xttcps_timer_clksrc(cs)->xttc;
return (cycle_t)__raw_readl(timer->base_addr +
XTTCPS_COUNT_VAL_OFFSET);
}
/**
* xttcps_set_next_event - Sets the time interval for next event
*
* @cycles: Timer interval ticks
* @evt: Address of clock event instance
*
* returns: Always 0 - success
**/
static int xttcps_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt);
struct xttcps_timer *timer = &xttce->xttc;
xttcps_set_interval(timer, cycles);
return 0;
}
/**
* xttcps_set_mode - Sets the mode of timer
*
* @mode: Mode to be set
* @evt: Address of clock event instance
**/
static void xttcps_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
struct xttcps_timer_clockevent *xttce = to_xttcps_timer_clkevent(evt);
struct xttcps_timer *timer = &xttce->xttc;
u32 ctrl_reg;
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
xttcps_set_interval(timer,
DIV_ROUND_CLOSEST(clk_get_rate(xttce->clk),
PRESCALE * HZ));
break;
case CLOCK_EVT_MODE_ONESHOT:
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
ctrl_reg = __raw_readl(timer->base_addr +
XTTCPS_CNT_CNTRL_OFFSET);
ctrl_reg |= XTTCPS_CNT_CNTRL_DISABLE_MASK;
__raw_writel(ctrl_reg,
timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
break;
case CLOCK_EVT_MODE_RESUME:
ctrl_reg = __raw_readl(timer->base_addr +
XTTCPS_CNT_CNTRL_OFFSET);
ctrl_reg &= ~XTTCPS_CNT_CNTRL_DISABLE_MASK;
__raw_writel(ctrl_reg,
timer->base_addr + XTTCPS_CNT_CNTRL_OFFSET);
break;
}
}
static void __init zynq_ttc_setup_clocksource(struct device_node *np,
void __iomem *base)
{
struct xttcps_timer_clocksource *ttccs;
struct clk *clk;
int err;
u32 reg;
ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL);
if (WARN_ON(!ttccs))
return;
err = of_property_read_u32(np, "reg", &reg);
if (WARN_ON(err))
return;
clk = of_clk_get_by_name(np, "cpu_1x");
if (WARN_ON(IS_ERR(clk)))
return;
err = clk_prepare_enable(clk);
if (WARN_ON(err))
return;
ttccs->xttc.base_addr = base + reg * 4;
ttccs->cs.name = np->name;
ttccs->cs.rating = 200;
ttccs->cs.read = __xttc_clocksource_read;
ttccs->cs.mask = CLOCKSOURCE_MASK(16);
ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
__raw_writel(0x0, ttccs->xttc.base_addr + XTTCPS_IER_OFFSET);
__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
ttccs->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET);
__raw_writel(CNT_CNTRL_RESET,
ttccs->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET);
err = clocksource_register_hz(&ttccs->cs, clk_get_rate(clk) / PRESCALE);
if (WARN_ON(err))
return;
}
static void __init zynq_ttc_setup_clockevent(struct device_node *np,
void __iomem *base)
{
struct xttcps_timer_clockevent *ttcce;
int err, irq;
u32 reg;
ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL);
if (WARN_ON(!ttcce))
return;
err = of_property_read_u32(np, "reg", &reg);
if (WARN_ON(err))
return;
ttcce->xttc.base_addr = base + reg * 4;
ttcce->clk = of_clk_get_by_name(np, "cpu_1x");
if (WARN_ON(IS_ERR(ttcce->clk)))
return;
err = clk_prepare_enable(ttcce->clk);
if (WARN_ON(err))
return;
irq = irq_of_parse_and_map(np, 0);
if (WARN_ON(!irq))
return;
ttcce->ce.name = np->name;
ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
ttcce->ce.set_next_event = xttcps_set_next_event;
ttcce->ce.set_mode = xttcps_set_mode;
ttcce->ce.rating = 200;
ttcce->ce.irq = irq;
ttcce->ce.cpumask = cpu_possible_mask;
__raw_writel(0x23, ttcce->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET);
__raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
ttcce->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET);
__raw_writel(0x1, ttcce->xttc.base_addr + XTTCPS_IER_OFFSET);
err = request_irq(irq, xttcps_clock_event_interrupt, IRQF_TIMER,
np->name, ttcce);
if (WARN_ON(err))
return;
clockevents_config_and_register(&ttcce->ce,
clk_get_rate(ttcce->clk) / PRESCALE,
1, 0xfffe);
}
static const __initconst struct of_device_id zynq_ttc_match[] = {
{ .compatible = "xlnx,ttc-counter-clocksource",
.data = zynq_ttc_setup_clocksource, },
{ .compatible = "xlnx,ttc-counter-clockevent",
.data = zynq_ttc_setup_clockevent, },
{}
};
/**
* xttcps_timer_init - Initialize the timer
*
* Initializes the timer hardware and register the clock source and clock event
* timers with Linux kernal timer framework
**/
void __init xttcps_timer_init(void)
{
struct device_node *np;
for_each_compatible_node(np, NULL, "xlnx,ttc") {
struct device_node *np_chld;
void __iomem *base;
base = of_iomap(np, 0);
if (WARN_ON(!base))
return;
for_each_available_child_of_node(np, np_chld) {
int (*cb)(struct device_node *np, void __iomem *base);
const struct of_device_id *match;
match = of_match_node(zynq_ttc_match, np_chld);
if (match) {
cb = match->data;
cb(np_chld, base);
}
}
}
}
......@@ -31,6 +31,9 @@ config SUNXI_TIMER
config VT8500_TIMER
bool
config CADENCE_TTC_TIMER
bool
config CLKSRC_NOMADIK_MTU
bool
depends on (ARCH_NOMADIK || ARCH_U8500)
......
......@@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o
obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o
obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o
obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o
obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
......@@ -95,23 +95,13 @@ static irqreturn_t bcm2835_time_interrupt(int irq, void *dev_id)
}
}
static struct of_device_id bcm2835_time_match[] __initconst = {
{ .compatible = "brcm,bcm2835-system-timer" },
{}
};
static void __init bcm2835_timer_init(void)
static void __init bcm2835_timer_init(struct device_node *node)
{
struct device_node *node;
void __iomem *base;
u32 freq;
int irq;
struct bcm2835_timer *timer;
node = of_find_matching_node(NULL, bcm2835_time_match);
if (!node)
panic("No bcm2835 timer node");
base = of_iomap(node, 0);
if (!base)
panic("Can't remap registers");
......
This diff is collapsed.
......@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/of.h>
#include <linux/clocksource.h>
extern struct of_device_id __clksrc_of_table[];
......@@ -26,10 +27,10 @@ void __init clocksource_of_init(void)
{
struct device_node *np;
const struct of_device_id *match;
void (*init_func)(void);
clocksource_of_init_fn init_func;
for_each_matching_node_and_match(np, __clksrc_of_table, &match) {
init_func = match->data;
init_func();
init_func(np);
}
}
......@@ -154,29 +154,12 @@ static struct irqaction tegra_timer_irq = {
.dev_id = &tegra_clockevent,
};
static const struct of_device_id timer_match[] __initconst = {
{ .compatible = "nvidia,tegra20-timer" },
{}
};
static const struct of_device_id rtc_match[] __initconst = {
{ .compatible = "nvidia,tegra20-rtc" },
{}
};
static void __init tegra20_init_timer(void)
static void __init tegra20_init_timer(struct device_node *np)
{
struct device_node *np;
struct clk *clk;
unsigned long rate;
int ret;
np = of_find_matching_node(NULL, timer_match);
if (!np) {
pr_err("Failed to find timer DT node\n");
BUG();
}
timer_reg_base = of_iomap(np, 0);
if (!timer_reg_base) {
pr_err("Can't map timer registers\n");
......@@ -200,30 +183,6 @@ static void __init tegra20_init_timer(void)
of_node_put(np);
np = of_find_matching_node(NULL, rtc_match);
if (!np) {
pr_err("Failed to find RTC DT node\n");
BUG();
}
rtc_base = of_iomap(np, 0);
if (!rtc_base) {
pr_err("Can't map RTC registers");
BUG();
}
/*
* rtc registers are used by read_persistent_clock, keep the rtc clock
* enabled
*/
clk = clk_get_sys("rtc-tegra", NULL);
if (IS_ERR(clk))
pr_warn("Unable to get rtc-tegra clock\n");
else
clk_prepare_enable(clk);
of_node_put(np);
switch (rate) {
case 12000000:
timer_writel(0x000b, TIMERUS_USEC_CFG);
......@@ -259,12 +218,34 @@ static void __init tegra20_init_timer(void)
tegra_clockevent.irq = tegra_timer_irq.irq;
clockevents_config_and_register(&tegra_clockevent, 1000000,
0x1, 0x1fffffff);
#ifdef CONFIG_HAVE_ARM_TWD
twd_local_timer_of_register();
#endif
}
CLOCKSOURCE_OF_DECLARE(tegra20_timer, "nvidia,tegra20-timer", tegra20_init_timer);
static void __init tegra20_init_rtc(struct device_node *np)
{
struct clk *clk;
rtc_base = of_iomap(np, 0);
if (!rtc_base) {
pr_err("Can't map RTC registers");
BUG();
}
/*
* rtc registers are used by read_persistent_clock, keep the rtc clock
* enabled
*/
clk = clk_get_sys("rtc-tegra", NULL);
if (IS_ERR(clk))
pr_warn("Unable to get rtc-tegra clock\n");
else
clk_prepare_enable(clk);
of_node_put(np);
register_persistent_clock(NULL, tegra_read_persistent_clock);
}
CLOCKSOURCE_OF_DECLARE(tegra20, "nvidia,tegra20-timer", tegra20_init_timer);
CLOCKSOURCE_OF_DECLARE(tegra20_rtc, "nvidia,tegra20-rtc", tegra20_init_rtc);
#ifdef CONFIG_PM
static u32 usec_config;
......
......@@ -129,22 +129,10 @@ static struct irqaction irq = {
.dev_id = &clockevent,
};
static struct of_device_id vt8500_timer_ids[] = {
{ .compatible = "via,vt8500-timer" },
{ }
};
static void __init vt8500_timer_init(void)
static void __init vt8500_timer_init(struct device_node *np)
{
struct device_node *np;
int timer_irq;
np = of_find_matching_node(NULL, vt8500_timer_ids);
if (!np) {
pr_err("%s: Timer description missing from Device Tree\n",
__func__);
return;
}
regbase = of_iomap(np, 0);
if (!regbase) {
pr_err("%s: Missing iobase description in Device Tree\n",
......@@ -177,4 +165,4 @@ static void __init vt8500_timer_init(void)
4, 0xf0000000);
}
CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init)
CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init);
......@@ -332,15 +332,23 @@ extern int clocksource_mmio_init(void __iomem *, const char *,
extern int clocksource_i8253_init(void);
struct device_node;
typedef void(*clocksource_of_init_fn)(struct device_node *);
#ifdef CONFIG_CLKSRC_OF
extern void clocksource_of_init(void);
#define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \
static const struct of_device_id __clksrc_of_table_##name \
__used __section(__clksrc_of_table) \
= { .compatible = compat, .data = fn };
= { .compatible = compat, \
.data = (fn == (clocksource_of_init_fn)NULL) ? fn : fn }
#else
#define CLOCKSOURCE_OF_DECLARE(name, compat, fn)
static inline void clocksource_of_init(void) {}
#define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \
static const struct of_device_id __clksrc_of_table_##name \
__attribute__((unused)) \
= { .compatible = compat, \
.data = (fn == (clocksource_of_init_fn)NULL) ? fn : fn }
#endif
#endif /* _LINUX_CLOCKSOURCE_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment