diff options
author | James Hogan <james.hogan@imgtec.com> | 2012-10-09 05:54:39 -0400 |
---|---|---|
committer | James Hogan <james.hogan@imgtec.com> | 2013-03-02 15:09:22 -0500 |
commit | a2c5d4ed92bbc02ff4a37efc2adffe7d145abe4f (patch) | |
tree | 21fc65e4f0b04928025565f208a410a7a64ab523 | |
parent | bc3966bf1583a6c22b76397535174445c43952de (diff) |
metag: Time keeping
Add time keeping code for metag. Meta hardware threads have 2 timers.
The background timer (TXTIMER) is used as a free-running time base, and
the interrupt timer (TXTIMERI) is used for the timer interrupt. Both
counters traditionally count at approximately 1MHz.
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: John Stultz <johnstul@us.ibm.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | arch/metag/include/asm/clock.h | 51 | ||||
-rw-r--r-- | arch/metag/include/asm/delay.h | 29 | ||||
-rw-r--r-- | arch/metag/include/asm/mach/arch.h | 4 | ||||
-rw-r--r-- | arch/metag/kernel/clock.c | 53 | ||||
-rw-r--r-- | arch/metag/kernel/time.c | 15 | ||||
-rw-r--r-- | drivers/clocksource/Kconfig | 5 | ||||
-rw-r--r-- | drivers/clocksource/Makefile | 1 | ||||
-rw-r--r-- | drivers/clocksource/metag_generic.c | 198 | ||||
-rw-r--r-- | include/clocksource/metag_generic.h | 21 |
10 files changed, 378 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 401145d7c5bb..749d76699ead 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -5039,6 +5039,7 @@ S: Supported | |||
5039 | F: arch/metag/ | 5039 | F: arch/metag/ |
5040 | F: Documentation/metag/ | 5040 | F: Documentation/metag/ |
5041 | F: Documentation/devicetree/bindings/metag/ | 5041 | F: Documentation/devicetree/bindings/metag/ |
5042 | F: drivers/clocksource/metag_generic.c | ||
5042 | 5043 | ||
5043 | MICROBLAZE ARCHITECTURE | 5044 | MICROBLAZE ARCHITECTURE |
5044 | M: Michal Simek <monstr@monstr.eu> | 5045 | M: Michal Simek <monstr@monstr.eu> |
diff --git a/arch/metag/include/asm/clock.h b/arch/metag/include/asm/clock.h new file mode 100644 index 000000000000..3e2915a280c7 --- /dev/null +++ b/arch/metag/include/asm/clock.h | |||
@@ -0,0 +1,51 @@ | |||
1 | /* | ||
2 | * arch/metag/include/asm/clock.h | ||
3 | * | ||
4 | * Copyright (C) 2012 Imagination Technologies Ltd. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #ifndef _METAG_CLOCK_H_ | ||
12 | #define _METAG_CLOCK_H_ | ||
13 | |||
14 | #include <asm/mach/arch.h> | ||
15 | |||
16 | /** | ||
17 | * struct meta_clock_desc - Meta Core clock callbacks. | ||
18 | * @get_core_freq: Get the frequency of the Meta core. If this is NULL, the | ||
19 | * core frequency will be determined like this: | ||
20 | * Meta 1: based on loops_per_jiffy. | ||
21 | * Meta 2: (EXPAND_TIMER_DIV + 1) MHz. | ||
22 | */ | ||
23 | struct meta_clock_desc { | ||
24 | unsigned long (*get_core_freq)(void); | ||
25 | }; | ||
26 | |||
27 | extern struct meta_clock_desc _meta_clock; | ||
28 | |||
29 | /* | ||
30 | * Set up the default clock, ensuring all callbacks are valid - only accessible | ||
31 | * during boot. | ||
32 | */ | ||
33 | void setup_meta_clocks(struct meta_clock_desc *desc); | ||
34 | |||
35 | /** | ||
36 | * get_coreclock() - Get the frequency of the Meta core clock. | ||
37 | * | ||
38 | * Returns: The Meta core clock frequency in Hz. | ||
39 | */ | ||
40 | static inline unsigned long get_coreclock(void) | ||
41 | { | ||
42 | /* | ||
43 | * Use the current clock callback. If set correctly this will provide | ||
44 | * the most accurate frequency as it can be calculated directly from the | ||
45 | * PLL configuration. otherwise a default callback will have been set | ||
46 | * instead. | ||
47 | */ | ||
48 | return _meta_clock.get_core_freq(); | ||
49 | } | ||
50 | |||
51 | #endif /* _METAG_CLOCK_H_ */ | ||
diff --git a/arch/metag/include/asm/delay.h b/arch/metag/include/asm/delay.h new file mode 100644 index 000000000000..9c92f996957a --- /dev/null +++ b/arch/metag/include/asm/delay.h | |||
@@ -0,0 +1,29 @@ | |||
1 | #ifndef _METAG_DELAY_H | ||
2 | #define _METAG_DELAY_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1993 Linus Torvalds | ||
6 | * | ||
7 | * Delay routines calling functions in arch/metag/lib/delay.c | ||
8 | */ | ||
9 | |||
10 | /* Undefined functions to get compile-time errors */ | ||
11 | extern void __bad_udelay(void); | ||
12 | extern void __bad_ndelay(void); | ||
13 | |||
14 | extern void __udelay(unsigned long usecs); | ||
15 | extern void __ndelay(unsigned long nsecs); | ||
16 | extern void __const_udelay(unsigned long xloops); | ||
17 | extern void __delay(unsigned long loops); | ||
18 | |||
19 | /* 0x10c7 is 2**32 / 1000000 (rounded up) */ | ||
20 | #define udelay(n) (__builtin_constant_p(n) ? \ | ||
21 | ((n) > 20000 ? __bad_udelay() : __const_udelay((n) * 0x10c7ul)) : \ | ||
22 | __udelay(n)) | ||
23 | |||
24 | /* 0x5 is 2**32 / 1000000000 (rounded up) */ | ||
25 | #define ndelay(n) (__builtin_constant_p(n) ? \ | ||
26 | ((n) > 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : \ | ||
27 | __ndelay(n)) | ||
28 | |||
29 | #endif /* _METAG_DELAY_H */ | ||
diff --git a/arch/metag/include/asm/mach/arch.h b/arch/metag/include/asm/mach/arch.h index 6845d80857e4..12c5664fea6e 100644 --- a/arch/metag/include/asm/mach/arch.h +++ b/arch/metag/include/asm/mach/arch.h | |||
@@ -16,10 +16,13 @@ | |||
16 | 16 | ||
17 | #include <linux/stddef.h> | 17 | #include <linux/stddef.h> |
18 | 18 | ||
19 | #include <asm/clock.h> | ||
20 | |||
19 | /** | 21 | /** |
20 | * struct machine_desc - Describes a board controlled by a Meta. | 22 | * struct machine_desc - Describes a board controlled by a Meta. |
21 | * @name: Board/SoC name. | 23 | * @name: Board/SoC name. |
22 | * @dt_compat: Array of device tree 'compatible' strings. | 24 | * @dt_compat: Array of device tree 'compatible' strings. |
25 | * @clocks: Clock callbacks. | ||
23 | * | 26 | * |
24 | * @nr_irqs: Maximum number of IRQs. | 27 | * @nr_irqs: Maximum number of IRQs. |
25 | * If 0, defaults to NR_IRQS in asm-generic/irq.h. | 28 | * If 0, defaults to NR_IRQS in asm-generic/irq.h. |
@@ -37,6 +40,7 @@ | |||
37 | struct machine_desc { | 40 | struct machine_desc { |
38 | const char *name; | 41 | const char *name; |
39 | const char **dt_compat; | 42 | const char **dt_compat; |
43 | struct meta_clock_desc *clocks; | ||
40 | 44 | ||
41 | unsigned int nr_irqs; | 45 | unsigned int nr_irqs; |
42 | 46 | ||
diff --git a/arch/metag/kernel/clock.c b/arch/metag/kernel/clock.c new file mode 100644 index 000000000000..defc84056f18 --- /dev/null +++ b/arch/metag/kernel/clock.c | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * arch/metag/kernel/clock.c | ||
3 | * | ||
4 | * Copyright (C) 2012 Imagination Technologies Ltd. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/delay.h> | ||
12 | #include <linux/io.h> | ||
13 | |||
14 | #include <asm/param.h> | ||
15 | #include <asm/clock.h> | ||
16 | |||
17 | struct meta_clock_desc _meta_clock; | ||
18 | |||
19 | /* Default machine get_core_freq callback. */ | ||
20 | static unsigned long get_core_freq_default(void) | ||
21 | { | ||
22 | #ifdef CONFIG_METAG_META21 | ||
23 | /* | ||
24 | * Meta 2 cores divide down the core clock for the Meta timers, so we | ||
25 | * can estimate the core clock from the divider. | ||
26 | */ | ||
27 | return (metag_in32(EXPAND_TIMER_DIV) + 1) * 1000000; | ||
28 | #else | ||
29 | /* | ||
30 | * On Meta 1 we don't know the core clock, but assuming the Meta timer | ||
31 | * is correct it can be estimated based on loops_per_jiffy. | ||
32 | */ | ||
33 | return (loops_per_jiffy * HZ * 5) >> 1; | ||
34 | #endif | ||
35 | } | ||
36 | |||
37 | /** | ||
38 | * setup_meta_clocks() - Set up the Meta clock. | ||
39 | * @desc: Clock descriptor usually provided by machine description | ||
40 | * | ||
41 | * Ensures all callbacks are valid. | ||
42 | */ | ||
43 | void __init setup_meta_clocks(struct meta_clock_desc *desc) | ||
44 | { | ||
45 | /* copy callbacks */ | ||
46 | if (desc) | ||
47 | _meta_clock = *desc; | ||
48 | |||
49 | /* set fallback functions */ | ||
50 | if (!_meta_clock.get_core_freq) | ||
51 | _meta_clock.get_core_freq = get_core_freq_default; | ||
52 | } | ||
53 | |||
diff --git a/arch/metag/kernel/time.c b/arch/metag/kernel/time.c new file mode 100644 index 000000000000..17dc10733b2f --- /dev/null +++ b/arch/metag/kernel/time.c | |||
@@ -0,0 +1,15 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2005-2013 Imagination Technologies Ltd. | ||
3 | * | ||
4 | * This file contains the Meta-specific time handling details. | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #include <linux/init.h> | ||
9 | |||
10 | #include <clocksource/metag_generic.h> | ||
11 | |||
12 | void __init time_init(void) | ||
13 | { | ||
14 | metag_generic_timer_init(); | ||
15 | } | ||
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 7fdcbd3f4da5..75bc7520ace5 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig | |||
@@ -58,3 +58,8 @@ config CLKSRC_ARM_GENERIC | |||
58 | def_bool y if ARM64 | 58 | def_bool y if ARM64 |
59 | help | 59 | help |
60 | This option enables support for the ARM generic timer. | 60 | This option enables support for the ARM generic timer. |
61 | |||
62 | config CLKSRC_METAG_GENERIC | ||
63 | def_bool y if METAG | ||
64 | help | ||
65 | This option enables support for the Meta per-thread timers. | ||
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index f93453d01673..09dcd49b7e31 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile | |||
@@ -18,3 +18,4 @@ obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o | |||
18 | obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o | 18 | obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o |
19 | 19 | ||
20 | obj-$(CONFIG_CLKSRC_ARM_GENERIC) += arm_generic.o | 20 | obj-$(CONFIG_CLKSRC_ARM_GENERIC) += arm_generic.o |
21 | obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o | ||
diff --git a/drivers/clocksource/metag_generic.c b/drivers/clocksource/metag_generic.c new file mode 100644 index 000000000000..ade7513a11d1 --- /dev/null +++ b/drivers/clocksource/metag_generic.c | |||
@@ -0,0 +1,198 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2005-2013 Imagination Technologies Ltd. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | * | ||
16 | * | ||
17 | * Support for Meta per-thread timers. | ||
18 | * | ||
19 | * Meta hardware threads have 2 timers. The background timer (TXTIMER) is used | ||
20 | * as a free-running time base (hz clocksource), and the interrupt timer | ||
21 | * (TXTIMERI) is used for the timer interrupt (clock event). Both counters | ||
22 | * traditionally count at approximately 1MHz. | ||
23 | */ | ||
24 | |||
25 | #include <clocksource/metag_generic.h> | ||
26 | #include <linux/cpu.h> | ||
27 | #include <linux/errno.h> | ||
28 | #include <linux/sched.h> | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/param.h> | ||
31 | #include <linux/time.h> | ||
32 | #include <linux/init.h> | ||
33 | #include <linux/proc_fs.h> | ||
34 | #include <linux/clocksource.h> | ||
35 | #include <linux/clockchips.h> | ||
36 | #include <linux/interrupt.h> | ||
37 | |||
38 | #include <asm/clock.h> | ||
39 | #include <asm/hwthread.h> | ||
40 | #include <asm/core_reg.h> | ||
41 | #include <asm/metag_mem.h> | ||
42 | #include <asm/tbx.h> | ||
43 | |||
44 | #define HARDWARE_FREQ 1000000 /* 1MHz */ | ||
45 | #define HARDWARE_DIV 1 /* divide by 1 = 1MHz clock */ | ||
46 | #define HARDWARE_TO_NS_SHIFT 10 /* convert ticks to ns */ | ||
47 | |||
48 | static unsigned int hwtimer_freq = HARDWARE_FREQ; | ||
49 | static DEFINE_PER_CPU(struct clock_event_device, local_clockevent); | ||
50 | static DEFINE_PER_CPU(char [11], local_clockevent_name); | ||
51 | |||
52 | static int metag_timer_set_next_event(unsigned long delta, | ||
53 | struct clock_event_device *dev) | ||
54 | { | ||
55 | __core_reg_set(TXTIMERI, -delta); | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | static void metag_timer_set_mode(enum clock_event_mode mode, | ||
60 | struct clock_event_device *evt) | ||
61 | { | ||
62 | switch (mode) { | ||
63 | case CLOCK_EVT_MODE_ONESHOT: | ||
64 | case CLOCK_EVT_MODE_RESUME: | ||
65 | break; | ||
66 | |||
67 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
68 | /* We should disable the IRQ here */ | ||
69 | break; | ||
70 | |||
71 | case CLOCK_EVT_MODE_PERIODIC: | ||
72 | case CLOCK_EVT_MODE_UNUSED: | ||
73 | WARN_ON(1); | ||
74 | break; | ||
75 | }; | ||
76 | } | ||
77 | |||
78 | static cycle_t metag_clocksource_read(struct clocksource *cs) | ||
79 | { | ||
80 | return __core_reg_get(TXTIMER); | ||
81 | } | ||
82 | |||
83 | static struct clocksource clocksource_metag = { | ||
84 | .name = "META", | ||
85 | .rating = 200, | ||
86 | .mask = CLOCKSOURCE_MASK(32), | ||
87 | .read = metag_clocksource_read, | ||
88 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
89 | }; | ||
90 | |||
91 | static irqreturn_t metag_timer_interrupt(int irq, void *dummy) | ||
92 | { | ||
93 | struct clock_event_device *evt = &__get_cpu_var(local_clockevent); | ||
94 | |||
95 | evt->event_handler(evt); | ||
96 | |||
97 | return IRQ_HANDLED; | ||
98 | } | ||
99 | |||
100 | static struct irqaction metag_timer_irq = { | ||
101 | .name = "META core timer", | ||
102 | .handler = metag_timer_interrupt, | ||
103 | .flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_PERCPU, | ||
104 | }; | ||
105 | |||
106 | unsigned long long sched_clock(void) | ||
107 | { | ||
108 | unsigned long long ticks = __core_reg_get(TXTIMER); | ||
109 | return ticks << HARDWARE_TO_NS_SHIFT; | ||
110 | } | ||
111 | |||
112 | static void __cpuinit arch_timer_setup(unsigned int cpu) | ||
113 | { | ||
114 | unsigned int txdivtime; | ||
115 | struct clock_event_device *clk = &per_cpu(local_clockevent, cpu); | ||
116 | char *name = per_cpu(local_clockevent_name, cpu); | ||
117 | |||
118 | txdivtime = __core_reg_get(TXDIVTIME); | ||
119 | |||
120 | txdivtime &= ~TXDIVTIME_DIV_BITS; | ||
121 | txdivtime |= (HARDWARE_DIV & TXDIVTIME_DIV_BITS); | ||
122 | |||
123 | __core_reg_set(TXDIVTIME, txdivtime); | ||
124 | |||
125 | sprintf(name, "META %d", cpu); | ||
126 | clk->name = name; | ||
127 | clk->features = CLOCK_EVT_FEAT_ONESHOT, | ||
128 | |||
129 | clk->rating = 200, | ||
130 | clk->shift = 12, | ||
131 | clk->irq = tbisig_map(TBID_SIGNUM_TRT), | ||
132 | clk->set_mode = metag_timer_set_mode, | ||
133 | clk->set_next_event = metag_timer_set_next_event, | ||
134 | |||
135 | clk->mult = div_sc(hwtimer_freq, NSEC_PER_SEC, clk->shift); | ||
136 | clk->max_delta_ns = clockevent_delta2ns(0x7fffffff, clk); | ||
137 | clk->min_delta_ns = clockevent_delta2ns(0xf, clk); | ||
138 | clk->cpumask = cpumask_of(cpu); | ||
139 | |||
140 | clockevents_register_device(clk); | ||
141 | |||
142 | /* | ||
143 | * For all non-boot CPUs we need to synchronize our free | ||
144 | * running clock (TXTIMER) with the boot CPU's clock. | ||
145 | * | ||
146 | * While this won't be accurate, it should be close enough. | ||
147 | */ | ||
148 | if (cpu) { | ||
149 | unsigned int thread0 = cpu_2_hwthread_id[0]; | ||
150 | unsigned long val; | ||
151 | |||
152 | val = core_reg_read(TXUCT_ID, TXTIMER_REGNUM, thread0); | ||
153 | __core_reg_set(TXTIMER, val); | ||
154 | } | ||
155 | } | ||
156 | |||
157 | static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self, | ||
158 | unsigned long action, void *hcpu) | ||
159 | { | ||
160 | int cpu = (long)hcpu; | ||
161 | |||
162 | switch (action) { | ||
163 | case CPU_STARTING: | ||
164 | case CPU_STARTING_FROZEN: | ||
165 | arch_timer_setup(cpu); | ||
166 | break; | ||
167 | } | ||
168 | |||
169 | return NOTIFY_OK; | ||
170 | } | ||
171 | |||
172 | static struct notifier_block __cpuinitdata arch_timer_cpu_nb = { | ||
173 | .notifier_call = arch_timer_cpu_notify, | ||
174 | }; | ||
175 | |||
176 | int __init metag_generic_timer_init(void) | ||
177 | { | ||
178 | /* | ||
179 | * On Meta 2 SoCs, the actual frequency of the timer is based on the | ||
180 | * Meta core clock speed divided by an integer, so it is only | ||
181 | * approximately 1MHz. Calculating the real frequency here drastically | ||
182 | * reduces clock skew on these SoCs. | ||
183 | */ | ||
184 | #ifdef CONFIG_METAG_META21 | ||
185 | hwtimer_freq = get_coreclock() / (metag_in32(EXPAND_TIMER_DIV) + 1); | ||
186 | #endif | ||
187 | clocksource_register_hz(&clocksource_metag, hwtimer_freq); | ||
188 | |||
189 | setup_irq(tbisig_map(TBID_SIGNUM_TRT), &metag_timer_irq); | ||
190 | |||
191 | /* Configure timer on boot CPU */ | ||
192 | arch_timer_setup(smp_processor_id()); | ||
193 | |||
194 | /* Hook cpu boot to configure other CPU's timers */ | ||
195 | register_cpu_notifier(&arch_timer_cpu_nb); | ||
196 | |||
197 | return 0; | ||
198 | } | ||
diff --git a/include/clocksource/metag_generic.h b/include/clocksource/metag_generic.h new file mode 100644 index 000000000000..ac17e7d06cfb --- /dev/null +++ b/include/clocksource/metag_generic.h | |||
@@ -0,0 +1,21 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 Imaginaton Technologies Ltd. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | #ifndef __CLKSOURCE_METAG_GENERIC_H | ||
17 | #define __CLKSOURCE_METAG_GENERIC_H | ||
18 | |||
19 | extern int metag_generic_timer_init(void); | ||
20 | |||
21 | #endif /* __CLKSOURCE_METAG_GENERIC_H */ | ||