Commit | Line | Data |
---|---|---|
a2c5d4ed JH |
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 | ||
a2c5d4ed JH |
59 | static cycle_t metag_clocksource_read(struct clocksource *cs) |
60 | { | |
61 | return __core_reg_get(TXTIMER); | |
62 | } | |
63 | ||
64 | static struct clocksource clocksource_metag = { | |
65 | .name = "META", | |
66 | .rating = 200, | |
67 | .mask = CLOCKSOURCE_MASK(32), | |
68 | .read = metag_clocksource_read, | |
69 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | |
70 | }; | |
71 | ||
72 | static irqreturn_t metag_timer_interrupt(int irq, void *dummy) | |
73 | { | |
27d05167 | 74 | struct clock_event_device *evt = this_cpu_ptr(&local_clockevent); |
a2c5d4ed JH |
75 | |
76 | evt->event_handler(evt); | |
77 | ||
78 | return IRQ_HANDLED; | |
79 | } | |
80 | ||
81 | static struct irqaction metag_timer_irq = { | |
82 | .name = "META core timer", | |
83 | .handler = metag_timer_interrupt, | |
84 | .flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_PERCPU, | |
85 | }; | |
86 | ||
87 | unsigned long long sched_clock(void) | |
88 | { | |
89 | unsigned long long ticks = __core_reg_get(TXTIMER); | |
90 | return ticks << HARDWARE_TO_NS_SHIFT; | |
91 | } | |
92 | ||
8c37bb3a | 93 | static void arch_timer_setup(unsigned int cpu) |
a2c5d4ed JH |
94 | { |
95 | unsigned int txdivtime; | |
96 | struct clock_event_device *clk = &per_cpu(local_clockevent, cpu); | |
97 | char *name = per_cpu(local_clockevent_name, cpu); | |
98 | ||
99 | txdivtime = __core_reg_get(TXDIVTIME); | |
100 | ||
101 | txdivtime &= ~TXDIVTIME_DIV_BITS; | |
102 | txdivtime |= (HARDWARE_DIV & TXDIVTIME_DIV_BITS); | |
103 | ||
104 | __core_reg_set(TXDIVTIME, txdivtime); | |
105 | ||
106 | sprintf(name, "META %d", cpu); | |
107 | clk->name = name; | |
108 | clk->features = CLOCK_EVT_FEAT_ONESHOT, | |
109 | ||
110 | clk->rating = 200, | |
111 | clk->shift = 12, | |
112 | clk->irq = tbisig_map(TBID_SIGNUM_TRT), | |
a2c5d4ed JH |
113 | clk->set_next_event = metag_timer_set_next_event, |
114 | ||
115 | clk->mult = div_sc(hwtimer_freq, NSEC_PER_SEC, clk->shift); | |
116 | clk->max_delta_ns = clockevent_delta2ns(0x7fffffff, clk); | |
117 | clk->min_delta_ns = clockevent_delta2ns(0xf, clk); | |
118 | clk->cpumask = cpumask_of(cpu); | |
119 | ||
120 | clockevents_register_device(clk); | |
121 | ||
122 | /* | |
123 | * For all non-boot CPUs we need to synchronize our free | |
124 | * running clock (TXTIMER) with the boot CPU's clock. | |
125 | * | |
126 | * While this won't be accurate, it should be close enough. | |
127 | */ | |
128 | if (cpu) { | |
129 | unsigned int thread0 = cpu_2_hwthread_id[0]; | |
130 | unsigned long val; | |
131 | ||
132 | val = core_reg_read(TXUCT_ID, TXTIMER_REGNUM, thread0); | |
133 | __core_reg_set(TXTIMER, val); | |
134 | } | |
135 | } | |
136 | ||
8c37bb3a | 137 | static int arch_timer_cpu_notify(struct notifier_block *self, |
a2c5d4ed JH |
138 | unsigned long action, void *hcpu) |
139 | { | |
140 | int cpu = (long)hcpu; | |
141 | ||
142 | switch (action) { | |
143 | case CPU_STARTING: | |
144 | case CPU_STARTING_FROZEN: | |
145 | arch_timer_setup(cpu); | |
146 | break; | |
147 | } | |
148 | ||
149 | return NOTIFY_OK; | |
150 | } | |
151 | ||
8c37bb3a | 152 | static struct notifier_block arch_timer_cpu_nb = { |
a2c5d4ed JH |
153 | .notifier_call = arch_timer_cpu_notify, |
154 | }; | |
155 | ||
156 | int __init metag_generic_timer_init(void) | |
157 | { | |
158 | /* | |
159 | * On Meta 2 SoCs, the actual frequency of the timer is based on the | |
160 | * Meta core clock speed divided by an integer, so it is only | |
161 | * approximately 1MHz. Calculating the real frequency here drastically | |
162 | * reduces clock skew on these SoCs. | |
163 | */ | |
164 | #ifdef CONFIG_METAG_META21 | |
165 | hwtimer_freq = get_coreclock() / (metag_in32(EXPAND_TIMER_DIV) + 1); | |
166 | #endif | |
cf070532 JH |
167 | pr_info("Timer frequency: %u Hz\n", hwtimer_freq); |
168 | ||
a2c5d4ed JH |
169 | clocksource_register_hz(&clocksource_metag, hwtimer_freq); |
170 | ||
171 | setup_irq(tbisig_map(TBID_SIGNUM_TRT), &metag_timer_irq); | |
172 | ||
173 | /* Configure timer on boot CPU */ | |
174 | arch_timer_setup(smp_processor_id()); | |
175 | ||
176 | /* Hook cpu boot to configure other CPU's timers */ | |
177 | register_cpu_notifier(&arch_timer_cpu_nb); | |
178 | ||
179 | return 0; | |
180 | } |