diff options
author | Hans J. Koch <hjk@linutronix.de> | 2010-09-17 12:17:42 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2010-09-17 15:55:08 -0400 |
commit | 3de7b517dfacf1deb0690dbac28f917643e49975 (patch) | |
tree | 77548d323cef82e380f354f98132dc779aee0919 /arch/arm/mach-tcc8k | |
parent | e9268ef2252c2dfc7e2d0c435826768bb0e549ea (diff) |
ARM: Add TCC8xxx system timer
Add the system timer using clockevents with the internal TC32 timer.
This also adds a clocksource using the same timer.
Signed-off-by: "Hans J. Koch" <hjk@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch/arm/mach-tcc8k')
-rw-r--r-- | arch/arm/mach-tcc8k/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-tcc8k/clock.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-tcc8k/common.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-tcc8k/time.c | 149 |
4 files changed, 154 insertions, 1 deletions
diff --git a/arch/arm/mach-tcc8k/Makefile b/arch/arm/mach-tcc8k/Makefile index 53bc2f58549b..b4a12f294c57 100644 --- a/arch/arm/mach-tcc8k/Makefile +++ b/arch/arm/mach-tcc8k/Makefile | |||
@@ -3,4 +3,4 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | # Common support | 5 | # Common support |
6 | obj-y += clock.o irq.o | 6 | obj-y += clock.o irq.o time.o |
diff --git a/arch/arm/mach-tcc8k/clock.c b/arch/arm/mach-tcc8k/clock.c index a8982af15326..ba32a15127ab 100644 --- a/arch/arm/mach-tcc8k/clock.c +++ b/arch/arm/mach-tcc8k/clock.c | |||
@@ -563,4 +563,5 @@ void __init tcc_clocks_init(unsigned long xi_freq, unsigned long xti_freq) | |||
563 | find_aclk_parent(lookups[i].clk); | 563 | find_aclk_parent(lookups[i].clk); |
564 | clkdev_add(&lookups[i]); | 564 | clkdev_add(&lookups[i]); |
565 | } | 565 | } |
566 | tcc8k_timer_init(&tcz, (void __iomem *)TIMER_BASE, INT_TC32); | ||
566 | } | 567 | } |
diff --git a/arch/arm/mach-tcc8k/common.h b/arch/arm/mach-tcc8k/common.h index e539548e58d2..858210ecd67a 100644 --- a/arch/arm/mach-tcc8k/common.h +++ b/arch/arm/mach-tcc8k/common.h | |||
@@ -1,7 +1,10 @@ | |||
1 | #ifndef MACH_TCC8K_COMMON_H | 1 | #ifndef MACH_TCC8K_COMMON_H |
2 | #define MACH_TCC8K_COMMON_H | 2 | #define MACH_TCC8K_COMMON_H |
3 | 3 | ||
4 | struct clk; | ||
5 | |||
4 | extern void tcc_clocks_init(unsigned long xi_freq, unsigned long xti_freq); | 6 | extern void tcc_clocks_init(unsigned long xi_freq, unsigned long xti_freq); |
7 | extern void tcc8k_timer_init(struct clk *clock, void __iomem *base, int irq); | ||
5 | extern void tcc8k_init_irq(void); | 8 | extern void tcc8k_init_irq(void); |
6 | 9 | ||
7 | #endif | 10 | #endif |
diff --git a/arch/arm/mach-tcc8k/time.c b/arch/arm/mach-tcc8k/time.c new file mode 100644 index 000000000000..78d06008841d --- /dev/null +++ b/arch/arm/mach-tcc8k/time.c | |||
@@ -0,0 +1,149 @@ | |||
1 | /* | ||
2 | * TCC8000 system timer setup | ||
3 | * | ||
4 | * (C) 2009 Hans J. Koch <hjk@linutronix.de> | ||
5 | * | ||
6 | * Licensed under the terms of the GPL version 2. | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include <linux/clk.h> | ||
11 | #include <linux/clockchips.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/interrupt.h> | ||
14 | #include <linux/io.h> | ||
15 | #include <linux/irq.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/spinlock.h> | ||
18 | |||
19 | #include <asm/mach/time.h> | ||
20 | |||
21 | #include <mach/tcc8k-regs.h> | ||
22 | #include <mach/irqs.h> | ||
23 | |||
24 | #include "common.h" | ||
25 | |||
26 | static void __iomem *timer_base; | ||
27 | |||
28 | static cycle_t tcc_get_cycles(struct clocksource *cs) | ||
29 | { | ||
30 | return __raw_readl(timer_base + TC32MCNT_OFFS); | ||
31 | } | ||
32 | |||
33 | static struct clocksource clocksource_tcc = { | ||
34 | .name = "tcc_tc32", | ||
35 | .rating = 200, | ||
36 | .read = tcc_get_cycles, | ||
37 | .mask = CLOCKSOURCE_MASK(32), | ||
38 | .shift = 28, | ||
39 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
40 | }; | ||
41 | |||
42 | static int tcc_set_next_event(unsigned long evt, | ||
43 | struct clock_event_device *unused) | ||
44 | { | ||
45 | unsigned long reg = __raw_readl(timer_base + TC32MCNT_OFFS); | ||
46 | |||
47 | __raw_writel(reg + evt, timer_base + TC32CMP0_OFFS); | ||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | static void tcc_set_mode(enum clock_event_mode mode, | ||
52 | struct clock_event_device *evt) | ||
53 | { | ||
54 | unsigned long tc32irq; | ||
55 | |||
56 | switch (mode) { | ||
57 | case CLOCK_EVT_MODE_ONESHOT: | ||
58 | tc32irq = __raw_readl(timer_base + TC32IRQ_OFFS); | ||
59 | tc32irq |= TC32IRQ_IRQEN0; | ||
60 | __raw_writel(tc32irq, timer_base + TC32IRQ_OFFS); | ||
61 | break; | ||
62 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
63 | case CLOCK_EVT_MODE_UNUSED: | ||
64 | tc32irq = __raw_readl(timer_base + TC32IRQ_OFFS); | ||
65 | tc32irq &= ~TC32IRQ_IRQEN0; | ||
66 | __raw_writel(tc32irq, timer_base + TC32IRQ_OFFS); | ||
67 | break; | ||
68 | case CLOCK_EVT_MODE_PERIODIC: | ||
69 | case CLOCK_EVT_MODE_RESUME: | ||
70 | break; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | static irqreturn_t tcc8k_timer_interrupt(int irq, void *dev_id) | ||
75 | { | ||
76 | struct clock_event_device *evt = dev_id; | ||
77 | |||
78 | /* Acknowledge TC32 interrupt by reading TC32IRQ */ | ||
79 | __raw_readl(timer_base + TC32IRQ_OFFS); | ||
80 | |||
81 | evt->event_handler(evt); | ||
82 | |||
83 | return IRQ_HANDLED; | ||
84 | } | ||
85 | |||
86 | static struct clock_event_device clockevent_tcc = { | ||
87 | .name = "tcc_timer1", | ||
88 | .features = CLOCK_EVT_FEAT_ONESHOT, | ||
89 | .shift = 32, | ||
90 | .set_mode = tcc_set_mode, | ||
91 | .set_next_event = tcc_set_next_event, | ||
92 | .rating = 200, | ||
93 | }; | ||
94 | |||
95 | static struct irqaction tcc8k_timer_irq = { | ||
96 | .name = "TC32_timer", | ||
97 | .flags = IRQF_DISABLED | IRQF_TIMER, | ||
98 | .handler = tcc8k_timer_interrupt, | ||
99 | .dev_id = &clockevent_tcc, | ||
100 | }; | ||
101 | |||
102 | static int __init tcc_clockevent_init(struct clk *clock) | ||
103 | { | ||
104 | unsigned int c = clk_get_rate(clock); | ||
105 | |||
106 | clocksource_tcc.mult = clocksource_hz2mult(c, | ||
107 | clocksource_tcc.shift); | ||
108 | clocksource_register(&clocksource_tcc); | ||
109 | |||
110 | clockevent_tcc.mult = div_sc(c, NSEC_PER_SEC, | ||
111 | clockevent_tcc.shift); | ||
112 | clockevent_tcc.max_delta_ns = | ||
113 | clockevent_delta2ns(0xfffffffe, &clockevent_tcc); | ||
114 | clockevent_tcc.min_delta_ns = | ||
115 | clockevent_delta2ns(0xff, &clockevent_tcc); | ||
116 | |||
117 | clockevent_tcc.cpumask = cpumask_of(0); | ||
118 | |||
119 | clockevents_register_device(&clockevent_tcc); | ||
120 | |||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | void __init tcc8k_timer_init(struct clk *clock, void __iomem *base, int irq) | ||
125 | { | ||
126 | u32 reg; | ||
127 | |||
128 | timer_base = base; | ||
129 | tcc8k_timer_irq.irq = irq; | ||
130 | |||
131 | /* Enable clocks */ | ||
132 | clk_enable(clock); | ||
133 | |||
134 | /* Initialize 32-bit timer */ | ||
135 | reg = __raw_readl(timer_base + TC32EN_OFFS); | ||
136 | reg &= ~TC32EN_ENABLE; /* Disable timer */ | ||
137 | __raw_writel(reg, timer_base + TC32EN_OFFS); | ||
138 | /* Free running timer, counting from 0 to 0xffffffff */ | ||
139 | __raw_writel(0, timer_base + TC32EN_OFFS); | ||
140 | __raw_writel(0, timer_base + TC32LDV_OFFS); | ||
141 | reg = __raw_readl(timer_base + TC32IRQ_OFFS); | ||
142 | reg |= TC32IRQ_IRQEN0; /* irq at match with CMP0 */ | ||
143 | __raw_writel(reg, timer_base + TC32IRQ_OFFS); | ||
144 | |||
145 | __raw_writel(TC32EN_ENABLE, timer_base + TC32EN_OFFS); | ||
146 | |||
147 | tcc_clockevent_init(clock); | ||
148 | setup_irq(irq, &tcc8k_timer_irq); | ||
149 | } | ||