Now that we have a generic arch hook for broadcast we can remove the local

timer API entirely. Doing so will reduce code in ARM core, reduce the
 architecture dependencies of our timer drivers, and simplify the code because
 we no longer go through an architecture layer that is essentially a hotplug
 notifier.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.10 (GNU/Linux)
 
 iQIcBAABCAAGBQJRydGwAAoJENidgRMleOc9tMMP/Awy0ETkLHQkXdFdRNLQH7Jd
 BpDagzBkpt/U/qsh2Dy5Yhz7Qf/HQg6CwJK/b1apHguSPzNcSov+YS7ArPV+kuDY
 9OCF5wkVqPW7BPHlxJ+2QAjayIFUaBR+35kpiU+hv9Gkhs11oK90iAWxEIDUGzA6
 5miuOVJeyDEwV8des3NYmVVOKziHLJjX3fjMVnc2gxE6PLmOEUQ9t42bVU1aCfub
 pRMUXNuG+aFk8lRExPfs4wWfEKCOHrgJPSE4OPFp3pDJeqhFi5tXTiPBQ22FpNf5
 78fEDeguS0QS6f/3rGKSdGu/Yz5Lmcldv4tOkfzvNaZP7zhE18wKJu6bSCrZ/Onn
 MnhriYWZh+YgX1V8wTMFMPEV+OR+FJdN4C8PhvNWmMC1Xoq25HtrVQUP5aElgAJs
 mLcgTXxLYpvZH3jPxdtMR9IuDc+86qmFerGShWqp/1YaNHwpR8dNZxFyVNfw+FUL
 /GoAjk/6MsJlZUchiF7I8yp4jYyMcXcV8Bi4tgIAf5rJO9PZBpuxtyAL9uVHnL13
 pwkctMnMQwoP6AE9uYfbdnHCKJxF1hny4tKI5sNxAmK8I6bBkfibIZ3sNRAbSrY7
 56kV+tJrrgvgAUDkgdluyS9eFs26iHGPsHK4lmzJzFylWmvWFa8tWpP6G0kCa0wP
 A1XCqUOryLeL8enPV+z2
 =wZbm
 -----END PGP SIGNATURE-----

Merge tag 'remove-local-timers' of git://git.kernel.org/pub/scm/linux/kernel/git/davidb/linux-msm into next/cleanup

From Stephen Boyd:

Now that we have a generic arch hook for broadcast we can remove the
local timer API entirely. Doing so will reduce code in ARM core, reduce
the architecture dependencies of our timer drivers, and simplify the code
because we no longer go through an architecture layer that is essentially
a hotplug notifier.

* tag 'remove-local-timers' of git://git.kernel.org/pub/scm/linux/kernel/git/davidb/linux-msm:
  ARM: smp: Remove local timer API
  clocksource: time-armada-370-xp: Divorce from local timer API
  clocksource: time-armada-370-xp: Fix sparse warning
  ARM: msm: Divorce msm_timer from local timer API
  ARM: PRIMA2: Divorce timer-marco from local timer API
  ARM: EXYNOS4: Divorce mct from local timer API
  ARM: OMAP2+: Divorce from local timer API
  ARM: smp_twd: Divorce smp_twd from local timer API
  ARM: smp: Remove duplicate dummy timer implementation

Resolved a large number of conflicts due to __cpuinit cleanups, etc.

Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
Olof Johansson 2013-07-23 14:51:34 -07:00
commit 47dcd3563e
19 changed files with 265 additions and 341 deletions

View File

