[ARM] smp: allow re-use of realview localtimer TWD support
authorRussell King <rmk@dyn-67.arm.linux.org.uk>
Sat, 16 May 2009 11:14:21 +0000 (12:14 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Sun, 17 May 2009 18:16:41 +0000 (19:16 +0100)
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/Kconfig
arch/arm/include/asm/hardware/arm_twd.h [deleted file]
arch/arm/include/asm/localtimer.h
arch/arm/include/asm/smp_twd.h [new file with mode: 0644]
arch/arm/kernel/Makefile
arch/arm/kernel/smp_twd.c [new file with mode: 0644]
arch/arm/mach-realview/Makefile
arch/arm/mach-realview/core.h
arch/arm/mach-realview/localtimer.c
arch/arm/mach-realview/realview_eb.c
arch/arm/mach-realview/realview_pb11mp.c

index f19a951..93d63bf 100644 (file)
@@ -885,6 +885,12 @@ config HAVE_ARM_SCU
        help
          This option enables support for the ARM system coherency unit
 
+config HAVE_ARM_TWD
+       bool
+       depends on SMP
+       help
+         This options enables support for the ARM timer and watchdog unit
+
 choice
        prompt "Memory split"
        default VMSPLIT_3G
@@ -925,6 +931,7 @@ config LOCAL_TIMERS
        bool "Use local timer interrupts"
        depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || REALVIEW_EB_A9MP)
        default y
+       select HAVE_ARM_TWD if ARCH_REALVIEW
        help
          Enable support for local timers on SMP platforms, rather then the
          legacy IPI broadcast method.  Local timers allows the system
diff --git a/arch/arm/include/asm/hardware/arm_twd.h b/arch/arm/include/asm/hardware/arm_twd.h
deleted file mode 100644 (file)
index e521b70..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef __ASM_HARDWARE_TWD_H
-#define __ASM_HARDWARE_TWD_H
-
-#define TWD_TIMER_LOAD                         0x00
-#define TWD_TIMER_COUNTER              0x04
-#define TWD_TIMER_CONTROL              0x08
-#define TWD_TIMER_INTSTAT              0x0C
-
-#define TWD_WDOG_LOAD                  0x20
-#define TWD_WDOG_COUNTER               0x24
-#define TWD_WDOG_CONTROL               0x28
-#define TWD_WDOG_INTSTAT               0x2C
-#define TWD_WDOG_RESETSTAT             0x30
-#define TWD_WDOG_DISABLE               0x34
-
-#define TWD_TIMER_CONTROL_ENABLE       (1 << 0)
-#define TWD_TIMER_CONTROL_ONESHOT      (0 << 1)
-#define TWD_TIMER_CONTROL_PERIODIC     (1 << 1)
-#define TWD_TIMER_CONTROL_IT_ENABLE    (1 << 2)
-
-#endif
index 3f8c9eb..50c7e7c 100644 (file)
@@ -24,6 +24,16 @@ asmlinkage void do_local_timer(struct pt_regs *);
 
 
 #ifdef CONFIG_LOCAL_TIMERS
+
+#ifdef CONFIG_HAVE_ARM_TWD
+
+#include "smp_twd.h"
+
+#define local_timer_ack()      twd_timer_ack()
+#define local_timer_stop()     twd_timer_stop()
+
+#else
+
 /*
  * Platform provides this to acknowledge a local timer IRQ.
  * Returns true if the local timer IRQ is to be processed.
@@ -35,6 +45,8 @@ int local_timer_ack(void);
  */
 void local_timer_stop(void);
 
+#endif
+
 /*
  * Setup a local timer interrupt for a CPU.
  */
diff --git a/arch/arm/include/asm/smp_twd.h b/arch/arm/include/asm/smp_twd.h
new file mode 100644 (file)
index 0000000..7be0978
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __ASMARM_SMP_TWD_H
+#define __ASMARM_SMP_TWD_H
+
+struct clock_event_device;
+
+extern void __iomem *twd_base;
+
+void twd_timer_stop(void);
+int twd_timer_ack(void);
+void twd_timer_setup(struct clock_event_device *);
+
+#endif
index 90ffbaf..ff89d0b 100644 (file)
@@ -23,6 +23,7 @@ obj-$(CONFIG_ISA_DMA)         += dma-isa.o
 obj-$(CONFIG_PCI)              += bios32.o isa.o
 obj-$(CONFIG_SMP)              += smp.o
 obj-$(CONFIG_HAVE_ARM_SCU)     += smp_scu.o
