Skip to content

Commit

Permalink
Merge branch 'local_timers-for-arm-soc' of git://git.kernel.org/pub/s…
Browse files Browse the repository at this point in the history
…cm/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
  • Loading branch information
Arnd Bergmann committed Mar 13, 2012
2 parents a282114 + d457859 commit 648ab3d
Show file tree
Hide file tree
Showing 38 changed files with 407 additions and 411 deletions.
48 changes: 48 additions & 0 deletions Documentation/devicetree/bindings/arm/twd.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
* 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>;
};
8 changes: 4 additions & 4 deletions arch/arm/boot/dts/highbank.dts
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,15 @@
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 {
Expand Down
6 changes: 3 additions & 3 deletions arch/arm/boot/dts/imx6q.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@
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 {
Expand Down
37 changes: 7 additions & 30 deletions arch/arm/include/asm/localtimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
25 changes: 21 additions & 4 deletions arch/arm/include/asm/smp_twd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
22 changes: 19 additions & 3 deletions arch/arm/kernel/smp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -459,15 +461,28 @@ 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);

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);
}

Expand All @@ -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

Expand Down
123 changes: 100 additions & 23 deletions arch/arm/kernel/smp_twd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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();

Expand All @@ -260,11 +245,103 @@ 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;

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
Loading

0 comments on commit 648ab3d

Please sign in to comment.