aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorRussell King <rmk@dyn-67.arm.linux.org.uk>2009-05-16 07:14:21 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2009-05-17 14:16:41 -0400
commitf32f4ce25745209f16a5a6cef7442144b596c68a (patch)
tree76083f10ee56c82177669bb100358ea2818f8f5a /arch/arm
parenta8cbcd92bd4bf893085eddf7f58e63ea98503d94 (diff)
[ARM] smp: allow re-use of realview localtimer TWD support
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/Kconfig7
-rw-r--r--arch/arm/include/asm/hardware/arm_twd.h21
-rw-r--r--arch/arm/include/asm/localtimer.h12
-rw-r--r--arch/arm/include/asm/smp_twd.h12
-rw-r--r--arch/arm/kernel/Makefile1
-rw-r--r--arch/arm/kernel/smp_twd.c173
-rw-r--r--arch/arm/mach-realview/Makefile3
-rw-r--r--arch/arm/mach-realview/core.h3
-rw-r--r--arch/arm/mach-realview/localtimer.c144
-rw-r--r--arch/arm/mach-realview/realview_eb.c1
-rw-r--r--arch/arm/mach-realview/realview_pb11mp.c1
11 files changed, 213 insertions, 165 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index f19a9519a723..93d63bf927d5 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -885,6 +885,12 @@ config HAVE_ARM_SCU
885 help 885 help
886 This option enables support for the ARM system coherency unit 886 This option enables support for the ARM system coherency unit
887 887
888config HAVE_ARM_TWD
889 bool
890 depends on SMP
891 help
892 This options enables support for the ARM timer and watchdog unit
893
888choice 894choice
889 prompt "Memory split" 895 prompt "Memory split"
890 default VMSPLIT_3G 896 default VMSPLIT_3G
@@ -925,6 +931,7 @@ config LOCAL_TIMERS
925 bool "Use local timer interrupts" 931 bool "Use local timer interrupts"
926 depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || REALVIEW_EB_A9MP) 932 depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || REALVIEW_EB_A9MP)
927 default y 933 default y
934 select HAVE_ARM_TWD if ARCH_REALVIEW
928 help 935 help
929 Enable support for local timers on SMP platforms, rather then the 936 Enable support for local timers on SMP platforms, rather then the
930 legacy IPI broadcast method. Local timers allows the system 937 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
index e521b70713c8..000000000000
--- a/arch/arm/include/asm/hardware/arm_twd.h
+++ /dev/null
@@ -1,21 +0,0 @@
1#ifndef __ASM_HARDWARE_TWD_H
2#define __ASM_HARDWARE_TWD_H
3
4#define TWD_TIMER_LOAD 0x00
5#define TWD_TIMER_COUNTER 0x04
6#define TWD_TIMER_CONTROL 0x08
7#define TWD_TIMER_INTSTAT 0x0C
8
9#define TWD_WDOG_LOAD 0x20
10#define TWD_WDOG_COUNTER 0x24
11#define TWD_WDOG_CONTROL 0x28
12#define TWD_WDOG_INTSTAT 0x2C
13#define TWD_WDOG_RESETSTAT 0x30
14#define TWD_WDOG_DISABLE 0x34
15
16#define TWD_TIMER_CONTROL_ENABLE (1 << 0)
17#define TWD_TIMER_CONTROL_ONESHOT (0 << 1)
18#define TWD_TIMER_CONTROL_PERIODIC (1 << 1)
19#define TWD_TIMER_CONTROL_IT_ENABLE (1 << 2)
20
21#endif
diff --git a/arch/arm/include/asm/localtimer.h b/arch/arm/include/asm/localtimer.h
index 3f8c9ebb646c..50c7e7cfd670 100644
--- a/arch/arm/include/asm/localtimer.h
+++ b/arch/arm/include/asm/localtimer.h
@@ -24,6 +24,16 @@ asmlinkage void do_local_timer(struct pt_regs *);
24 24
25 25
26#ifdef CONFIG_LOCAL_TIMERS 26#ifdef CONFIG_LOCAL_TIMERS
27
28#ifdef CONFIG_HAVE_ARM_TWD
29
30#include "smp_twd.h"
31
32#define local_timer_ack() twd_timer_ack()
33#define local_timer_stop() twd_timer_stop()
34
35#else
36
27/* 37/*
28 * Platform provides this to acknowledge a local timer IRQ. 38 * Platform provides this to acknowledge a local timer IRQ.
29 * Returns true if the local timer IRQ is to be processed. 39 * Returns true if the local timer IRQ is to be processed.
@@ -35,6 +45,8 @@ int local_timer_ack(void);
35 */ 45 */
36void local_timer_stop(void); 46void local_timer_stop(void);
37 47
48#endif
49
38/* 50/*
39 * Setup a local timer interrupt for a CPU. 51 * Setup a local timer interrupt for a CPU.
40 */ 52 */
diff --git a/arch/arm/include/asm/smp_twd.h b/arch/arm/include/asm/smp_twd.h
new file mode 100644
index 000000000000..7be0978b2625
--- /dev/null
+++ b/arch/arm/include/asm/smp_twd.h
@@ -0,0 +1,12 @@
1#ifndef __ASMARM_SMP_TWD_H
2#define __ASMARM_SMP_TWD_H
3
4struct clock_event_device;
5
6extern void __iomem *twd_base;
7
8void twd_timer_stop(void);
9int twd_timer_ack(void);
10void twd_timer_setup(struct clock_event_device *);
11
12#endif
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 90ffbaf23b4e..ff89d0b3abc5 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_ISA_DMA) += dma-isa.o
23obj-$(CONFIG_PCI) += bios32.o isa.o 23obj-$(CONFIG_PCI) += bios32.o isa.o
24obj-$(CONFIG_SMP) += smp.o 24obj-$(CONFIG_SMP) += smp.o
25obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o 25obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o
26obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o
26obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o 27obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
27obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o 28obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
28obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o 29obj-$(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
index 000000000000..aabd62d6bd19
--- /dev/null
+++ b/arch/arm/kernel/smp_twd.c
@@ -0,0 +1,173 @@
1/*
2 * linux/arch/arm/kernel/smp_twd.c
3 *
4 * Copyright (C) 2002 ARM Ltd.
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11#include <linux/init.h>
12#include <linux/kernel.h>
13#include <linux/delay.h>
14#include <linux/device.h>
15#include <linux/smp.h>
16#include <linux/jiffies.h>
17#include <linux/clockchips.h>
18#include <linux/irq.h>
19#include <linux/io.h>
20
21#include <asm/smp_twd.h>
22#include <asm/hardware/gic.h>
23
24#define TWD_TIMER_LOAD 0x00
25#define TWD_TIMER_COUNTER 0x04
26#define TWD_TIMER_CONTROL 0x08
27#define TWD_TIMER_INTSTAT 0x0C
28
29#define TWD_WDOG_LOAD 0x20
30#define TWD_WDOG_COUNTER 0x24
31#define TWD_WDOG_CONTROL 0x28
32#define TWD_WDOG_INTSTAT 0x2C
33#define TWD_WDOG_RESETSTAT 0x30
34#define TWD_WDOG_DISABLE 0x34
35
36#define TWD_TIMER_CONTROL_ENABLE (1 << 0)
37#define TWD_TIMER_CONTROL_ONESHOT (0 << 1)
38#define TWD_TIMER_CONTROL_PERIODIC (1 << 1)
39#define TWD_TIMER_CONTROL_IT_ENABLE (1 << 2)
40
41/* set up by the platform code */
42void __iomem *twd_base;
43
44static unsigned long twd_timer_rate;
45
46static void twd_set_mode(enum clock_event_mode mode,
47 struct clock_event_device *clk)
48{
49 unsigned long ctrl;
50
51 switch(mode) {
52 case CLOCK_EVT_MODE_PERIODIC:
53 /* timer load already set up */
54 ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE
55 | TWD_TIMER_CONTROL_PERIODIC;
56 break;
57 case CLOCK_EVT_MODE_ONESHOT:
58 /* period set, and timer enabled in 'next_event' hook */
59 ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT;
60 break;
61 case CLOCK_EVT_MODE_UNUSED:
62 case CLOCK_EVT_MODE_SHUTDOWN:
63 default:
64 ctrl = 0;
65 }
66
67 __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
68}
69
70static int twd_set_next_event(unsigned long evt,
71 struct clock_event_device *unused)
72{
73 unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL);
74
75 __raw_writel(evt, twd_base + TWD_TIMER_COUNTER);
76 __raw_writel(ctrl | TWD_TIMER_CONTROL_ENABLE, twd_base + TWD_TIMER_CONTROL);
77
78 return 0;
79}
80
81/*
82 * local_timer_ack: checks for a local timer interrupt.
83 *
84 * If a local timer interrupt has occurred, acknowledge and return 1.
85 * Otherwise, return 0.
86 */
87int twd_timer_ack(void)
88{
89 if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) {
90 __raw_writel(1, twd_base + TWD_TIMER_INTSTAT);
91 return 1;
92 }
93
94 return 0;
95}
96
97static void __cpuinit twd_calibrate_rate(void)
98{
99 unsigned long load, count;
100 u64 waitjiffies;
101
102 /*
103 * If this is the first time round, we need to work out how fast
104 * the timer ticks
105 */
106 if (twd_timer_rate == 0) {
107 printk("Calibrating local timer... ");
108
109 /* Wait for a tick to start */
110 waitjiffies = get_jiffies_64() + 1;
111
112 while (get_jiffies_64() < waitjiffies)
113 udelay(10);
114
115 /* OK, now the tick has started, let's get the timer going */
116 waitjiffies += 5;
117
118 /* enable, no interrupt or reload */
119 __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL);
120
121 /* maximum value */
122 __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
123
124 while (get_jiffies_64() < waitjiffies)
125 udelay(10);
126
127 count = __raw_readl(twd_base + TWD_TIMER_COUNTER);
128
129 twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
130
131 printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000,
132 (twd_timer_rate / 100000) % 100);
133 }
134
135 load = twd_timer_rate / HZ;
136
137 __raw_writel(load, twd_base + TWD_TIMER_LOAD);
138}
139
140/*
141 * Setup the local clock events for a CPU.
142 */
143void __cpuinit twd_timer_setup(struct clock_event_device *clk)
144{
145 unsigned long flags;
146
147 twd_calibrate_rate();
148
149 clk->name = "local_timer";
150 clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
151 clk->rating = 350;
152 clk->set_mode = twd_set_mode;
153 clk->set_next_event = twd_set_next_event;
154 clk->shift = 20;
155 clk->mult = div_sc(twd_timer_rate, NSEC_PER_SEC, clk->shift);
156 clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk);
157 clk->min_delta_ns = clockevent_delta2ns(0xf, clk);
158
159 /* Make sure our local interrupt controller has this enabled */
160 local_irq_save(flags);
161 get_irq_chip(clk->irq)->unmask(clk->irq);
162 local_irq_restore(flags);
163
164 clockevents_register_device(clk);
165}
166
167/*
168 * take a local timer down
169 */
170void __cpuexit twd_timer_stop(void)
171{
172 __raw_writel(0, twd_base + TWD_TIMER_CONTROL);
173}
diff --git a/arch/arm/mach-realview/Makefile b/arch/arm/mach-realview/Makefile
index 7bea8ffc4b59..e13d0947ad0b 100644
--- a/arch/arm/mach-realview/Makefile
+++ b/arch/arm/mach-realview/Makefile
@@ -7,5 +7,6 @@ obj-$(CONFIG_MACH_REALVIEW_EB) += realview_eb.o
7obj-$(CONFIG_MACH_REALVIEW_PB11MP) += realview_pb11mp.o 7obj-$(CONFIG_MACH_REALVIEW_PB11MP) += realview_pb11mp.o
8obj-$(CONFIG_MACH_REALVIEW_PB1176) += realview_pb1176.o 8obj-$(CONFIG_MACH_REALVIEW_PB1176) += realview_pb1176.o
9obj-$(CONFIG_MACH_REALVIEW_PBA8) += realview_pba8.o 9obj-$(CONFIG_MACH_REALVIEW_PBA8) += realview_pba8.o
10obj-$(CONFIG_SMP) += platsmp.o headsmp.o localtimer.o 10obj-$(CONFIG_SMP) += platsmp.o headsmp.o
11obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o 11obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
12obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o
diff --git a/arch/arm/mach-realview/core.h b/arch/arm/mach-realview/core.h
index 21c08637683b..59a337ba4be7 100644
--- a/arch/arm/mach-realview/core.h
+++ b/arch/arm/mach-realview/core.h
@@ -51,9 +51,6 @@ extern struct mmc_platform_data realview_mmc0_plat_data;
51extern struct mmc_platform_data realview_mmc1_plat_data; 51extern struct mmc_platform_data realview_mmc1_plat_data;
52extern struct clcd_board clcd_plat_data; 52extern struct clcd_board clcd_plat_data;
53extern void __iomem *gic_cpu_base_addr; 53extern void __iomem *gic_cpu_base_addr;
54#ifdef CONFIG_LOCAL_TIMERS
55extern void __iomem *twd_base;
56#endif
57extern void __iomem *timer0_va_base; 54extern void __iomem *timer0_va_base;
58extern void __iomem *timer1_va_base; 55extern void __iomem *timer1_va_base;
59extern void __iomem *timer2_va_base; 56extern void __iomem *timer2_va_base;
diff --git a/arch/arm/mach-realview/localtimer.c b/arch/arm/mach-realview/localtimer.c
index cd98e7acd94d..60b4e111f459 100644
--- a/arch/arm/mach-realview/localtimer.c
+++ b/arch/arm/mach-realview/localtimer.c
@@ -9,154 +9,18 @@
9 * published by the Free Software Foundation. 9 * published by the Free Software Foundation.
10 */ 10 */
11#include <linux/init.h> 11#include <linux/init.h>
12#include <linux/kernel.h>
13#include <linux/delay.h>
14#include <linux/smp.h> 12#include <linux/smp.h>
15#include <linux/jiffies.h>
16#include <linux/clockchips.h> 13#include <linux/clockchips.h>
17#include <linux/irq.h>
18#include <linux/io.h>
19 14
20#include <asm/hardware/arm_twd.h>
21#include <asm/hardware/gic.h>
22#include <mach/hardware.h>
23#include <asm/irq.h> 15#include <asm/irq.h>
24 16#include <asm/smp_twd.h>
25#ifdef CONFIG_LOCAL_TIMERS 17#include <asm/localtimer.h>
26
27/* set up by the platform code */
28void __iomem *twd_base;
29
30static unsigned long mpcore_timer_rate;
31
32static void local_timer_set_mode(enum clock_event_mode mode,
33 struct clock_event_device *evt)
34{
35 unsigned long ctrl;
36
37 switch(mode) {
38 case CLOCK_EVT_MODE_PERIODIC:
39 /* timer load already set up */
40 ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE
41 | TWD_TIMER_CONTROL_PERIODIC;
42 break;
43 case CLOCK_EVT_MODE_ONESHOT:
44 /* period set, and timer enabled in 'next_event' hook */
45 ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT;
46 break;
47 case CLOCK_EVT_MODE_UNUSED:
48 case CLOCK_EVT_MODE_SHUTDOWN:
49 default:
50 ctrl = 0;
51 }
52
53 __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
54}
55
56static int local_timer_set_next_event(unsigned long evt,
57 struct clock_event_device *unused)
58{
59 unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL);
60
61 __raw_writel(evt, twd_base + TWD_TIMER_COUNTER);
62 __raw_writel(ctrl | TWD_TIMER_CONTROL_ENABLE, twd_base + TWD_TIMER_CONTROL);
63
64 return 0;
65}
66
67/*
68 * local_timer_ack: checks for a local timer interrupt.
69 *
70 * If a local timer interrupt has occurred, acknowledge and return 1.
71 * Otherwise, return 0.
72 */
73int local_timer_ack(void)
74{
75 if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) {
76 __raw_writel(1, twd_base + TWD_TIMER_INTSTAT);
77 return 1;
78 }
79
80 return 0;
81}
82
83static void __cpuinit twd_calibrate_rate(void)
84{
85 unsigned long load, count;
86 u64 waitjiffies;
87
88 /*
89 * If this is the first time round, we need to work out how fast
90 * the timer ticks
91 */
92 if (mpcore_timer_rate == 0) {
93 printk("Calibrating local timer... ");
94
95 /* Wait for a tick to start */
96 waitjiffies = get_jiffies_64() + 1;
97
98 while (get_jiffies_64() < waitjiffies)
99 udelay(10);
100
101 /* OK, now the tick has started, let's get the timer going */
102 waitjiffies += 5;
103
104 /* enable, no interrupt or reload */
105 __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL);
106
107 /* maximum value */
108 __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
109
110 while (get_jiffies_64() < waitjiffies)
111 udelay(10);
112
113 count = __raw_readl(twd_base + TWD_TIMER_COUNTER);
114
115 mpcore_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
116
117 printk("%lu.%02luMHz.\n", mpcore_timer_rate / 1000000,
118 (mpcore_timer_rate / 100000) % 100);
119 }
120
121 load = mpcore_timer_rate / HZ;
122
123 __raw_writel(load, twd_base + TWD_TIMER_LOAD);
124}
125 18
126/* 19/*
127 * Setup the local clock events for a CPU. 20 * Setup the local clock events for a CPU.
128 */ 21 */
129void __cpuinit local_timer_setup(struct clock_event_device *evt) 22void __cpuinit local_timer_setup(struct clock_event_device *evt)
130{ 23{
131 unsigned long flags; 24 evt->irq = IRQ_LOCALTIMER;
132 25 twd_timer_setup(evt);
133 twd_calibrate_rate();
134
135 evt->name = "local_timer";
136 evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
137 evt->rating = 350;
138 evt->set_mode = local_timer_set_mode;
139 evt->set_next_event = local_timer_set_next_event;
140 evt->irq = IRQ_LOCALTIMER;
141 evt->shift = 20;
142 evt->mult = div_sc(mpcore_timer_rate, NSEC_PER_SEC, evt->shift);
143 evt->max_delta_ns = clockevent_delta2ns(0xffffffff, evt);
144 evt->min_delta_ns = clockevent_delta2ns(0xf, evt);
145
146 /* Make sure our local interrupt controller has this enabled */
147 local_irq_save(flags);
148 get_irq_chip(IRQ_LOCALTIMER)->unmask(IRQ_LOCALTIMER);
149 local_irq_restore(flags);
150
151 clockevents_register_device(evt);
152} 26}
153
154/*
155 * take a local timer down
156 */
157void __cpuexit local_timer_stop(void)
158{
159 __raw_writel(0, twd_base + TWD_TIMER_CONTROL);
160}
161
162#endif /* CONFIG_LOCAL_TIMERS */
diff --git a/arch/arm/mach-realview/realview_eb.c b/arch/arm/mach-realview/realview_eb.c
index c20fbef122b3..8dfa44e08a94 100644
--- a/arch/arm/mach-realview/realview_eb.c
+++ b/arch/arm/mach-realview/realview_eb.c
@@ -32,6 +32,7 @@
32#include <asm/hardware/gic.h> 32#include <asm/hardware/gic.h>
33#include <asm/hardware/icst307.h> 33#include <asm/hardware/icst307.h>
34#include <asm/hardware/cache-l2x0.h> 34#include <asm/hardware/cache-l2x0.h>
35#include <asm/localtimer.h>
35 36
36#include <asm/mach/arch.h> 37#include <asm/mach/arch.h>
37#include <asm/mach/map.h> 38#include <asm/mach/map.h>
diff --git a/arch/arm/mach-realview/realview_pb11mp.c b/arch/arm/mach-realview/realview_pb11mp.c
index ea1e60eca359..dc4b16943907 100644
--- a/arch/arm/mach-realview/realview_pb11mp.c
+++ b/arch/arm/mach-realview/realview_pb11mp.c
@@ -32,6 +32,7 @@
32#include <asm/hardware/gic.h> 32#include <asm/hardware/gic.h>
33#include <asm/hardware/icst307.h> 33#include <asm/hardware/icst307.h>
34#include <asm/hardware/cache-l2x0.h> 34#include <asm/hardware/cache-l2x0.h>
35#include <asm/localtimer.h>
35 36
36#include <asm/mach/arch.h> 37#include <asm/mach/arch.h>
37#include <asm/mach/flash.h> 38#include <asm/mach/flash.h>