+obj-$(CONFIG_HAVE_ARM_TWD)     += smp_twd.o
 obj-$(CONFIG_DYNAMIC_FTRACE)   += ftrace.o
 obj-$(CONFIG_KEXEC)            += machine_kexec.o relocate_kernel.o
 obj-$(CONFIG_KPROBES)          += kprobes.o kprobes-decode.o
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
new file mode 100644 (file)
index 0000000..aabd62d
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ *  linux/arch/arm/kernel/smp_twd.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/kernel.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/smp.h>
+#include <linux/jiffies.h>
+#include <linux/clockchips.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <asm/smp_twd.h>
+#include <asm/hardware/gic.h>
+
+#define TWD_TIMER_LOAD                         0x00
+#define TWD_TIMER_COUNTER              0x04
+#define TWD_TIMER_CONTROL              0x08
+#define TWD_TIMER_INTSTAT              0x0C
+
+#define TWD_WDOG_LOAD                  0x20
+#define TWD_WDOG_COUNTER               0x24
+#define TWD_WDOG_CONTROL               0x28
+#define TWD_WDOG_INTSTAT               0x2C
+#define TWD_WDOG_RESETSTAT             0x30
+#define TWD_WDOG_DISABLE               0x34
+
+#define TWD_TIMER_CONTROL_ENABLE       (1 << 0)
+#define TWD_TIMER_CONTROL_ONESHOT      (0 << 1)
+#define TWD_TIMER_CONTROL_PERIODIC     (1 << 1)
+#define TWD_TIMER_CONTROL_IT_ENABLE    (1 << 2)
+
+/* set up by the platform code */
+void __iomem *twd_base;
+
+static unsigned long twd_timer_rate;
+
+static void twd_set_mode(enum clock_event_mode mode,
+                       struct clock_event_device *clk)
+{
+       unsigned long ctrl;
+
+       switch(mode) {
+       case CLOCK_EVT_MODE_PERIODIC:
+               /* timer load already set up */
+               ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE
+                       | TWD_TIMER_CONTROL_PERIODIC;
+               break;
+       case CLOCK_EVT_MODE_ONESHOT:
+               /* period set, and timer enabled in 'next_event' hook */
+               ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT;
+               break;
+       case CLOCK_EVT_MODE_UNUSED:
+       case CLOCK_EVT_MODE_SHUTDOWN:
+       default:
+               ctrl = 0;
+       }
+
+       __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
+}
+
+static int twd_set_next_event(unsigned long evt,
+                       struct clock_event_device *unused)
+{
+       unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL);
+
+       __raw_writel(evt, twd_base + TWD_TIMER_COUNTER);
+       __raw_writel(ctrl | TWD_TIMER_CONTROL_ENABLE, twd_base + TWD_TIMER_CONTROL);
+
+       return 0;
+}
+
+/*
+ * local_timer_ack: checks for a local timer interrupt.
+ *
+ * If a local timer interrupt has occurred, acknowledge and return 1.
+ * Otherwise, return 0.
+ */
+int twd_timer_ack(void)
+{
+       if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) {
+               __raw_writel(1, twd_base + TWD_TIMER_INTSTAT);
+               return 1;
+       }
+
+       return 0;
+}
+
+static void __cpuinit twd_calibrate_rate(void)
+{
+       unsigned long load, count;
+       u64 waitjiffies;
+
+       /*
+        * If this is the first time round, we need to work out how fast
+        * the timer ticks
+        */
+       if (twd_timer_rate == 0) {
+               printk("Calibrating local timer... ");
+
+               /* Wait for a tick to start */
+               waitjiffies = get_jiffies_64() + 1;
+
+               while (get_jiffies_64() < waitjiffies)
+                       udelay(10);
+
+               /* OK, now the tick has started, let's get the timer going */
+               waitjiffies += 5;
+
+                                /* enable, no interrupt or reload */
+               __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL);
+
+                                /* maximum value */
+               __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
+
+               while (get_jiffies_64() < waitjiffies)
+                       udelay(10);
+
+               count = __raw_readl(twd_base + TWD_TIMER_COUNTER);
+
+               twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
+
+               printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000,
+                       (twd_timer_rate / 100000) % 100);
+       }
+
+       load = twd_timer_rate / HZ;
+
+       __raw_writel(load, twd_base + TWD_TIMER_LOAD);
+}
+
+/*
+ * Setup the local clock events for a CPU.
+ */
+void __cpuinit twd_timer_setup(struct clock_event_device *clk)
+{
+       unsigned long flags;
+
+       twd_calibrate_rate();
+
+       clk->name               = "local_timer";
+       clk->features           = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+       clk->rating             = 350;
+       clk->set_mode           = twd_set_mode;
+       clk->set_next_event     = twd_set_next_event;
+       clk->shift              = 20;
+       clk->mult               = div_sc(twd_timer_rate, NSEC_PER_SEC, clk->shift);
+       clk->max_delta_ns       = clockevent_delta2ns(0xffffffff, clk);
+       clk->min_delta_ns       = clockevent_delta2ns(0xf, clk);
+
+       /* Make sure our local interrupt controller has this enabled */
+       local_irq_save(flags);
+       get_irq_chip(clk->irq)->unmask(clk->irq);
+       local_irq_restore(flags);
+
+       clockevents_register_device(clk);
+}
+
+/*
+ * take a local timer down
+ */
+void __cpuexit twd_timer_stop(void)
+{
+       __raw_writel(0, twd_base + TWD_TIMER_CONTROL);
+}
index 7bea8ff..e13d094 100644 (file)
@@ -7,5 +7,6 @@ obj-$(CONFIG_MACH_REALVIEW_EB)          += realview_eb.o
 obj-$(CONFIG_MACH_REALVIEW_PB11MP)     += realview_pb11mp.o
 obj-$(CONFIG_MACH_REALVIEW_PB1176)     += realview_pb1176.o
 obj-$(CONFIG_MACH_REALVIEW_PBA8)       += realview_pba8.o
