Commit 648ab3df authored by Arnd Bergmann's avatar Arnd Bergmann

Merge branch 'local_timers-for-arm-soc' of...

Merge branch 'local_timers-for-arm-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into next/timer

* 'local_timers-for-arm-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms:
  ARM: local timers: make the runtime registration interface mandatory
  ARM: local timers: convert MSM to runtime registration interface
  ARM: local timers: convert exynos to runtime registration interface
  ARM: smp_twd: remove old local timer interface
  ARM: imx6q: convert to twd_local_timer_register() interface
  ARM: highbank: convert to twd_local_timer_register() interface
  ARM: ux500: convert to twd_local_timer_register() interface
  ARM: shmobile: convert to twd_local_timer_register() interface
  ARM: tegra: convert to twd_local_timer_register() interface
  ARM: plat-versatile: convert to twd_local_timer_register() interface
  ARM: OMAP4: convert to twd_local_timer_register() interface
  ARM: smp_twd: add device tree support
  ARM: smp_twd: add runtime registration support
  ARM: local timers: introduce a new registration interface
  ARM: smp_twd: make local_timer_stop a symbol instead of a #define
parents a2821146 d4578592
* ARM Timer Watchdog
ARM 11MP, Cortex-A5 and Cortex-A9 are often associated with a per-core
Timer-Watchdog (aka TWD), which provides both a per-cpu local timer
and watchdog.
The TWD is usually attached to a GIC to deliver its two per-processor
interrupts.
** Timer node required properties:
- compatible : Should be one of:
"arm,cortex-a9-twd-timer"
"arm,cortex-a5-twd-timer"
"arm,arm11mp-twd-timer"
- interrupts : One interrupt to each core
- reg : Specify the base address and the size of the TWD timer
register window.
Example:
twd-timer@2c000600 {
compatible = "arm,arm11mp-twd-timer"";
reg = <0x2c000600 0x20>;
interrupts = <1 13 0xf01>;
};
** Watchdog node properties:
- compatible : Should be one of:
"arm,cortex-a9-twd-wdt"
"arm,cortex-a5-twd-wdt"
"arm,arm11mp-twd-wdt"
- interrupts : One interrupt to each core
- reg : Specify the base address and the size of the TWD watchdog
register window.
Example:
twd-watchdog@2c000620 {
compatible = "arm,arm11mp-twd-wdt";
reg = <0x2c000620 0x20>;
interrupts = <1 14 0xf01>;
};
......@@ -72,15 +72,15 @@ soc {
ranges;
timer@fff10600 {
compatible = "arm,smp-twd";
compatible = "arm,cortex-a9-twd-timer";
reg = <0xfff10600 0x20>;
interrupts = <1 13 0xf04>;
interrupts = <1 13 0xf01>;
};
watchdog@fff10620 {
compatible = "arm,cortex-a9-wdt";
compatible = "arm,cortex-a9-twd-wdt";
reg = <0xfff10620 0x20>;
interrupts = <1 14 0xf04>;
interrupts = <1 14 0xf01>;
};
intc: interrupt-controller@fff11000 {
......
......@@ -88,9 +88,9 @@ soc {
ranges;
timer@00a00600 {
compatible = "arm,smp-twd";
reg = <0x00a00600 0x100>;
interrupts = <1 13 0xf4>;
compatible = "arm,cortex-a9-twd-timer";
reg = <0x00a00600 0x20>;
interrupts = <1 13 0xf01>;
};
L2: l2-cache@00a02000 {
......
......@@ -11,47 +11,24 @@
#define __ASM_ARM_LOCALTIMER_H
#include <linux/errno.h>
#include <linux/interrupt.h>
struct clock_event_device;
/*
* Setup a per-cpu timer, whether it be a local timer or dummy broadcast
*/
void percpu_timer_setup(void);
struct local_timer_ops {
int (*setup)(struct clock_event_device *);
void (*stop)(struct clock_event_device *);
};
#ifdef CONFIG_LOCAL_TIMERS
#ifdef CONFIG_HAVE_ARM_TWD
#include "smp_twd.h"
#define local_timer_stop(c) twd_timer_stop((c))
#else
/*
* Stop the local timer
*/
void local_timer_stop(struct clock_event_device *);
#endif
/*
* Setup a local timer interrupt for a CPU.
* Register a local timer driver
*/
int local_timer_setup(struct clock_event_device *);
int local_timer_register(struct local_timer_ops *);
#else
static inline int local_timer_setup(struct clock_event_device *evt)
static inline int local_timer_register(struct local_timer_ops *ops)
{
return -ENXIO;
}
static inline void local_timer_stop(struct clock_event_device *evt)
{
}
#endif
#endif
......@@ -18,11 +18,28 @@
#define TWD_TIMER_CONTROL_PERIODIC (1 << 1)
#define TWD_TIMER_CONTROL_IT_ENABLE (1 << 2)
struct clock_event_device;
#include <linux/ioport.h>
extern void __iomem *twd_base;
struct twd_local_timer {
struct resource res[2];
};
void twd_timer_setup(struct clock_event_device *);
void twd_timer_stop(struct clock_event_device *);
#define DEFINE_TWD_LOCAL_TIMER(name,base,irq) \
struct twd_local_timer name __initdata = { \
.res = { \
DEFINE_RES_MEM(base, 0x10), \
DEFINE_RES_IRQ(irq), \
}, \
};
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
......@@ -246,6 +246,8 @@ static void __cpuinit smp_store_cpu_info(unsigned int cpuid)
store_cpu_topology(cpuid);
}
static void percpu_timer_setup(void);
/*
* This is the secondary CPU boot entry. We're using this CPUs
* idle thread stack, but a set of temporary page tables.
......@@ -459,7 +461,20 @@ static void __cpuinit broadcast_timer_setup(struct clock_event_device *evt)
clockevents_register_device(evt);
}
void __cpuinit percpu_timer_setup(void)
static struct local_timer_ops *lt_ops;
#ifdef CONFIG_LOCAL_TIMERS
int local_timer_register(struct local_timer_ops *ops)
{
if (lt_ops)
return -EBUSY;
lt_ops = ops;
return 0;
}
#endif
static void __cpuinit percpu_timer_setup(void)
{
unsigned int cpu = smp_processor_id();
struct clock_event_device *evt = &per_cpu(percpu_clockevent, cpu);
......@@ -467,7 +482,7 @@ void __cpuinit percpu_timer_setup(void)
evt->cpumask = cpumask_of(cpu);
evt->broadcast = smp_timer_broadcast;
if (local_timer_setup(evt))
if (!lt_ops || lt_ops->setup(evt))
broadcast_timer_setup(evt);
}
......@@ -482,7 +497,8 @@ static void percpu_timer_stop(void)
unsigned int cpu = smp_processor_id();
struct clock_event_device *evt = &per_cpu(percpu_clockevent, cpu);
local_timer_stop(evt);
if (lt_ops)
lt_ops->stop(evt);
}
#endif
......
......@@ -18,20 +18,23 @@
#include <linux/smp.h>
#include <linux/jiffies.h>
#include <linux/clockchips.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <asm/smp_twd.h>
#include <asm/localtimer.h>
#include <asm/hardware/gic.h>
/* set up by the platform code */
void __iomem *twd_base;
static void __iomem *twd_base;
static struct clk *twd_clk;
static unsigned long twd_timer_rate;
static struct clock_event_device __percpu **twd_evt;
static int twd_ppi;
static void twd_set_mode(enum clock_event_mode mode,
struct clock_event_device *clk)
......@@ -77,7 +80,7 @@ static int twd_set_next_event(unsigned long evt,
* If a local timer interrupt has occurred, acknowledge and return 1.
* Otherwise, return 0.
*/
int twd_timer_ack(void)
static int twd_timer_ack(void)
{
if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) {
__raw_writel(1, twd_base + TWD_TIMER_INTSTAT);
......@@ -87,7 +90,7 @@ int twd_timer_ack(void)
return 0;
}
void twd_timer_stop(struct clock_event_device *clk)
static void twd_timer_stop(struct clock_event_device *clk)
{
twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
disable_percpu_irq(clk->irq);
......@@ -222,28 +225,10 @@ static struct clk *twd_get_clock(void)
/*
* Setup the local clock events for a CPU.
*/
void __cpuinit twd_timer_setup(struct clock_event_device *clk)
static int __cpuinit twd_timer_setup(struct clock_event_device *clk)
{
struct clock_event_device **this_cpu_clk;
if (!twd_evt) {
int err;
twd_evt = alloc_percpu(struct clock_event_device *);
if (!twd_evt) {
pr_err("twd: can't allocate memory\n");
return;
}
err = request_percpu_irq(clk->irq, twd_handler,
"twd", twd_evt);
if (err) {
pr_err("twd: can't register interrupt %d (%d)\n",
clk->irq, err);
return;
}
}
if (!twd_clk)
twd_clk = twd_get_clock();
......@@ -260,6 +245,7 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk)
clk->rating = 350;
clk->set_mode = twd_set_mode;
clk->set_next_event = twd_set_next_event;
clk->irq = twd_ppi;
this_cpu_clk = __this_cpu_ptr(twd_evt);
*this_cpu_clk = clk;
......@@ -267,4 +253,95 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk)
clockevents_config_and_register(clk, twd_timer_rate,
0xf, 0xffffffff);
enable_percpu_irq(clk->irq, 0);
return 0;
}
static struct local_timer_ops twd_lt_ops __cpuinitdata = {
.setup = twd_timer_setup,
.stop = twd_timer_stop,
};
static int __init twd_local_timer_common_register(void)
{
int err;
twd_evt = alloc_percpu(struct clock_event_device *);
if (!twd_evt) {
err = -ENOMEM;
goto out_free;
}
err = request_percpu_irq(twd_ppi, twd_handler, "twd", twd_evt);
if (err) {
pr_err("twd: can't register interrupt %d (%d)\n", twd_ppi, err);
goto out_free;
}
err = local_timer_register(&twd_lt_ops);
if (err)
goto out_irq;
return 0;
out_irq:
free_percpu_irq(twd_ppi, twd_evt);
out_free:
iounmap(twd_base);
twd_base = NULL;
free_percpu(twd_evt);
return err;
}
int __init twd_local_timer_register(struct twd_local_timer *tlt)
{
if (twd_base || twd_evt)
return -EBUSY;
twd_ppi = tlt->res[1].start;
twd_base = ioremap(tlt->res[0].start, resource_size(&tlt->res[0]));
if (!twd_base)
return -ENOMEM;
return twd_local_timer_common_register();
}
#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)
{
struct device_node *np;
int err;
np = of_find_matching_node(NULL, twd_of_match);
if (!np) {
err = -ENODEV;
goto out;
}
twd_ppi = irq_of_parse_and_map(np, 0);
if (!twd_ppi) {
err = -EINVAL;
goto out;
}
twd_base = of_iomap(np, 0);
if (!twd_base) {
err = -ENOMEM;
goto out;
}
err = twd_local_timer_common_register();
out:
WARN(err, "twd_local_timer_of_register failed (%d)\n", err);
}
#endif
......@@ -21,6 +21,7 @@
#include <linux/percpu.h>
#include <asm/hardware/gic.h>
#include <asm/localtimer.h>
#include <plat/cpu.h>
......@@ -375,7 +376,7 @@ static struct irqaction mct_tick1_event_irq = {
.handler = exynos4_mct_tick_isr,
};
static void exynos4_mct_tick_init(struct clock_event_device *evt)
static int __cpuinit exynos4_local_timer_setup(struct clock_event_device *evt)
{
struct mct_clock_event_device *mevt;
unsigned int cpu = smp_processor_id();
......@@ -417,17 +418,11 @@ static void exynos4_mct_tick_init(struct clock_event_device *evt)
} else {
enable_percpu_irq(IRQ_MCT_LOCALTIMER, 0);
}
}
/* Setup the local clock events for a CPU */
int __cpuinit local_timer_setup(struct clock_event_device *evt)
{
exynos4_mct_tick_init(evt);
return 0;
}
void local_timer_stop(struct clock_event_device *evt)
static void exynos4_local_timer_stop(struct clock_event_device *evt)
{
unsigned int cpu = smp_processor_id();
evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
......@@ -439,6 +434,11 @@ void local_timer_stop(struct clock_event_device *evt)
else
disable_percpu_irq(IRQ_MCT_LOCALTIMER);
}
static struct local_timer_ops exynos4_mct_tick_ops __cpuinitdata = {
.setup = exynos4_local_timer_setup,
.stop = exynos4_local_timer_stop,
};
#endif /* CONFIG_LOCAL_TIMERS */
static void __init exynos4_timer_resources(void)
......@@ -458,6 +458,8 @@ static void __init exynos4_timer_resources(void)
WARN(err, "MCT: can't request IRQ %d (%d)\n",
IRQ_MCT_LOCALTIMER, err);
}
local_timer_register(&exynos4_mct_tick_ops);
#endif /* CONFIG_LOCAL_TIMERS */
}
......
obj-y := clock.o highbank.o system.o
obj-$(CONFIG_DEBUG_HIGHBANK_UART) += lluart.o
obj-$(CONFIG_SMP) += platsmp.o
obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_PM_SLEEP) += pm.o
......@@ -27,6 +27,7 @@
#include <asm/cacheflush.h>
#include <asm/smp_plat.h>
#include <asm/smp_scu.h>
#include <asm/smp_twd.h>
#include <asm/hardware/arm_timer.h>
#include <asm/hardware/timer-sp.h>
#include <asm/hardware/gic.h>
......@@ -111,6 +112,8 @@ static void __init highbank_timer_init(void)
sp804_clocksource_init(timer_base + 0x20, "timer1");
sp804_clockevents_init(timer_base, irq, "timer0");
twd_local_timer_of_register();
}
static struct sys_timer highbank_timer = {
......
/*
* Copyright 2010-2011 Calxeda, Inc.
* Based on localtimer.c, Copyright (C) 2002 ARM Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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/init.h>
#include <linux/clockchips.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <asm/smp_twd.h>
/*
* Setup the local clock events for a CPU.
*/
int __cpuinit local_timer_setup(struct clock_event_device *evt)
{
struct device_node *np;
np = of_find_compatible_node(NULL, NULL, "arm,smp-twd");
if (!twd_base) {
twd_base = of_iomap(np, 0);
WARN_ON(!twd_base);
}
evt->irq = irq_of_parse_and_map(np, 0);
twd_timer_setup(evt);
return 0;
}
......@@ -71,7 +71,6 @@ obj-$(CONFIG_CPU_V7) += head-v7.o
AFLAGS_head-v7.o :=-Wa,-march=armv7-a
obj-$(CONFIG_SMP) += platsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o
obj-$(CONFIG_SOC_IMX6Q) += clock-imx6q.o mach-imx6q.o
ifeq ($(CONFIG_PM),y)
......
/*
* Copyright 2011 Freescale Semiconductor, Inc.
* Copyright 2011 Linaro Ltd.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/init.h>
#include <linux/clockchips.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <asm/smp_twd.h>
/*
* Setup the local clock events for a CPU.
*/
int __cpuinit local_timer_setup(struct clock_event_device *evt)
{
struct device_node *np;
np = of_find_compatible_node(NULL, NULL, "arm,smp-twd");
if (!twd_base) {
twd_base = of_iomap(np, 0);
WARN_ON(!twd_base);
}
evt->irq = irq_of_parse_and_map(np, 0);
twd_timer_setup(evt);
return 0;
}
......@@ -21,6 +21,7 @@
#include <linux/of_platform.h>
#include <linux/phy.h>
#include <linux/micrel_phy.h>
#include <asm/smp_twd.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/hardware/gic.h>
#include <asm/mach/arch.h>
......@@ -119,6 +120,7 @@ static void __init imx6q_init_irq(void)
static void __init imx6q_timer_init(void)
{
mx6q_clocks_init();
twd_local_timer_of_register();
}
static struct sys_timer imx6q_timer = {
......
......@@ -127,6 +127,45 @@ static struct clocksource msm_clocksource = {
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
#ifdef CONFIG_LOCAL_TIMERS
static int __cpuinit msm_local_timer_setup(struct clock_event_device *evt)
{
/* Use existing clock_event for cpu 0 */
if (!smp_processor_id())
return 0;
writel_relaxed(0, event_base + TIMER_ENABLE);
writel_relaxed(0, event_base + TIMER_CLEAR);
writel_relaxed(~0, event_base + TIMER_MATCH_VAL);
evt->irq = msm_clockevent.irq;
evt->name = "local_timer";
evt->features = msm_clockevent.features;
evt->rating = msm_clockevent.rating;
evt->set_mode = msm_timer_set_mode;
evt->set_next_event = msm_timer_set_next_event;
evt->shift = msm_clockevent.shift;
evt->mult = div_sc(GPT_HZ, NSEC_PER_SEC, evt->shift);
evt->max_delta_ns = clockevent_delta2ns(0xf0000000, evt);
evt->min_delta_ns = clockevent_delta2ns(4, evt);
*__this_cpu_ptr(msm_evt.percpu_evt) = evt;
clockevents_register_device(evt);
enable_percpu_irq(evt->irq, 0);
return 0;
}
static void msm_local_timer_stop(struct clock_event_device *evt)
{
evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
disable_percpu_irq(evt->irq);
}
static struct local_timer_ops msm_local_timer_ops __cpuinitdata = {
.setup = msm_local_timer_setup,
.stop = msm_local_timer_stop,
};
#endif /* CONFIG_LOCAL_TIMERS */
static void __init msm_timer_init(void)
{
struct clock_event_device *ce = &msm_clockevent;
......@@ -173,8 +212,12 @@ static void __init msm_timer_init(void)
*__this_cpu_ptr(msm_evt.percpu_evt) = ce;
res = request_percpu_irq(ce->irq, msm_timer_interrupt,
ce->name, msm_evt.percpu_evt);
if (!res)
if (!res) {
enable_percpu_irq(ce->irq, 0);
#ifdef CONFIG_LOCAL_TIMERS
local_timer_register(&msm_local_timer_ops);
#endif
}
} else {
msm_evt.evt = ce;
res = request_irq(ce->irq, msm_timer_interrupt,
......@@ -191,40 +234,6 @@ static void __init msm_timer_init(void)
pr_err("clocksource_register failed\n");
}
#ifdef CONFIG_LOCAL_TIMERS
int __cpuinit local_timer_setup(struct clock_event_device *evt)
{
/* Use existing clock_event for cpu 0 */
if (!smp_processor_id())
return 0;
writel_relaxed(0, event_base + TIMER_ENABLE);
writel_relaxed(0, event_base + TIMER_CLEAR);
writel_relaxed(~0, event_base + TIMER_MATCH_VAL);
evt->irq = msm_clockevent.irq;
evt->name = "local_timer";
evt->features = msm_clockevent.features;
evt->rating = msm_clockevent.rating;
evt->set_mode = msm_timer_set_mode;
evt->set_next_event = msm_timer_set_next_event;
evt->shift = msm_clockevent.shift;
evt->mult = div_sc(GPT_HZ, NSEC_PER_SEC, evt->shift);
evt->max_delta_ns = clockevent_delta2ns(0xf0000000, evt);
evt->min_delta_ns = clockevent_delta2ns(4, evt);
*__this_cpu_ptr(msm_evt.percpu_evt) = evt;
clockevents_register_device(evt);
enable_percpu_irq(evt->irq, 0);
return 0;
}
void local_timer_stop(struct clock_event_device *evt)
{
evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
disable_percpu_irq(evt->irq);
}
#endif /* CONFIG_LOCAL_TIMERS */
struct sys_timer msm_timer = {
.init = msm_timer_init
};
......@@ -23,7 +23,6 @@ obj-$(CONFIG_TWL4030_CORE) += omap_twl.o
# SMP support ONLY available for OMAP4
obj-$(CONFIG_SMP) += omap-smp.o omap-headsmp.o
obj-$(CONFIG_LOCAL_TIMERS) += timer-mpu.o
obj-$(CONFIG_HOTPLUG_CPU) += omap-hotplug.o
obj-$(CONFIG_ARCH_OMAP4) += omap4-common.o omap-wakeupgen.o \
sleep44xx.o
......
/*
* The MPU local timer source file. In OMAP4, both cortex-a9 cores have
* own timer in it's MPU domain. These timers will be driving the
* linux kernel SMP tick framework when active. These timers are not
* part of the wake up domain.
*
* Copyright (C) 2009 Texas Instruments, Inc.
*
* Author:
* Santosh Shilimkar <santosh.shilimkar@ti.com>
*
* This file is based on arm realview smp platform file.
* Copyright (C) 2002 ARM Ltd.
*
* 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/init.h>
#include <linux/smp.h>
#include <linux/clockchips.h>
#include <asm/irq.h>
#include <asm/smp_twd.h>
#include <asm/localtimer.h>
/*
* Setup the local clock events for a CPU.
*/
int __cpuinit local_timer_setup(struct clock_event_device *evt)
{
/* Local timers are not supprted on OMAP4430 ES1.0 */
if (omap_rev() == OMAP4430_REV_ES1_0)
return -ENXIO;
evt->irq = OMAP44XX_IRQ_LOCALTIMER;
twd_timer_setup(evt);
return 0;
}
......@@ -39,7 +39,7 @@
#include <asm/mach/time.h>
#include <plat/dmtimer.h>
#include <asm/localtimer.h>
#include <asm/smp_twd.h>
#include <asm/sched_clock.h>
#include "common.h"
#include <plat/omap_hwmod.h>
......@@ -324,14 +324,26 @@ OMAP_SYS_TIMER(3_secure)
#endif
#ifdef CONFIG_ARCH_OMAP4
static void __init omap4_timer_init(void)
{
#ifdef CONFIG_LOCAL_TIMERS
twd_base = ioremap(OMAP44XX_LOCAL_TWD_BASE, SZ_256);
BUG_ON(!twd_base);
static DEFINE_TWD_LOCAL_TIMER(twd_local_timer,
OMAP44XX_LOCAL_TWD_BASE,
OMAP44XX_IRQ_LOCALTIMER);
#endif
static void __init omap4_timer_init(void)
{
omap2_gp_clockevent_init(1, OMAP4_CLKEV_SOURCE);
omap2_gp_clocksource_init(2, OMAP4_MPU_SOURCE);
#ifdef CONFIG_LOCAL_TIMERS
/* Local timers are not supprted on OMAP4430 ES1.0 */
if (omap_rev() != OMAP4430_REV_ES1_0) {
int err;
err = twd_local_timer_register(&twd_local_timer);
if (err)
pr_err("twd_local_timer_register failed %d\n", err);
}
#endif
}
OMAP_SYS_TIMER(4)
#endif
......
......@@ -36,7 +36,7 @@
#include <asm/pgtable.h>
#include <asm/hardware/gic.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/localtimer.h>
#include <asm/smp_twd.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
......@@ -383,6 +383,23 @@ static void realview_eb11mp_fixup(void)
realview_eb_isp1761_resources[1].end = IRQ_EB11MP_USB;
}
#ifdef CONFIG_HAVE_ARM_TWD
static DEFINE_TWD_LOCAL_TIMER(twd_local_timer,
REALVIEW_EB11MP_TWD_BASE,
IRQ_LOCALTIMER);
static void __init realview_eb_twd_init(void)
{
if (core_tile_eb11mp() || core_tile_a9mp()) {
int err = twd_local_timer_register(&twd_local_timer);
if (err)
pr_err("twd_local_timer_register failed %d\n", err);
}
}
#else
#define realview_eb_twd_init() do { } while(0)
#endif
static void __init realview_eb_timer_init(void)
{
unsigned int timer_irq;
......@@ -392,15 +409,13 @@ static void __init realview_eb_timer_init(void)
timer2_va_base = __io_address(REALVIEW_EB_TIMER2_3_BASE);
timer3_va_base = __io_address(REALVIEW_EB_TIMER2_3_BASE) + 0x20;
if (core_tile_eb11mp() || core_tile_a9mp()) {
#ifdef CONFIG_LOCAL_TIMERS
twd_base = __io_address(REALVIEW_EB11MP_TWD_BASE);
#endif
if (core_tile_eb11mp() || core_tile_a9mp())
timer_irq = IRQ_EB11MP_TIMER0_1;
} else
else
timer_irq = IRQ_EB_TIMER0_1;
realview_timer_init(timer_irq);
realview_eb_twd_init();
}
static struct sys_timer realview_eb_timer = {
......
......@@ -36,7 +36,7 @@
#include <asm/pgtable.h>
#include <asm/hardware/gic.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/localtimer.h>
#include <asm/smp_twd.h>
#include <asm/mach/arch.h>
#include <asm/mach/flash.h>
......@@ -290,6 +290,21 @@ static void __init gic_init_irq(void)
gic_cascade_irq(1, IRQ_TC11MP_PB_IRQ1);
}
#ifdef CONFIG_HAVE_ARM_TWD
static DEFINE_TWD_LOCAL_TIMER(twd_local_timer,
REALVIEW_TC11MP_TWD_BASE,
IRQ_LOCALTIMER);
static void __init realview_pb11mp_twd_init(void)
{
int err = twd_local_timer_register(&twd_local_timer);
if (err)
pr_err("twd_local_timer_register failed %d\n", err);
}
#else
#define realview_pb11mp_twd_init() do {} while(0)
#endif
static void __init realview_pb11mp_timer_init(void)
{
timer0_va_base = __io_address(REALVIEW_PB11MP_TIMER0_1_BASE);
......@@ -297,10 +312,8 @@ static void __init realview_pb11mp_timer_init(void)
timer2_va_base = __io_address(REALVIEW_PB11MP_TIMER2_3_BASE);
timer3_va_base = __io_address(REALVIEW_PB11MP_TIMER2_3_BASE) + 0x20;
#ifdef CONFIG_LOCAL_TIMERS
twd_base = __io_address(REALVIEW_TC11MP_TWD_BASE);
#endif
realview_timer_init(IRQ_TC11MP_TIMER0_1);
realview_pb11mp_twd_init();
}
static struct sys_timer realview_pb11mp_timer = {
......
......@@ -298,6 +298,21 @@ static void __init gic_init_irq(void)
}
}
#ifdef CONFIG_HAVE_ARM_TWD
static DEFINE_TWD_LOCAL_TIMER(twd_local_timer,
REALVIEW_PBX_TILE_TWD_BASE,
IRQ_LOCALTIMER);
static void __init realview_pbx_twd_init(void)
{
int err = twd_local_timer_register(&twd_local_timer);
if (err)
pr_err("twd_local_timer_register failed %d\n", err);
}
#else
#define realview_pbx_twd_init() do { } while(0)
#endif
static void __init realview_pbx_timer_init(void)
{
timer0_va_base = __io_address(REALVIEW_PBX_TIMER0_1_BASE);
......@@ -305,11 +320,8 @@ static void __init realview_pbx_timer_init(void)
timer2_va_base = __io_address(REALVIEW_PBX_TIMER2_3_BASE);
timer3_va_base = __io_address(REALVIEW_PBX_TIMER2_3_BASE) + 0x20;
#ifdef CONFIG_LOCAL_TIMERS
if (core_tile_pbx11mp() || core_tile_pbxa9mp())
twd_base = __io_address(REALVIEW_PBX_TILE_TWD_BASE);
#endif
realview_timer_init(IRQ_PBX_TIMER0_1);
realview_pbx_twd_init();
}
static struct sys_timer realview_pbx_timer = {
......
......@@ -16,7 +16,6 @@ obj-$(CONFIG_ARCH_R8A7779) += setup-r8a7779.o clock-r8a7779.o intc-r8a7779.o
# SMP objects
smp-y := platsmp.o headsmp.o
smp-$(CONFIG_HOTPLUG_CPU) += hotplug.o
smp-$(CONFIG_LOCAL_TIMERS) += localtimer.o
smp-$(CONFIG_ARCH_SH73A0) += smp-sh73a0.o
smp-$(CONFIG_ARCH_R8A7779) += smp-r8a7779.o
......
......@@ -2,6 +2,8 @@
#define __ARCH_MACH_COMMON_H
extern struct sys_timer shmobile_timer;
struct twd_local_timer;
void shmobile_twd_init(struct twd_local_timer *twd_local_timer);
extern void shmobile_setup_console(void);
extern void shmobile_secondary_vector(void);
extern int shmobile_platform_cpu_kill(unsigned int cpu);
......
/*
* SMP support for R-Mobile / SH-Mobile - local timer portion
*
* Copyright (C) 2010 Magnus Damm
*
* Based on vexpress, 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/init.h>
#include <linux/smp.h>
#include <linux/clockchips.h>
#include <asm/smp_twd.h>
#include <asm/localtimer.h>
/*
* Setup the local clock events for a CPU.
*/
int __cpuinit local_timer_setup(struct clock_event_device *evt)
{
evt->irq = 29;
twd_timer_setup(evt);
return 0;
}
......@@ -17,7 +17,6 @@
#include <linux/smp.h>
#include <linux/io.h>
#include <asm/hardware/gic.h>
#include <asm/localtimer.h>
#include <asm/mach-types.h>
#include <mach/common.h>
......
......@@ -64,6 +64,8 @@ static void __iomem *scu_base_addr(void)
static DEFINE_SPINLOCK(scu_lock);
static unsigned long tmp;
static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, 0xf0000600, 29);
static void modify_scu_cpu_psr(unsigned long set, unsigned long clr)
{
void __iomem *scu_base = scu_base_addr();
......@@ -82,11 +84,7 @@ unsigned int __init r8a7779_get_core_count(void)
{
void __iomem *scu_base = scu_base_addr();
#ifdef CONFIG_HAVE_ARM_TWD
/* twd_base needs to be initialized before percpu_timer_setup() */
twd_base = (void __iomem *)0xf0000600;
#endif
shmobile_twd_init(&twd_local_timer);
return scu_get_core_count(scu_base);
}
......
......@@ -42,6 +42,8 @@ static void __iomem *scu_base_addr(void)
static DEFINE_SPINLOCK(scu_lock);
static unsigned long tmp;
static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, 0xf0000600, 29);
static void modify_scu_cpu_psr(unsigned long set, unsigned long clr)
{
void __iomem *scu_base = scu_base_addr();
......@@ -60,11 +62,7 @@ unsigned int __init sh73a0_get_core_count(void)
{
void __iomem *scu_base = scu_base_addr();
#ifdef CONFIG_HAVE_ARM_TWD
/* twd_base needs to be initialized before percpu_timer_setup() */
twd_base = (void __iomem *)0xf0000600;
#endif
shmobile_twd_init(&twd_local_timer);
return scu_get_core_count(scu_base);
}
......
......@@ -20,6 +20,7 @@
*/
#include <linux/platform_device.h>
#include <asm/mach/time.h>
#include <asm/smp_twd.h>
static void __init shmobile_late_time_init(void)
{
......@@ -41,6 +42,15 @@ static void __init shmobile_timer_init(void)
late_time_init = shmobile_late_time_init;
}
void __init shmobile_twd_init(struct twd_local_timer *twd_local_timer)
{
#ifdef CONFIG_HAVE_ARM_TWD
int err = twd_local_timer_register(twd_local_timer);
if (err)
pr_err("twd_local_timer_register failed %d\n", err);
#endif
}
struct sys_timer shmobile_timer = {
.init = shmobile_timer_init,
};
......@@ -13,7 +13,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pinmux-tegra20-tables.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += pinmux-tegra30-tables.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += board-dt-tegra30.o
obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_TEGRA_SYSTEM_DMA) += dma.o
obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o
......
/*
* arch/arm/mach-tegra/localtimer.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/init.h>
#include <linux/smp.h>
#include <linux/clockchips.h>
#include <asm/irq.h>
#include <asm/smp_twd.h>
#include <asm/localtimer.h>
/*
* Setup the local clock events for a CPU.
*/
int __cpuinit local_timer_setup(struct clock_event_device *evt)
{
evt->irq = IRQ_LOCALTIMER;
twd_timer_setup(evt);
return 0;
}
......@@ -28,7 +28,7 @@
#include <linux/io.h>
#include <asm/mach/time.h>
#include <asm/localtimer.h>
#include <asm/smp_twd.h>
#include <asm/sched_clock.h>
#include <mach/iomap.h>
......@@ -162,6 +162,21 @@ static struct irqaction tegra_timer_irq = {
.irq = INT_TMR3,
};
#ifdef CONFIG_HAVE_ARM_TWD
static DEFINE_TWD_LOCAL_TIMER(twd_local_timer,
TEGRA_ARM_PERIF_BASE + 0x600,
IRQ_LOCALTIMER);
static void __init tegra_twd_init(void)
{
int err = twd_local_timer_register(&twd_local_timer);
if (err)
pr_err("twd_local_timer_register failed %d\n", err);
}
#else
#define tegra_twd_init() do {} while(0)
#endif
static void __init tegra_init_timer(void)
{
struct clk *clk;
......@@ -188,10 +203,6 @@ static void __init tegra_init_timer(void)
else
clk_enable(clk);
#ifdef CONFIG_HAVE_ARM_TWD
twd_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x600);
#endif
switch (rate) {
case 12000000:
timer_writel(0x000b, TIMERUS_USEC_CFG);
......@@ -231,6 +242,7 @@ static void __init tegra_init_timer(void)
tegra_clockevent.cpumask = cpu_all_mask;
tegra_clockevent.irq = tegra_timer_irq.irq;
clockevents_register_device(&tegra_clockevent);
tegra_twd_init();
}
struct sys_timer tegra_timer = {
......
......@@ -15,7 +15,6 @@ obj-$(CONFIG_MACH_U8500) += board-mop500.o board-mop500-sdi.o \
obj-$(CONFIG_MACH_U5500) += board-u5500.o board-u5500-sdi.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o
obj-$(CONFIG_U5500_MODEM_IRQ) += modem-irq-db5500.o
obj-$(CONFIG_U5500_MBOX) += mbox-db5500.o
......@@ -14,7 +14,6 @@
#include <asm/hardware/gic.h>
#include <asm/mach/map.h>
#include <asm/localtimer.h>
#include <mach/hardware.h>
#include <mach/setup.h>
......
/*
* Copyright (C) 2008-2009 ST-Ericsson
* Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
*
* This file is heavily based on relaview platform, almost a copy.
*
* Copyright (C) 2002 ARM Ltd.
*
* 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/init.h>
#include <linux/smp.h>
#include <linux/clockchips.h>
#include <asm/irq.h>
#include <asm/smp_twd.h>
#include <asm/localtimer.h>
/*
* Setup the local clock events for a CPU.
*/
int __cpuinit local_timer_setup(struct clock_event_device *evt)
{
evt->irq = IRQ_LOCALTIMER;
twd_timer_setup(evt);
return 0;
}
......@@ -8,28 +8,45 @@
#include <linux/errno.h>
#include <linux/clksrc-dbx500-prcmu.h>
#include <asm/localtimer.h>
#include <asm/smp_twd.h>
#include <plat/mtu.h>
#include <mach/setup.h>
#include <mach/hardware.h>
#ifdef CONFIG_HAVE_ARM_TWD
static DEFINE_TWD_LOCAL_TIMER(u5500_twd_local_timer,
U5500_TWD_BASE, IRQ_LOCALTIMER);
static DEFINE_TWD_LOCAL_TIMER(u8500_twd_local_timer,
U8500_TWD_BASE, IRQ_LOCALTIMER);
static void __init ux500_twd_init(void)
{
struct twd_local_timer *twd_local_timer;
int err;
twd_local_timer = cpu_is_u5500() ? &u5500_twd_local_timer :
&u8500_twd_local_timer;
err = twd_local_timer_register(twd_local_timer);
if (err)
pr_err("twd_local_timer_register failed %d\n", err);
}
#else
#define ux500_twd_init() do { } while(0)
#endif
static void __init ux500_timer_init(void)
{
void __iomem *mtu_timer_base;
void __iomem *prcmu_timer_base;
int err;
if (cpu_is_u5500()) {
#ifdef CONFIG_LOCAL_TIMERS
twd_base = __io_address(U5500_TWD_BASE);
#endif
mtu_timer_base = __io_address(U5500_MTU0_BASE);
prcmu_timer_base = __io_address(U5500_PRCMU_TIMER_3_BASE);
} else if (cpu_is_u8500()) {
#ifdef CONFIG_LOCAL_TIMERS
twd_base = __io_address(U8500_TWD_BASE);
#endif
mtu_timer_base = __io_address(U8500_MTU0_BASE);
prcmu_timer_base = __io_address(U8500_PRCMU_TIMER_4_BASE);
} else {
......@@ -55,6 +72,7 @@ static void __init ux500_timer_init(void)
nmdk_timer_init(mtu_timer_base);
clksrc_dbx500_prcmu_init(prcmu_timer_base);
ux500_twd_init();
}
static void ux500_timer_reset(void)
......
......@@ -42,15 +42,26 @@ static struct map_desc ct_ca9x4_io_desc[] __initdata = {
static void __init ct_ca9x4_map_io(void)
{
iotable_init(ct_ca9x4_io_desc, ARRAY_SIZE(ct_ca9x4_io_desc));
#ifdef CONFIG_LOCAL_TIMERS
twd_base = ioremap(A9_MPCORE_TWD, SZ_32);
#endif
}
#ifdef CONFIG_HAVE_ARM_TWD
static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, A9_MPCORE_TWD, IRQ_LOCALTIMER);
static void __init ca9x4_twd_init(void)
{
int err = twd_local_timer_register(&twd_local_timer);
if (err)
pr_err("twd_local_timer_register failed %d\n", err);
}
#else
#define ca9x4_twd_init() do {} while(0)
#endif
static void __init ct_ca9x4_init_irq(void)
{
gic_init(0, 29, ioremap(A9_MPCORE_GIC_DIST, SZ_4K),
ioremap(A9_MPCORE_GIC_CPU, SZ_256));
ca9x4_twd_init();
}
static void ct_ca9x4_clcd_enable(struct clcd_fb *fb)
......
obj-y := clock.o
obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o
obj-$(CONFIG_PLAT_VERSATILE_CLCD) += clcd.o
obj-$(CONFIG_PLAT_VERSATILE_FPGA_IRQ) += fpga-irq.o
obj-$(CONFIG_PLAT_VERSATILE_LEDS) += leds.o
......
/*
* linux/arch/arm/plat-versatile/localtimer.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/init.h>
#include <linux/smp.h>
#include <linux/clockchips.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/smp_twd.h>
#include <asm/localtimer.h>
#include <mach/irqs.h>
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", },
{ },
};
/*
* Setup the local clock events for a CPU.
*/
int __cpuinit local_timer_setup(struct clock_event_device *evt)
{
#if defined(CONFIG_OF)
static int dt_node_probed;
/* Look for TWD node only once */
if (!dt_node_probed) {
struct device_node *node = of_find_matching_node(NULL,
twd_of_match);
if (node)
twd_base = of_iomap(node, 0);
dt_node_probed = 1;
}
#endif
if (!twd_base)
return -ENXIO;
evt->irq = IRQ_LOCALTIMER;
twd_timer_setup(evt);
return 0;
}
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