@ -645,7 +645,7 @@ config ARCH_SHMOBILE
select CLKDEV_LOOKUP
select GENERIC_CLOCKEVENTS
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if LOCAL_TIMERS
select HAVE_ARM_TWD if SMP
select HAVE_CLK
select HAVE_MACH_CLKDEV
select HAVE_SMP
@ -1584,16 +1584,6 @@ config ARM_PSCI
0022A ("Power State Coordination Interface System Software on
ARM processors").
config LOCAL_TIMERS
bool "Use local timer interrupts"
depends on SMP
default y
help
Enable support for local timers on SMP platforms, rather then the
legacy IPI broadcast method. Local timers allows the system
accounting to be spread across the timer interval, preventing a
"thundering herd" at every timer tick.
# The GPIO number here must be sorted by descending number. In case of
# a multiplatform kernel, we just want the highest value required by the
# selected platforms.

View File

@ -1,34 +0,0 @@
/*
* arch/arm/include/asm/localtimer.h
*
* Copyright (C) 2004-2005 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.
*/
#ifndef __ASM_ARM_LOCALTIMER_H
#define __ASM_ARM_LOCALTIMER_H
#include <linux/errno.h>
struct clock_event_device;
struct local_timer_ops {
int (*setup)(struct clock_event_device *);
void (*stop)(struct clock_event_device *);
};
#ifdef CONFIG_LOCAL_TIMERS
/*
* Register a local timer driver
*/
int local_timer_register(struct local_timer_ops *);
#else
static inline int local_timer_register(struct local_timer_ops *ops)
{
return -ENXIO;
}
#endif
#endif

View File

@ -41,7 +41,6 @@
#include <asm/sections.h>
#include <asm/tlbflush.h>
#include <asm/ptrace.h>
#include <asm/localtimer.h>
#include <asm/smp_plat.h>
#include <asm/virt.h>
#include <asm/mach/arch.h>
@ -146,8 +145,6 @@ int boot_secondary(unsigned int cpu, struct task_struct *idle)
}
#ifdef CONFIG_HOTPLUG_CPU
static void percpu_timer_stop(void);
static int platform_cpu_kill(unsigned int cpu)
{
if (smp_ops.cpu_kill)
@ -190,11 +187,6 @@ int __cpu_disable(void)
*/
migrate_irqs();
/*
* Stop the local timer for this CPU.
*/
percpu_timer_stop();
/*
* Flush user cache and TLB mappings, and then remove this CPU
* from the vm mask set of all processes.
@ -316,8 +308,6 @@ static void 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.
@ -372,11 +362,6 @@ asmlinkage void secondary_start_kernel(void)
set_cpu_online(cpu, true);
complete(&cpu_running);
/*
* Setup the percpu timer for this CPU.
*/
percpu_timer_setup();
local_irq_enable();
local_fiq_enable();
@ -422,12 +407,6 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
if (max_cpus > ncores)
max_cpus = ncores;
if (ncores > 1 && max_cpus) {
/*
* Enable the local timer or broadcast device for the
* boot CPU, but only if we have more than one CPU.
*/
percpu_timer_setup();
/*
* Initialise the present map, which describes the set of CPUs
* actually populated at the present time. A platform should
@ -504,11 +483,6 @@ u64 smp_irq_stat_cpu(unsigned int cpu)
return sum;
}
/*
* Timer (local or broadcast) support
*/
static DEFINE_PER_CPU(struct clock_event_device, percpu_clockevent);
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
void tick_broadcast(const struct cpumask *mask)
{
@ -516,67 +490,6 @@ void tick_broadcast(const struct cpumask *mask)
}
#endif
static void broadcast_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
}
static void broadcast_timer_setup(struct clock_event_device *evt)
{
evt->name = "dummy_timer";
evt->features = CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_DUMMY;
evt->rating = 100;
evt->mult = 1;
evt->set_mode = broadcast_timer_set_mode;
clockevents_register_device(evt);
}
static struct local_timer_ops *lt_ops;
#ifdef CONFIG_LOCAL_TIMERS
int local_timer_register(struct local_timer_ops *ops)
{
if (!is_smp() || !setup_max_cpus)
return -ENXIO;
if (lt_ops)
return -EBUSY;
lt_ops = ops;
return 0;
}
#endif
static void percpu_timer_setup(void)
{
unsigned int cpu = smp_processor_id();
struct clock_event_device *evt = &per_cpu(percpu_clockevent, cpu);
evt->cpumask = cpumask_of(cpu);
if (!lt_ops || lt_ops->setup(evt))
broadcast_timer_setup(evt);
}
#ifdef CONFIG_HOTPLUG_CPU
/*
* The generic clock events code purposely does not stop the local timer
* on CPU_DEAD/CPU_DEAD_FROZEN hotplug events, so we have to do it
* manually here.
*/
static void percpu_timer_stop(void)
{
unsigned int cpu = smp_processor_id();
struct clock_event_device *evt = &per_cpu(percpu_clockevent, cpu);
if (lt_ops)
lt_ops->stop(evt);
}
#endif
static DEFINE_RAW_SPINLOCK(stop_lock);
/*

View File

@ -11,6 +11,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
@ -24,7 +25,6 @@
#include <asm/smp_plat.h>
#include <asm/smp_twd.h>
#include <asm/localtimer.h>
/* set up by the platform code */
static void __iomem *twd_base;
@ -33,7 +33,7 @@ static struct clk *twd_clk;
static unsigned long twd_timer_rate;
static DEFINE_PER_CPU(bool, percpu_setup_called);
static struct clock_event_device __percpu **twd_evt;
static struct clock_event_device __percpu *twd_evt;
static int twd_ppi;
static void twd_set_mode(enum clock_event_mode mode,
@ -90,8 +90,10 @@ static int twd_timer_ack(void)
return 0;
}
static void twd_timer_stop(struct clock_event_device *clk)
static void twd_timer_stop(void)
{
struct clock_event_device *clk = __this_cpu_ptr(twd_evt);
twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
disable_percpu_irq(clk->irq);
}
@ -106,7 +108,7 @@ static void twd_update_frequency(void *new_rate)
{
twd_timer_rate = *((unsigned long *) new_rate);
clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate);
clockevents_update_freq(__this_cpu_ptr(twd_evt), twd_timer_rate);
}
static int twd_rate_change(struct notifier_block *nb,
@ -132,7 +134,7 @@ static struct notifier_block twd_clk_nb = {
static int twd_clk_init(void)
{
if (twd_evt && *__this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk))
if (twd_evt && __this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk))
return clk_notifier_register(twd_clk, &twd_clk_nb);
return 0;
@ -151,7 +153,7 @@ static void twd_update_frequency(void *data)
{
twd_timer_rate = clk_get_rate(twd_clk);
clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate);
clockevents_update_freq(__this_cpu_ptr(twd_evt), twd_timer_rate);
}
static int twd_cpufreq_transition(struct notifier_block *nb,
@ -177,7 +179,7 @@ static struct notifier_block twd_cpufreq_nb = {
static int twd_cpufreq_init(void)
{
if (twd_evt && *__this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk))
if (twd_evt && __this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk))
return cpufreq_register_notifier(&twd_cpufreq_nb,
CPUFREQ_TRANSITION_NOTIFIER);
@ -228,7 +230,7 @@ static void twd_calibrate_rate(void)
static irqreturn_t twd_handler(int irq, void *dev_id)
{
struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
struct clock_event_device *evt = dev_id;
if (twd_timer_ack()) {
evt->event_handler(evt);
@ -265,9 +267,9 @@ static void twd_get_clock(struct device_node *np)
/*
* Setup the local clock events for a CPU.
*/
static int twd_timer_setup(struct clock_event_device *clk)
static void twd_timer_setup(void)
{
struct clock_event_device **this_cpu_clk;
struct clock_event_device *clk = __this_cpu_ptr(twd_evt);
int cpu = smp_processor_id();
/*
@ -276,9 +278,9 @@ static int twd_timer_setup(struct clock_event_device *clk)
*/
if (per_cpu(percpu_setup_called, cpu)) {
__raw_writel(0, twd_base + TWD_TIMER_CONTROL);
clockevents_register_device(*__this_cpu_ptr(twd_evt));
clockevents_register_device(clk);
enable_percpu_irq(clk->irq, 0);
return 0;
return;
}
per_cpu(percpu_setup_called, cpu) = true;
@ -297,27 +299,37 @@ static int twd_timer_setup(struct clock_event_device *clk)
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;
clk->cpumask = cpumask_of(cpu);
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 = {
.setup = twd_timer_setup,
.stop = twd_timer_stop,
static int twd_timer_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_STARTING:
twd_timer_setup();
break;
case CPU_DYING:
twd_timer_stop();
break;
}
return NOTIFY_OK;
}
static struct notifier_block twd_timer_cpu_nb = {
.notifier_call = twd_timer_cpu_notify,
};
static int __init twd_local_timer_common_register(struct device_node *np)
{
int err;
twd_evt = alloc_percpu(struct clock_event_device *);
twd_evt = alloc_percpu(struct clock_event_device);
if (!twd_evt) {
err = -ENOMEM;
goto out_free;
@ -329,12 +341,22 @@ static int __init twd_local_timer_common_register(struct device_node *np)
goto out_free;
}
err = local_timer_register(&twd_lt_ops);
err = register_cpu_notifier(&twd_timer_cpu_nb);
if (err)
goto out_irq;
twd_get_clock(np);
/*
* Immediately configure the timer on the boot CPU, unless we need
* jiffies to be incrementing to calibrate the rate in which case
* setup the timer in late_time_init.
*/
if (twd_timer_rate)
twd_timer_setup();
else
late_time_init = twd_timer_setup;
return 0;
out_irq:

View File

@ -12,7 +12,7 @@ config ARCH_HIGHBANK
select CPU_V7
select GENERIC_CLOCKEVENTS
select HAVE_ARM_SCU
select HAVE_ARM_TWD if LOCAL_TIMERS
select HAVE_ARM_TWD if SMP
select HAVE_SMP
select MAILBOX
select PL320_MBOX

View File

@ -793,7 +793,8 @@ config SOC_IMX6Q
select COMMON_CLK
select CPU_V7
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if LOCAL_TIMERS
select HAVE_ARM_TWD if SMP
select HAVE_CAN_FLEXCAN if CAN
select HAVE_IMX_ANATOP
select HAVE_IMX_GPC
select HAVE_IMX_MMDC

View File

@ -16,6 +16,7 @@
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/cpu.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@ -26,7 +27,6 @@
#include <linux/sched_clock.h>
#include <asm/mach/time.h>
#include <asm/localtimer.h>
#include "common.h"
@ -49,7 +49,7 @@ static void __iomem *sts_base;
static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
struct clock_event_device *evt = dev_id;
/* Stop the timer tick */
if (evt->mode == CLOCK_EVT_MODE_ONESHOT) {
u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
@ -101,18 +101,7 @@ static void msm_timer_set_mode(enum clock_event_mode mode,
writel_relaxed(ctrl, event_base + TIMER_ENABLE);
}
static struct clock_event_device msm_clockevent = {
.name = "gp_timer",
.features = CLOCK_EVT_FEAT_ONESHOT,
.rating = 200,
.set_next_event = msm_timer_set_next_event,
.set_mode = msm_timer_set_mode,
};
static union {
struct clock_event_device *evt;
struct clock_event_device * __percpu *percpu_evt;
} msm_evt;
static struct clock_event_device __percpu *msm_evt;
static void __iomem *source_base;
@ -138,23 +127,34 @@ static struct clocksource msm_clocksource = {
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
#ifdef CONFIG_LOCAL_TIMERS
static int msm_timer_irq;
static int msm_timer_has_ppi;
static int msm_local_timer_setup(struct clock_event_device *evt)
{
/* Use existing clock_event for cpu 0 */
if (!smp_processor_id())
return 0;
int cpu = smp_processor_id();
int err;
evt->irq = msm_clockevent.irq;
evt->name = "local_timer";
evt->features = msm_clockevent.features;
evt->rating = msm_clockevent.rating;
evt->irq = msm_timer_irq;
evt->name = "msm_timer";
evt->features = CLOCK_EVT_FEAT_ONESHOT;
evt->rating = 200;
evt->set_mode = msm_timer_set_mode;
evt->set_next_event = msm_timer_set_next_event;
evt->cpumask = cpumask_of(cpu);
clockevents_config_and_register(evt, GPT_HZ, 4, 0xffffffff);
if (msm_timer_has_ppi) {
enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING);
} else {
err = request_irq(evt->irq, msm_timer_interrupt,
IRQF_TIMER | IRQF_NOBALANCING |
IRQF_TRIGGER_RISING, "gp_timer", evt);
if (err)
pr_err("request_irq failed\n");
}
*__this_cpu_ptr(msm_evt.percpu_evt) = evt;
clockevents_config_and_register(evt, GPT_HZ, 4, 0xf0000000);
enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING);
return 0;
}
@ -164,11 +164,28 @@ static void msm_local_timer_stop(struct clock_event_device *evt)
disable_percpu_irq(evt->irq);
}
static struct local_timer_ops msm_local_timer_ops = {
.setup = msm_local_timer_setup,
.stop = msm_local_timer_stop,
static int msm_timer_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
/*
* Grab cpu pointer in each case to avoid spurious
* preemptible warnings
*/
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_STARTING:
msm_local_timer_setup(this_cpu_ptr(msm_evt));
break;
case CPU_DYING:
msm_local_timer_stop(this_cpu_ptr(msm_evt));
break;
}
return NOTIFY_OK;
}
static struct notifier_block msm_timer_cpu_nb = {
.notifier_call = msm_timer_cpu_notify,
};
#endif /* CONFIG_LOCAL_TIMERS */
static notrace u32 msm_sched_clock_read(void)
{
@ -178,38 +195,35 @@ static notrace u32 msm_sched_clock_read(void)
static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq,
bool percpu)
{
struct clock_event_device *ce = &msm_clockevent;
struct clocksource *cs = &msm_clocksource;
int res;
int res = 0;
ce->cpumask = cpumask_of(0);
ce->irq = irq;
msm_timer_irq = irq;
msm_timer_has_ppi = percpu;
clockevents_config_and_register(ce, GPT_HZ, 4, 0xffffffff);
if (percpu) {
msm_evt.percpu_evt = alloc_percpu(struct clock_event_device *);
if (!msm_evt.percpu_evt) {
pr_err("memory allocation failed for %s\n", ce->name);
goto err;
}
*__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) {
enable_percpu_irq(ce->irq, IRQ_TYPE_EDGE_RISING);
#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,
IRQF_TIMER | IRQF_NOBALANCING |
IRQF_TRIGGER_RISING, ce->name, &msm_evt.evt);
msm_evt = alloc_percpu(struct clock_event_device);
if (!msm_evt) {
pr_err("memory allocation failed for clockevents\n");
goto err;
}
if (percpu)
res = request_percpu_irq(irq, msm_timer_interrupt,
"gp_timer", msm_evt);
if (res) {
pr_err("request_percpu_irq failed\n");
} else {
res = register_cpu_notifier(&msm_timer_cpu_nb);
if (res) {
free_percpu_irq(irq, msm_evt);
goto err;
}
/* Immediately configure the timer on the boot CPU */
msm_local_timer_setup(__this_cpu_ptr(msm_evt));
}
if (res)
pr_err("request_irq failed for %s\n", ce->name);
err:
writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE);
res = clocksource_register_hz(cs, dgt_hz);

View File

@ -37,9 +37,8 @@ config ARCH_OMAP4
select CACHE_L2X0
select CPU_V7
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if LOCAL_TIMERS
select HAVE_ARM_TWD if SMP
select HAVE_SMP
select LOCAL_TIMERS if SMP
select OMAP_INTERCONNECT
select PL310_ERRATA_588369
select PL310_ERRATA_727915

View File

@ -600,7 +600,6 @@ static OMAP_SYS_32K_TIMER_INIT(4, 1, "timer_32k_ck", "ti,timer-alwon",
#endif
#ifdef CONFIG_ARCH_OMAP4
#ifdef CONFIG_LOCAL_TIMERS
static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, OMAP44XX_LOCAL_TWD_BASE, 29);
void __init omap4_local_timer_init(void)
{
@ -619,12 +618,6 @@ void __init omap4_local_timer_init(void)
pr_err("twd_local_timer_register failed %d\n", err);
}
}
#else /* CONFIG_LOCAL_TIMERS */
void __init omap4_local_timer_init(void)
{
omap4_sync32k_timer_init();
}
#endif /* CONFIG_LOCAL_TIMERS */
#endif /* CONFIG_ARCH_OMAP4 */
#ifdef CONFIG_SOC_OMAP5

View File

@ -13,7 +13,7 @@ config REALVIEW_EB_A9MP
depends on MACH_REALVIEW_EB
select CPU_V7
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if LOCAL_TIMERS
select HAVE_ARM_TWD if SMP
select HAVE_SMP
select MIGHT_HAVE_CACHE_L2X0
help
@ -26,7 +26,7 @@ config REALVIEW_EB_ARM11MP
select ARCH_HAS_BARRIERS if SMP
select CPU_V6K
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if LOCAL_TIMERS
select HAVE_ARM_TWD if SMP
select HAVE_SMP
select MIGHT_HAVE_CACHE_L2X0
help
@ -48,7 +48,7 @@ config MACH_REALVIEW_PB11MP
select ARM_GIC
select CPU_V6K
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if LOCAL_TIMERS
select HAVE_ARM_TWD if SMP
select HAVE_PATA_PLATFORM
select HAVE_SMP
select MIGHT_HAVE_CACHE_L2X0
@ -92,7 +92,7 @@ config MACH_REALVIEW_PBX
select ARCH_SPARSEMEM_ENABLE if CPU_V7 && !REALVIEW_HIGH_PHYS_OFFSET
select ARM_GIC
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if LOCAL_TIMERS
select HAVE_ARM_TWD if SMP
select HAVE_PATA_PLATFORM
select HAVE_SMP
select MIGHT_HAVE_CACHE_L2X0

View File

@ -23,7 +23,7 @@ config ARCH_SPEAR13XX
select CPU_V7
select GPIO_SPEAR_SPICS
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if LOCAL_TIMERS
select HAVE_ARM_TWD if SMP
select HAVE_SMP
select MIGHT_HAVE_CACHE_L2X0
select PINCTRL

View File

@ -8,7 +8,7 @@ config ARCH_TEGRA
select COMMON_CLK
select GENERIC_CLOCKEVENTS
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if LOCAL_TIMERS
select HAVE_ARM_TWD if SMP
select HAVE_CLK
select HAVE_SMP
select MIGHT_HAVE_CACHE_L2X0

View File

@ -8,7 +8,7 @@ config ARCH_U8500
select CPU_V7
select GENERIC_CLOCKEVENTS
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if LOCAL_TIMERS
select HAVE_ARM_TWD if SMP
select HAVE_SMP
select MIGHT_HAVE_CACHE_L2X0
help

View File

@ -10,7 +10,7 @@ config ARCH_VEXPRESS
select CPU_V7
select GENERIC_CLOCKEVENTS
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if LOCAL_TIMERS
select HAVE_ARM_TWD if SMP
select HAVE_CLK
select HAVE_PATA_PLATFORM
select HAVE_SMP

View File

@ -6,7 +6,7 @@ config ARCH_ZYNQ
select CPU_V7
select GENERIC_CLOCKEVENTS
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if LOCAL_TIMERS
select HAVE_ARM_TWD if SMP
select ICST
select MIGHT_HAVE_CACHE_L2X0
select USE_OF

View File

@ -16,6 +16,7 @@
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/cpu.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/percpu.h>
@ -24,7 +25,6 @@
#include <linux/of_address.h>
#include <linux/clocksource.h>
#include <asm/localtimer.h>
#include <asm/mach/time.h>
#define EXYNOS4_MCTREG(x) (x)
@ -80,7 +80,7 @@ static unsigned int mct_int_type;
static int mct_irqs[MCT_NR_IRQS];
struct mct_clock_event_device {
struct clock_event_device *evt;
struct clock_event_device evt;
unsigned long base;
char name[10];
};
@ -295,8 +295,6 @@ static void exynos4_clockevent_init(void)
setup_irq(mct_irqs[MCT_G0_IRQ], &mct_comp_event_irq);
}
#ifdef CONFIG_LOCAL_TIMERS
static DEFINE_PER_CPU(struct mct_clock_event_device, percpu_mct_tick);
/* Clock event handling */
@ -369,7 +367,7 @@ static inline void exynos4_tick_set_mode(enum clock_event_mode mode,
static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
{
struct clock_event_device *evt = mevt->evt;
struct clock_event_device *evt = &mevt->evt;
/*
* This is for supporting oneshot mode.
@ -391,7 +389,7 @@ static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id)
{
struct mct_clock_event_device *mevt = dev_id;
struct clock_event_device *evt = mevt->evt;
struct clock_event_device *evt = &mevt->evt;
exynos4_mct_tick_clear(mevt);
@ -405,8 +403,7 @@ static int exynos4_local_timer_setup(struct clock_event_device *evt)
struct mct_clock_event_device *mevt;
unsigned int cpu = smp_processor_id();
mevt = this_cpu_ptr(&percpu_mct_tick);
mevt->evt = evt;
mevt = container_of(evt, struct mct_clock_event_device, evt);
mevt->base = EXYNOS4_MCT_L_BASE(cpu);
sprintf(mevt->name, "mct_tick%d", cpu);
@ -448,14 +445,37 @@ static void exynos4_local_timer_stop(struct clock_event_device *evt)
disable_percpu_irq(mct_irqs[MCT_L0_IRQ]);
}
static struct local_timer_ops exynos4_mct_tick_ops = {
.setup = exynos4_local_timer_setup,
.stop = exynos4_local_timer_stop,
static int exynos4_mct_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
struct mct_clock_event_device *mevt;
/*
* Grab cpu pointer in each case to avoid spurious
* preemptible warnings
*/
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_STARTING:
mevt = this_cpu_ptr(&percpu_mct_tick);
exynos4_local_timer_setup(&mevt->evt);
break;
case CPU_DYING:
mevt = this_cpu_ptr(&percpu_mct_tick);
exynos4_local_timer_stop(&mevt->evt);
break;
}
return NOTIFY_OK;
}
static struct notifier_block exynos4_mct_cpu_nb = {
.notifier_call = exynos4_mct_cpu_notify,
};
#endif /* CONFIG_LOCAL_TIMERS */
static void __init exynos4_timer_resources(struct device_node *np, void __iomem *base)
{
int err;
struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick);
struct clk *mct_clk, *tick_clk;
tick_clk = np ? of_clk_get_by_name(np, "fin_pll") :
@ -473,9 +493,7 @@ static void __init exynos4_timer_resources(struct device_node *np, void __iomem
if (!reg_base)
panic("%s: unable to ioremap mct address space\n", __func__);
#ifdef CONFIG_LOCAL_TIMERS
if (mct_int_type == MCT_INT_PPI) {
int err;
err = request_percpu_irq(mct_irqs[MCT_L0_IRQ],
exynos4_mct_tick_isr, "MCT",
@ -484,8 +502,16 @@ static void __init exynos4_timer_resources(struct device_node *np, void __iomem
mct_irqs[MCT_L0_IRQ], err);
}
local_timer_register(&exynos4_mct_tick_ops);
#endif /* CONFIG_LOCAL_TIMERS */
err = register_cpu_notifier(&exynos4_mct_cpu_nb);
if (err)
goto out_irq;
/* Immediately configure the timer on the boot CPU */
exynos4_local_timer_setup(&mevt->evt);
return;
out_irq:
free_percpu_irq(mct_irqs[MCT_L0_IRQ], &percpu_mct_tick);
}
void __init mct_init(void __iomem *base, int irq_g0, int irq_l0, int irq_l1)

View File

@ -19,6 +19,7 @@
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/cpu.h>
#include <linux/timer.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
@ -28,9 +29,9 @@
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/sched_clock.h>
#include <asm/localtimer.h>
#include <linux/percpu.h>
#include <linux/time-armada-370-xp.h>
/*
* Timer block registers.
*/
@ -69,7 +70,7 @@ static bool timer25Mhz = true;
*/
static u32 ticks_per_jiffy;
static struct clock_event_device __percpu **percpu_armada_370_xp_evt;
static struct clock_event_device __percpu *armada_370_xp_evt;
static u32 notrace armada_370_xp_read_sched_clock(void)
{
@ -142,21 +143,14 @@ armada_370_xp_clkevt_mode(enum clock_event_mode mode,
}
}
static struct clock_event_device armada_370_xp_clkevt = {
.name = "armada_370_xp_per_cpu_tick",
.features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
.shift = 32,
.rating = 300,
.set_next_event = armada_370_xp_clkevt_next_event,
.set_mode = armada_370_xp_clkevt_mode,
};
static int armada_370_xp_clkevt_irq;
static irqreturn_t armada_370_xp_timer_interrupt(int irq, void *dev_id)
{
/*
* ACK timer interrupt and call event handler.
*/
struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
struct clock_event_device *evt = dev_id;
writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS);
evt->event_handler(evt);
@ -172,42 +166,55 @@ static int armada_370_xp_timer_setup(struct clock_event_device *evt)
u32 u;
int cpu = smp_processor_id();
/* Use existing clock_event for cpu 0 */
if (!smp_processor_id())
return 0;
u = readl(local_base + TIMER_CTRL_OFF);
if (timer25Mhz)
writel(u | TIMER0_25MHZ, local_base + TIMER_CTRL_OFF);
else
writel(u & ~TIMER0_25MHZ, local_base + TIMER_CTRL_OFF);
evt->name = armada_370_xp_clkevt.name;
evt->irq = armada_370_xp_clkevt.irq;
evt->features = armada_370_xp_clkevt.features;
evt->shift = armada_370_xp_clkevt.shift;
evt->rating = armada_370_xp_clkevt.rating,
evt->name = "armada_370_xp_per_cpu_tick",
evt->features = CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_PERIODIC;
evt->shift = 32,
evt->rating = 300,
evt->set_next_event = armada_370_xp_clkevt_next_event,
evt->set_mode = armada_370_xp_clkevt_mode,
evt->irq = armada_370_xp_clkevt_irq;
evt->cpumask = cpumask_of(cpu);
*__this_cpu_ptr(percpu_armada_370_xp_evt) = evt;
clockevents_config_and_register(evt, timer_clk, 1, 0xfffffffe);
enable_percpu_irq(evt->irq, 0);
return 0;
}
static void armada_370_xp_timer_stop(struct clock_event_device *evt)
static void armada_370_xp_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 armada_370_xp_local_timer_ops = {
.setup = armada_370_xp_timer_setup,
.stop = armada_370_xp_timer_stop,
static int armada_370_xp_timer_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
/*
* Grab cpu pointer in each case to avoid spurious
* preemptible warnings
*/
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_STARTING:
armada_370_xp_timer_setup(this_cpu_ptr(armada_370_xp_evt));
break;
case CPU_DYING:
armada_370_xp_timer_stop(this_cpu_ptr(armada_370_xp_evt));
break;
}
return NOTIFY_OK;
}
static struct notifier_block armada_370_xp_timer_cpu_nb = {
.notifier_call = armada_370_xp_timer_cpu_notify,
};
void __init armada_370_xp_timer_init(void)
@ -223,9 +230,6 @@ void __init armada_370_xp_timer_init(void)
if (of_find_property(np, "marvell,timer-25Mhz", NULL)) {
/* The fixed 25MHz timer is available so let's use it */
u = readl(local_base + TIMER_CTRL_OFF);
writel(u | TIMER0_25MHZ,
local_base + TIMER_CTRL_OFF);
u = readl(timer_base + TIMER_CTRL_OFF);
writel(u | TIMER0_25MHZ,
timer_base + TIMER_CTRL_OFF);
@ -235,9 +239,6 @@ void __init armada_370_xp_timer_init(void)
struct clk *clk = of_clk_get(np, 0);
WARN_ON(IS_ERR(clk));
rate = clk_get_rate(clk);
u = readl(local_base + TIMER_CTRL_OFF);
writel(u & ~(TIMER0_25MHZ),
local_base + TIMER_CTRL_OFF);
u = readl(timer_base + TIMER_CTRL_OFF);
writel(u & ~(TIMER0_25MHZ),
@ -251,7 +252,7 @@ void __init armada_370_xp_timer_init(void)
* We use timer 0 as clocksource, and private(local) timer 0
* for clockevents
*/
armada_370_xp_clkevt.irq = irq_of_parse_and_map(np, 4);
armada_370_xp_clkevt_irq = irq_of_parse_and_map(np, 4);
ticks_per_jiffy = (timer_clk + HZ / 2) / HZ;
@ -276,26 +277,19 @@ void __init armada_370_xp_timer_init(void)
"armada_370_xp_clocksource",
timer_clk, 300, 32, clocksource_mmio_readl_down);
/* Register the clockevent on the private timer of CPU 0 */
armada_370_xp_clkevt.cpumask = cpumask_of(0);
clockevents_config_and_register(&armada_370_xp_clkevt,
timer_clk, 1, 0xfffffffe);
register_cpu_notifier(&armada_370_xp_timer_cpu_nb);
percpu_armada_370_xp_evt = alloc_percpu(struct clock_event_device *);
armada_370_xp_evt = alloc_percpu(struct clock_event_device);
/*
* Setup clockevent timer (interrupt-driven).
*/
*__this_cpu_ptr(percpu_armada_370_xp_evt) = &armada_370_xp_clkevt;
res = request_percpu_irq(armada_370_xp_clkevt.irq,
res = request_percpu_irq(armada_370_xp_clkevt_irq,
armada_370_xp_timer_interrupt,
armada_370_xp_clkevt.name,
percpu_armada_370_xp_evt);
if (!res) {
enable_percpu_irq(armada_370_xp_clkevt.irq, 0);
#ifdef CONFIG_LOCAL_TIMERS
local_timer_register(&armada_370_xp_local_timer_ops);
#endif
}
"armada_370_xp_per_cpu_tick",
armada_370_xp_evt);
/* Immediately configure the timer on the boot CPU */
if (!res)
armada_370_xp_timer_setup(this_cpu_ptr(armada_370_xp_evt));
}