-obj-$(CONFIG_SMP)                      += platsmp.o headsmp.o localtimer.o
+obj-$(CONFIG_SMP)                      += platsmp.o headsmp.o
 obj-$(CONFIG_HOTPLUG_CPU)              += hotplug.o
+obj-$(CONFIG_LOCAL_TIMERS)             += localtimer.o
index 21c0863..59a337b 100644 (file)
@@ -51,9 +51,6 @@ extern struct mmc_platform_data realview_mmc0_plat_data;
 extern struct mmc_platform_data realview_mmc1_plat_data;
 extern struct clcd_board clcd_plat_data;
 extern void __iomem *gic_cpu_base_addr;
-#ifdef CONFIG_LOCAL_TIMERS
-extern void __iomem *twd_base;
-#endif
 extern void __iomem *timer0_va_base;
 extern void __iomem *timer1_va_base;
 extern void __iomem *timer2_va_base;
index cd98e7a..60b4e11 100644 (file)
  * published by the Free Software Foundation.
  */
 #include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
 #include <linux/smp.h>
-#include <linux/jiffies.h>
 #include <linux/clockchips.h>
-#include <linux/irq.h>
-#include <linux/io.h>
 
-#include <asm/hardware/arm_twd.h>
-#include <asm/hardware/gic.h>
-#include <mach/hardware.h>
 #include <asm/irq.h>
