diff options
author | Aurelien Jacquiot <a-jacquiot@ti.com> | 2011-10-04 11:05:33 -0400 |
---|---|---|
committer | Mark Salter <msalter@redhat.com> | 2011-10-06 19:47:51 -0400 |
commit | 546a39546c64ad7e73796c5508ef5487af42cae2 (patch) | |
tree | 9ab7fb5512ac1a99ab29267482469dcd8a8252ff | |
parent | 03a347558749caaab482f34410ae5d27e893db89 (diff) |
C6X: time management
Original port to early 2.6 kernel using TI COFF toolchain.
Brought up to date by Mark Salter <msalter@redhat.com>
Signed-off-by: Aurelien Jacquiot <a-jacquiot@ti.com>
Signed-off-by: Mark Salter <msalter@redhat.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Arnd Bergmann <arnd@arndb.de>
-rw-r--r-- | arch/c6x/include/asm/timer64.h | 6 | ||||
-rw-r--r-- | arch/c6x/include/asm/timex.h | 33 | ||||
-rw-r--r-- | arch/c6x/kernel/time.c | 65 | ||||
-rw-r--r-- | arch/c6x/platforms/timer64.c | 236 |
4 files changed, 340 insertions, 0 deletions
diff --git a/arch/c6x/include/asm/timer64.h b/arch/c6x/include/asm/timer64.h new file mode 100644 index 000000000000..bbe27bb9887e --- /dev/null +++ b/arch/c6x/include/asm/timer64.h | |||
@@ -0,0 +1,6 @@ | |||
1 | #ifndef _C6X_TIMER64_H | ||
2 | #define _C6X_TIMER64_H | ||
3 | |||
4 | extern void __init timer64_init(void); | ||
5 | |||
6 | #endif /* _C6X_TIMER64_H */ | ||
diff --git a/arch/c6x/include/asm/timex.h b/arch/c6x/include/asm/timex.h new file mode 100644 index 000000000000..508c3ec971f9 --- /dev/null +++ b/arch/c6x/include/asm/timex.h | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * Modified for 2.6.34: Mark Salter <msalter@redhat.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #ifndef _ASM_C6X_TIMEX_H | ||
14 | #define _ASM_C6X_TIMEX_H | ||
15 | |||
16 | #define CLOCK_TICK_RATE ((1000 * 1000000UL) / 6) | ||
17 | |||
18 | /* 64-bit timestamp */ | ||
19 | typedef unsigned long long cycles_t; | ||
20 | |||
21 | static inline cycles_t get_cycles(void) | ||
22 | { | ||
23 | unsigned l, h; | ||
24 | |||
25 | asm volatile (" dint\n" | ||
26 | " mvc .s2 TSCL,%0\n" | ||
27 | " mvc .s2 TSCH,%1\n" | ||
28 | " rint\n" | ||
29 | : "=b"(l), "=b"(h)); | ||
30 | return ((cycles_t)h << 32) | l; | ||
31 | } | ||
32 | |||
33 | #endif /* _ASM_C6X_TIMEX_H */ | ||
diff --git a/arch/c6x/kernel/time.c b/arch/c6x/kernel/time.c new file mode 100644 index 000000000000..4c9f136165f7 --- /dev/null +++ b/arch/c6x/kernel/time.c | |||
@@ -0,0 +1,65 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
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 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/clocksource.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/param.h> | ||
17 | #include <linux/string.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/timex.h> | ||
21 | #include <linux/profile.h> | ||
22 | |||
23 | #include <asm/timer64.h> | ||
24 | |||
25 | static u32 sched_clock_multiplier; | ||
26 | #define SCHED_CLOCK_SHIFT 16 | ||
27 | |||
28 | static cycle_t tsc_read(struct clocksource *cs) | ||
29 | { | ||
30 | return get_cycles(); | ||
31 | } | ||
32 | |||
33 | static struct clocksource clocksource_tsc = { | ||
34 | .name = "timestamp", | ||
35 | .rating = 300, | ||
36 | .read = tsc_read, | ||
37 | .mask = CLOCKSOURCE_MASK(64), | ||
38 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
39 | }; | ||
40 | |||
41 | /* | ||
42 | * scheduler clock - returns current time in nanoseconds. | ||
43 | */ | ||
44 | u64 sched_clock(void) | ||
45 | { | ||
46 | u64 tsc = get_cycles(); | ||
47 | |||
48 | return (tsc * sched_clock_multiplier) >> SCHED_CLOCK_SHIFT; | ||
49 | } | ||
50 | |||
51 | void time_init(void) | ||
52 | { | ||
53 | u64 tmp = (u64)NSEC_PER_SEC << SCHED_CLOCK_SHIFT; | ||
54 | |||
55 | do_div(tmp, c6x_core_freq); | ||
56 | sched_clock_multiplier = tmp; | ||
57 | |||
58 | clocksource_register_hz(&clocksource_tsc, c6x_core_freq); | ||
59 | |||
60 | /* write anything into TSCL to enable counting */ | ||
61 | set_creg(TSCL, 0); | ||
62 | |||
63 | /* probe for timer64 event timer */ | ||
64 | timer64_init(); | ||
65 | } | ||
diff --git a/arch/c6x/platforms/timer64.c b/arch/c6x/platforms/timer64.c new file mode 100644 index 000000000000..783415861dae --- /dev/null +++ b/arch/c6x/platforms/timer64.c | |||
@@ -0,0 +1,236 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, 2011 Texas Instruments Incorporated | ||
3 | * Contributed by: Mark Salter (msalter@redhat.com) | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | |||
10 | #include <linux/clockchips.h> | ||
11 | #include <linux/interrupt.h> | ||
12 | #include <linux/io.h> | ||
13 | #include <linux/of.h> | ||
14 | #include <linux/of_irq.h> | ||
15 | #include <linux/of_address.h> | ||
16 | #include <asm/soc.h> | ||
17 | #include <asm/dscr.h> | ||
18 | #include <asm/timer64.h> | ||
19 | |||
20 | struct timer_regs { | ||
21 | u32 reserved0; | ||
22 | u32 emumgt; | ||
23 | u32 reserved1; | ||
24 | u32 reserved2; | ||
25 | u32 cntlo; | ||
26 | u32 cnthi; | ||
27 | u32 prdlo; | ||
28 | u32 prdhi; | ||
29 | u32 tcr; | ||
30 | u32 tgcr; | ||
31 | u32 wdtcr; | ||
32 | }; | ||
33 | |||
34 | static struct timer_regs __iomem *timer; | ||
35 | |||
36 | #define TCR_TSTATLO 0x001 | ||
37 | #define TCR_INVOUTPLO 0x002 | ||
38 | #define TCR_INVINPLO 0x004 | ||
39 | #define TCR_CPLO 0x008 | ||
40 | #define TCR_ENAMODELO_ONCE 0x040 | ||
41 | #define TCR_ENAMODELO_CONT 0x080 | ||
42 | #define TCR_ENAMODELO_MASK 0x0c0 | ||
43 | #define TCR_PWIDLO_MASK 0x030 | ||
44 | #define TCR_CLKSRCLO 0x100 | ||
45 | #define TCR_TIENLO 0x200 | ||
46 | #define TCR_TSTATHI (0x001 << 16) | ||
47 | #define TCR_INVOUTPHI (0x002 << 16) | ||
48 | #define TCR_CPHI (0x008 << 16) | ||
49 | #define TCR_PWIDHI_MASK (0x030 << 16) | ||
50 | #define TCR_ENAMODEHI_ONCE (0x040 << 16) | ||
51 | #define TCR_ENAMODEHI_CONT (0x080 << 16) | ||
52 | #define TCR_ENAMODEHI_MASK (0x0c0 << 16) | ||
53 | |||
54 | #define TGCR_TIMLORS 0x001 | ||
55 | #define TGCR_TIMHIRS 0x002 | ||
56 | #define TGCR_TIMMODE_UD32 0x004 | ||
57 | #define TGCR_TIMMODE_WDT64 0x008 | ||
58 | #define TGCR_TIMMODE_CD32 0x00c | ||
59 | #define TGCR_TIMMODE_MASK 0x00c | ||
60 | #define TGCR_PSCHI_MASK (0x00f << 8) | ||
61 | #define TGCR_TDDRHI_MASK (0x00f << 12) | ||
62 | |||
63 | /* | ||
64 | * Timer clocks are divided down from the CPU clock | ||
65 | * The divisor is in the EMUMGTCLKSPD register | ||
66 | */ | ||
67 | #define TIMER_DIVISOR \ | ||
68 | ((soc_readl(&timer->emumgt) & (0xf << 16)) >> 16) | ||
69 | |||
70 | #define TIMER64_RATE (c6x_core_freq / TIMER_DIVISOR) | ||
71 | |||
72 | #define TIMER64_MODE_DISABLED 0 | ||
73 | #define TIMER64_MODE_ONE_SHOT TCR_ENAMODELO_ONCE | ||
74 | #define TIMER64_MODE_PERIODIC TCR_ENAMODELO_CONT | ||
75 | |||
76 | static int timer64_mode; | ||
77 | static int timer64_devstate_id = -1; | ||
78 | |||
79 | static void timer64_config(unsigned long period) | ||
80 | { | ||
81 | u32 tcr = soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK; | ||
82 | |||
83 | soc_writel(tcr, &timer->tcr); | ||
84 | soc_writel(period - 1, &timer->prdlo); | ||
85 | soc_writel(0, &timer->cntlo); | ||
86 | tcr |= timer64_mode; | ||
87 | soc_writel(tcr, &timer->tcr); | ||
88 | } | ||
89 | |||
90 | static void timer64_enable(void) | ||
91 | { | ||
92 | u32 val; | ||
93 | |||
94 | if (timer64_devstate_id >= 0) | ||
95 | dscr_set_devstate(timer64_devstate_id, DSCR_DEVSTATE_ENABLED); | ||
96 | |||
97 | /* disable timer, reset count */ | ||
98 | soc_writel(soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK, &timer->tcr); | ||
99 | soc_writel(0, &timer->prdlo); | ||
100 | |||
101 | /* use internal clock and 1 cycle pulse width */ | ||
102 | val = soc_readl(&timer->tcr); | ||
103 | soc_writel(val & ~(TCR_CLKSRCLO | TCR_PWIDLO_MASK), &timer->tcr); | ||
104 | |||
105 | /* dual 32-bit unchained mode */ | ||
106 | val = soc_readl(&timer->tgcr) & ~TGCR_TIMMODE_MASK; | ||
107 | soc_writel(val, &timer->tgcr); | ||
108 | soc_writel(val | (TGCR_TIMLORS | TGCR_TIMMODE_UD32), &timer->tgcr); | ||
109 | } | ||
110 | |||
111 | static void timer64_disable(void) | ||
112 | { | ||
113 | /* disable timer, reset count */ | ||
114 | soc_writel(soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK, &timer->tcr); | ||
115 | soc_writel(0, &timer->prdlo); | ||
116 | |||
117 | if (timer64_devstate_id >= 0) | ||
118 | dscr_set_devstate(timer64_devstate_id, DSCR_DEVSTATE_DISABLED); | ||
119 | } | ||
120 | |||
121 | static int next_event(unsigned long delta, | ||
122 | struct clock_event_device *evt) | ||
123 | { | ||
124 | timer64_config(delta); | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static void set_clock_mode(enum clock_event_mode mode, | ||
129 | struct clock_event_device *evt) | ||
130 | { | ||
131 | switch (mode) { | ||
132 | case CLOCK_EVT_MODE_PERIODIC: | ||
133 | timer64_enable(); | ||
134 | timer64_mode = TIMER64_MODE_PERIODIC; | ||
135 | timer64_config(TIMER64_RATE / HZ); | ||
136 | break; | ||
137 | case CLOCK_EVT_MODE_ONESHOT: | ||
138 | timer64_enable(); | ||
139 | timer64_mode = TIMER64_MODE_ONE_SHOT; | ||
140 | break; | ||
141 | case CLOCK_EVT_MODE_UNUSED: | ||
142 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
143 | timer64_mode = TIMER64_MODE_DISABLED; | ||
144 | timer64_disable(); | ||
145 | break; | ||
146 | case CLOCK_EVT_MODE_RESUME: | ||
147 | break; | ||
148 | } | ||
149 | } | ||
150 | |||
151 | static struct clock_event_device t64_clockevent_device = { | ||
152 | .name = "TIMER64_EVT32_TIMER", | ||
153 | .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, | ||
154 | .rating = 200, | ||
155 | .set_mode = set_clock_mode, | ||
156 | .set_next_event = next_event, | ||
157 | }; | ||
158 | |||
159 | static irqreturn_t timer_interrupt(int irq, void *dev_id) | ||
160 | { | ||
161 | struct clock_event_device *cd = &t64_clockevent_device; | ||
162 | |||
163 | cd->event_handler(cd); | ||
164 | |||
165 | return IRQ_HANDLED; | ||
166 | } | ||
167 | |||
168 | static struct irqaction timer_iact = { | ||
169 | .name = "timer", | ||
170 | .flags = IRQF_TIMER, | ||
171 | .handler = timer_interrupt, | ||
172 | .dev_id = &t64_clockevent_device, | ||
173 | }; | ||
174 | |||
175 | void __init timer64_init(void) | ||
176 | { | ||
177 | struct clock_event_device *cd = &t64_clockevent_device; | ||
178 | struct device_node *np, *first = NULL; | ||
179 | u32 val; | ||
180 | int err, found = 0; | ||
181 | |||
182 | for_each_compatible_node(np, NULL, "ti,c64x+timer64") { | ||
183 | err = of_property_read_u32(np, "ti,core-mask", &val); | ||
184 | if (!err) { | ||
185 | if (val & (1 << get_coreid())) { | ||
186 | found = 1; | ||
187 | break; | ||
188 | } | ||
189 | } else if (!first) | ||
190 | first = np; | ||
191 | } | ||
192 | if (!found) { | ||
193 | /* try first one with no core-mask */ | ||
194 | if (first) | ||
195 | np = of_node_get(first); | ||
196 | else { | ||
197 | pr_debug("Cannot find ti,c64x+timer64 timer.\n"); | ||
198 | return; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | timer = of_iomap(np, 0); | ||
203 | if (!timer) { | ||
204 | pr_debug("%s: Cannot map timer registers.\n", np->full_name); | ||
205 | goto out; | ||
206 | } | ||
207 | pr_debug("%s: Timer registers=%p.\n", np->full_name, timer); | ||
208 | |||
209 | cd->irq = irq_of_parse_and_map(np, 0); | ||
210 | if (cd->irq == NO_IRQ) { | ||
211 | pr_debug("%s: Cannot find interrupt.\n", np->full_name); | ||
212 | iounmap(timer); | ||
213 | goto out; | ||
214 | } | ||
215 | |||
216 | /* If there is a device state control, save the ID. */ | ||
217 | err = of_property_read_u32(np, "ti,dscr-dev-enable", &val); | ||
218 | if (!err) | ||
219 | timer64_devstate_id = val; | ||
220 | |||
221 | pr_debug("%s: Timer irq=%d.\n", np->full_name, cd->irq); | ||
222 | |||
223 | clockevents_calc_mult_shift(cd, c6x_core_freq / TIMER_DIVISOR, 5); | ||
224 | |||
225 | cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd); | ||
226 | cd->min_delta_ns = clockevent_delta2ns(250, cd); | ||
227 | |||
228 | cd->cpumask = cpumask_of(smp_processor_id()); | ||
229 | |||
230 | clockevents_register_device(cd); | ||
231 | setup_irq(cd->irq, &timer_iact); | ||
232 | |||
233 | out: | ||
234 | of_node_put(np); | ||
235 | return; | ||
236 | } | ||