Browse Source

[ARM] smp: allow re-use of realview localtimer TWD support

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Russell King 16 years ago
parent
commit
f32f4ce257

+ 7 - 0
arch/arm/Kconfig

@@ -885,6 +885,12 @@ config HAVE_ARM_SCU
 	help
 	help
 	  This option enables support for the ARM system coherency unit
 	  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
 choice
 	prompt "Memory split"
 	prompt "Memory split"
 	default VMSPLIT_3G
 	default VMSPLIT_3G
@@ -925,6 +931,7 @@ config LOCAL_TIMERS
 	bool "Use local timer interrupts"
 	bool "Use local timer interrupts"
 	depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || REALVIEW_EB_A9MP)
 	depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || REALVIEW_EB_A9MP)
 	default y
 	default y
+	select HAVE_ARM_TWD if ARCH_REALVIEW
 	help
 	help
 	  Enable support for local timers on SMP platforms, rather then the
 	  Enable support for local timers on SMP platforms, rather then the
 	  legacy IPI broadcast method.  Local timers allows the system
 	  legacy IPI broadcast method.  Local timers allows the system

+ 0 - 21
arch/arm/include/asm/hardware/arm_twd.h

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

+ 12 - 0
arch/arm/include/asm/localtimer.h

@@ -24,6 +24,16 @@ asmlinkage void do_local_timer(struct pt_regs *);
 
 
 
 
 #ifdef CONFIG_LOCAL_TIMERS
 #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.
  * Platform provides this to acknowledge a local timer IRQ.
  * Returns true if the local timer IRQ is to be processed.
  * 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);
 void local_timer_stop(void);
 
 
+#endif
+
 /*
 /*
  * Setup a local timer interrupt for a CPU.
  * Setup a local timer interrupt for a CPU.
  */
  */

+ 12 - 0
arch/arm/include/asm/smp_twd.h

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

+ 1 - 0
arch/arm/kernel/Makefile

@@ -23,6 +23,7 @@ obj-$(CONFIG_ISA_DMA)		+= dma-isa.o
 obj-$(CONFIG_PCI)		+= bios32.o isa.o
 obj-$(CONFIG_PCI)		+= bios32.o isa.o
 obj-$(CONFIG_SMP)		+= smp.o
 obj-$(CONFIG_SMP)		+= smp.o
 obj-$(CONFIG_HAVE_ARM_SCU)	+= smp_scu.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_DYNAMIC_FTRACE)	+= ftrace.o
 obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
 obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
 obj-$(CONFIG_KPROBES)		+= kprobes.o kprobes-decode.o
 obj-$(CONFIG_KPROBES)		+= kprobes.o kprobes-decode.o

+ 173 - 0
arch/arm/kernel/smp_twd.c

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

+ 2 - 1
arch/arm/mach-realview/Makefile

@@ -7,5 +7,6 @@ obj-$(CONFIG_MACH_REALVIEW_EB)		+= realview_eb.o
 obj-$(CONFIG_MACH_REALVIEW_PB11MP)	+= realview_pb11mp.o
 obj-$(CONFIG_MACH_REALVIEW_PB11MP)	+= realview_pb11mp.o
 obj-$(CONFIG_MACH_REALVIEW_PB1176)	+= realview_pb1176.o
 obj-$(CONFIG_MACH_REALVIEW_PB1176)	+= realview_pb1176.o
 obj-$(CONFIG_MACH_REALVIEW_PBA8)	+= realview_pba8.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_HOTPLUG_CPU)		+= hotplug.o
+obj-$(CONFIG_LOCAL_TIMERS)		+= localtimer.o

+ 0 - 3
arch/arm/mach-realview/core.h

@@ -51,9 +51,6 @@ extern struct mmc_platform_data realview_mmc0_plat_data;
 extern struct mmc_platform_data realview_mmc1_plat_data;
 extern struct mmc_platform_data realview_mmc1_plat_data;
 extern struct clcd_board clcd_plat_data;
 extern struct clcd_board clcd_plat_data;
 extern void __iomem *gic_cpu_base_addr;
 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 *timer0_va_base;
 extern void __iomem *timer1_va_base;
 extern void __iomem *timer1_va_base;
 extern void __iomem *timer2_va_base;
 extern void __iomem *timer2_va_base;

+ 4 - 140
arch/arm/mach-realview/localtimer.c

@@ -9,154 +9,18 @@
  * published by the Free Software Foundation.
  * published by the Free Software Foundation.
  */
  */
 #include <linux/init.h>
 #include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
 #include <linux/smp.h>
 #include <linux/smp.h>
-#include <linux/jiffies.h>
 #include <linux/clockchips.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>
 #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.
  * Setup the local clock events for a CPU.
  */
  */
 void __cpuinit local_timer_setup(struct clock_event_device *evt)
 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 */

+ 1 - 0
arch/arm/mach-realview/realview_eb.c

@@ -32,6 +32,7 @@
 #include <asm/hardware/gic.h>
 #include <asm/hardware/gic.h>
 #include <asm/hardware/icst307.h>
 #include <asm/hardware/icst307.h>
 #include <asm/hardware/cache-l2x0.h>
 #include <asm/hardware/cache-l2x0.h>
+#include <asm/localtimer.h>
 
 
 #include <asm/mach/arch.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 #include <asm/mach/map.h>

+ 1 - 0
arch/arm/mach-realview/realview_pb11mp.c

@@ -32,6 +32,7 @@
 #include <asm/hardware/gic.h>
 #include <asm/hardware/gic.h>
 #include <asm/hardware/icst307.h>
 #include <asm/hardware/icst307.h>
 #include <asm/hardware/cache-l2x0.h>
 #include <asm/hardware/cache-l2x0.h>
+#include <asm/localtimer.h>
 
 
 #include <asm/mach/arch.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/flash.h>
 #include <asm/mach/flash.h>