View File

@ -10,6 +10,7 @@
#include <linux/interrupt.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/cpu.h>
#include <linux/bitops.h>
#include <linux/irq.h>
#include <linux/clk.h>
@ -18,7 +19,6 @@
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/sched_clock.h>
#include <asm/localtimer.h>
#include <asm/mach/time.h>
#define SIRFSOC_TIMER_32COUNTER_0_CTRL 0x0000
@ -151,13 +151,7 @@ static void sirfsoc_clocksource_resume(struct clocksource *cs)
BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
}
static struct clock_event_device sirfsoc_clockevent = {
.name = "sirfsoc_clockevent",
.rating = 200,
.features = CLOCK_EVT_FEAT_ONESHOT,
.set_mode = sirfsoc_timer_set_mode,
.set_next_event = sirfsoc_timer_set_next_event,
};
static struct clock_event_device __percpu *sirfsoc_clockevent;
static struct clocksource sirfsoc_clocksource = {
.name = "sirfsoc_clocksource",
@ -173,11 +167,8 @@ static struct irqaction sirfsoc_timer_irq = {
.name = "sirfsoc_timer0",
.flags = IRQF_TIMER | IRQF_NOBALANCING,
.handler = sirfsoc_timer_interrupt,
.dev_id = &sirfsoc_clockevent,
};
#ifdef CONFIG_LOCAL_TIMERS
static struct irqaction sirfsoc_timer1_irq = {
.name = "sirfsoc_timer1",
.flags = IRQF_TIMER | IRQF_NOBALANCING,
@ -186,24 +177,28 @@ static struct irqaction sirfsoc_timer1_irq = {
static int sirfsoc_local_timer_setup(struct clock_event_device *ce)
{
/* Use existing clock_event for cpu 0 */
if (!smp_processor_id())
return 0;
int cpu = smp_processor_id();
struct irqaction *action;
ce->irq = sirfsoc_timer1_irq.irq;
if (cpu == 0)
action = &sirfsoc_timer_irq;
else
action = &sirfsoc_timer1_irq;
ce->irq = action->irq;
ce->name = "local_timer";
ce->features = sirfsoc_clockevent.features;
ce->rating = sirfsoc_clockevent.rating;
ce->features = CLOCK_EVT_FEAT_ONESHOT;
ce->rating = 200;
ce->set_mode = sirfsoc_timer_set_mode;
ce->set_next_event = sirfsoc_timer_set_next_event;
ce->shift = sirfsoc_clockevent.shift;
ce->mult = sirfsoc_clockevent.mult;
ce->max_delta_ns = sirfsoc_clockevent.max_delta_ns;
ce->min_delta_ns = sirfsoc_clockevent.min_delta_ns;
clockevents_calc_mult_shift(ce, CLOCK_TICK_RATE, 60);
ce->max_delta_ns = clockevent_delta2ns(-2, ce);
ce->min_delta_ns = clockevent_delta2ns(2, ce);
ce->cpumask = cpumask_of(cpu);
sirfsoc_timer1_irq.dev_id = ce;
BUG_ON(setup_irq(ce->irq, &sirfsoc_timer1_irq));
irq_set_affinity(sirfsoc_timer1_irq.irq, cpumask_of(1));
action->dev_id = ce;
BUG_ON(setup_irq(ce->irq, action));
irq_set_affinity(action->irq, cpumask_of(cpu));
clockevents_register_device(ce);
return 0;
@ -211,31 +206,48 @@ static int sirfsoc_local_timer_setup(struct clock_event_device *ce)
static void sirfsoc_local_timer_stop(struct clock_event_device *ce)
{
int cpu = smp_processor_id();
sirfsoc_timer_count_disable(1);
remove_irq(sirfsoc_timer1_irq.irq, &sirfsoc_timer1_irq);
if (cpu == 0)
remove_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq);
else
remove_irq(sirfsoc_timer1_irq.irq, &sirfsoc_timer1_irq);
}
static struct local_timer_ops sirfsoc_local_timer_ops = {
.setup = sirfsoc_local_timer_setup,
.stop = sirfsoc_local_timer_stop,
static int sirfsoc_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
/*
* Grab cpu pointer in each case to avoid spurious
* preemptible warnings
*/
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_STARTING:
sirfsoc_local_timer_setup(this_cpu_ptr(sirfsoc_clockevent));
break;
case CPU_DYING:
sirfsoc_local_timer_stop(this_cpu_ptr(sirfsoc_clockevent));
break;
}
return NOTIFY_OK;
}
static struct notifier_block sirfsoc_cpu_nb = {
.notifier_call = sirfsoc_cpu_notify,
};
#endif /* CONFIG_LOCAL_TIMERS */
static void __init sirfsoc_clockevent_init(void)
{
clockevents_calc_mult_shift(&sirfsoc_clockevent, CLOCK_TICK_RATE, 60);
sirfsoc_clockevent = alloc_percpu(struct clock_event_device);
BUG_ON(!sirfsoc_clockevent);
sirfsoc_clockevent.max_delta_ns =
clockevent_delta2ns(-2, &sirfsoc_clockevent);
sirfsoc_clockevent.min_delta_ns =
clockevent_delta2ns(2, &sirfsoc_clockevent);
BUG_ON(register_cpu_notifier(&sirfsoc_cpu_nb));
sirfsoc_clockevent.cpumask = cpumask_of(0);
clockevents_register_device(&sirfsoc_clockevent);
#ifdef CONFIG_LOCAL_TIMERS
local_timer_register(&sirfsoc_local_timer_ops);
#endif
/* Immediately configure the timer on the boot CPU */
sirfsoc_local_timer_setup(this_cpu_ptr(sirfsoc_clockevent));
}
/* initialize the kernel jiffy timer source */
@ -273,8 +285,6 @@ static void __init sirfsoc_marco_timer_init(void)
BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE));
BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq));
sirfsoc_clockevent_init();
}
@ -288,11 +298,9 @@ static void __init sirfsoc_of_timer_init(struct device_node *np)
if (!sirfsoc_timer_irq.irq)
panic("No irq passed for timer0 via DT\n");
#ifdef CONFIG_LOCAL_TIMERS
sirfsoc_timer1_irq.irq = irq_of_parse_and_map(np, 1);
if (!sirfsoc_timer1_irq.irq)
panic("No irq passed for timer1 via DT\n");
#endif
sirfsoc_marco_timer_init();
}

View File

@ -11,8 +11,6 @@
#ifndef __TIME_ARMADA_370_XPPRCMU_H
#define __TIME_ARMADA_370_XPPRCMU_H
#include <linux/init.h>
void __init armada_370_xp_timer_init(void);
void armada_370_xp_timer_init(void);
#endif