-
-#ifdef CONFIG_LOCAL_TIMERS
-
-/* set up by the platform code */
-void __iomem *twd_base;
-
-static unsigned long mpcore_timer_rate;
-
-static void local_timer_set_mode(enum clock_event_mode mode,
-                                struct clock_event_device *evt)
-{
-       unsigned long ctrl;
-
-       switch(mode) {
-       case CLOCK_EVT_MODE_PERIODIC:
-               /* timer load already set up */
-               ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE
-                       | TWD_TIMER_CONTROL_PERIODIC;
-               break;
-       case CLOCK_EVT_MODE_ONESHOT:
-               /* period set, and timer enabled in 'next_event' hook */
-               ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT;
-               break;
-       case CLOCK_EVT_MODE_UNUSED:
-       case CLOCK_EVT_MODE_SHUTDOWN:
-       default:
-               ctrl = 0;
-       }
-
-       __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
-}
-
-static int local_timer_set_next_event(unsigned long evt,
-                                     struct clock_event_device *unused)
-{
-       unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL);
-
-       __raw_writel(evt, twd_base + TWD_TIMER_COUNTER);
-       __raw_writel(ctrl | TWD_TIMER_CONTROL_ENABLE, twd_base + TWD_TIMER_CONTROL);
-
-       return 0;
-}
-
-/*
- * local_timer_ack: checks for a local timer interrupt.
- *
- * If a local timer interrupt has occurred, acknowledge and return 1.
- * Otherwise, return 0.
- */
-int local_timer_ack(void)
-{
-       if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) {
-               __raw_writel(1, twd_base + TWD_TIMER_INTSTAT);
-               return 1;
-       }
-
-       return 0;
-}
-
-static void __cpuinit twd_calibrate_rate(void)
-{
-       unsigned long load, count;
-       u64 waitjiffies;
-
-       /*
-        * If this is the first time round, we need to work out how fast
-        * the timer ticks
-        */
-       if (mpcore_timer_rate == 0) {
-               printk("Calibrating local timer... ");
-
-               /* Wait for a tick to start */
-               waitjiffies = get_jiffies_64() + 1;
-
-               while (get_jiffies_64() < waitjiffies)
-                       udelay(10);
-
-               /* OK, now the tick has started, let's get the timer going */
-               waitjiffies += 5;
-
-                                /* enable, no interrupt or reload */
-               __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL);
-
-                                /* maximum value */
-               __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
-
-               while (get_jiffies_64() < waitjiffies)
-                       udelay(10);
-
-               count = __raw_readl(twd_base + TWD_TIMER_COUNTER);
-
-               mpcore_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
-
-               printk("%lu.%02luMHz.\n", mpcore_timer_rate / 1000000,
-                       (mpcore_timer_rate / 100000) % 100);
-       }
-
-       load = mpcore_timer_rate / HZ;
-
-       __raw_writel(load, twd_base + TWD_TIMER_LOAD);
-}
+#include <asm/smp_twd.h>
+#include <asm/localtimer.h>
 
 /*
  * Setup the local clock events for a CPU.
  */
 void __cpuinit local_timer_setup(struct clock_event_device *evt)
 {
-       unsigned long flags;
-
-       twd_calibrate_rate();
-
-       evt->name               = "local_timer";
-       evt->features           = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
-       evt->rating             = 350;
-       evt->set_mode           = local_timer_set_mode;
-       evt->set_next_event     = local_timer_set_next_event;
-       evt->irq                = IRQ_LOCALTIMER;
-       evt->shift              = 20;
-       evt->mult               = div_sc(mpcore_timer_rate, NSEC_PER_SEC, evt->shift);
-       evt->max_delta_ns       = clockevent_delta2ns(0xffffffff, evt);
-       evt->min_delta_ns       = clockevent_delta2ns(0xf, evt);
-
-       /* Make sure our local interrupt controller has this enabled */
-       local_irq_save(flags);
-       get_irq_chip(IRQ_LOCALTIMER)->unmask(IRQ_LOCALTIMER);
-       local_irq_restore(flags);
-
-       clockevents_register_device(evt);
+       evt->irq = IRQ_LOCALTIMER;
+       twd_timer_setup(evt);
 }
-
-/*
- * take a local timer down
- */
-void __cpuexit local_timer_stop(void)
-{
-       __raw_writel(0, twd_base + TWD_TIMER_CONTROL);
-}
-
-#endif /* CONFIG_LOCAL_TIMERS */
index c20fbef..8dfa44e 100644 (file)
@@ -32,6 +32,7 @@
 #include <asm/hardware/gic.h>
 #include <asm/hardware/icst307.h>
 #include <asm/hardware/cache-l2x0.h>
+#include <asm/localtimer.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
index ea1e60e..dc4b169 100644 (file)
@@ -32,6 +32,7 @@
 #include <asm/hardware/gic.h>
 #include <asm/hardware/icst307.h>
 #include <asm/hardware/cache-l2x0.h>
+#include <asm/localtimer.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/flash.h>