]>
Commit | Line | Data |
---|---|---|
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 = this_cpu_ptr(&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 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 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 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 | pr_info("Timer frequency: %u Hz\n", hwtimer_freq); | |
188 | ||
189 | clocksource_register_hz(&clocksource_metag, hwtimer_freq); | |
190 | ||
191 | setup_irq(tbisig_map(TBID_SIGNUM_TRT), &metag_timer_irq); | |
192 | ||
193 | /* Configure timer on boot CPU */ | |
194 | arch_timer_setup(smp_processor_id()); | |
195 | ||
196 | /* Hook cpu boot to configure other CPU's timers */ | |
197 | register_cpu_notifier(&arch_timer_cpu_nb); | |
198 | ||
199 | return 0; | |
200 | } |