mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-05 11:54:01 +08:00
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:
commit
47dcd3563e
@ -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.
|
||||
|
@ -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
|
@ -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);
|
||||
|
||||
/*
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user