diff options
author | Anton Vorontsov <avorontsov@mvista.com> | 2010-03-25 10:12:41 -0400 |
---|---|---|
committer | Anton Vorontsov <avorontsov@mvista.com> | 2010-05-02 13:55:46 -0400 |
commit | d94f944e108da21badabd99f527b25e03b677b96 (patch) | |
tree | 3c150c38dcf4c6749eff881bacad872347ab3edd /arch/arm/mach-cns3xxx/core.c | |
parent | ea5ce655b90f5debb7b9768284eaafcca218ddef (diff) |
ARM: cns3xxx: Add basic support for Cavium Networks CNS3xxx processors
This patch adds very basic support for ECONA CNS3xxx ARM11 MPcore
(ARMv6) dual-core processors.
Note that SMP is not yet supported, as well as many peripheral
devices. Support for these features will be added later.
Signed-off-by: Anton Vorontsov <avorontsov@mvista.com>
Diffstat (limited to 'arch/arm/mach-cns3xxx/core.c')
-rw-r--r-- | arch/arm/mach-cns3xxx/core.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/arch/arm/mach-cns3xxx/core.c b/arch/arm/mach-cns3xxx/core.c new file mode 100644 index 000000000000..9ca4d581016f --- /dev/null +++ b/arch/arm/mach-cns3xxx/core.c | |||
@@ -0,0 +1,249 @@ | |||
1 | /* | ||
2 | * Copyright 1999 - 2003 ARM Limited | ||
3 | * Copyright 2000 Deep Blue Solutions Ltd | ||
4 | * Copyright 2008 Cavium Networks | ||
5 | * | ||
6 | * This file 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/init.h> | ||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/clockchips.h> | ||
14 | #include <linux/io.h> | ||
15 | #include <asm/mach/map.h> | ||
16 | #include <asm/mach/time.h> | ||
17 | #include <asm/mach/irq.h> | ||
18 | #include <asm/hardware/gic.h> | ||
19 | #include <mach/cns3xxx.h> | ||
20 | #include "core.h" | ||
21 | |||
22 | static struct map_desc cns3xxx_io_desc[] __initdata = { | ||
23 | { | ||
24 | .virtual = CNS3XXX_TC11MP_TWD_BASE_VIRT, | ||
25 | .pfn = __phys_to_pfn(CNS3XXX_TC11MP_TWD_BASE), | ||
26 | .length = SZ_4K, | ||
27 | .type = MT_DEVICE, | ||
28 | }, { | ||
29 | .virtual = CNS3XXX_TC11MP_GIC_CPU_BASE_VIRT, | ||
30 | .pfn = __phys_to_pfn(CNS3XXX_TC11MP_GIC_CPU_BASE), | ||
31 | .length = SZ_4K, | ||
32 | .type = MT_DEVICE, | ||
33 | }, { | ||
34 | .virtual = CNS3XXX_TC11MP_GIC_DIST_BASE_VIRT, | ||
35 | .pfn = __phys_to_pfn(CNS3XXX_TC11MP_GIC_DIST_BASE), | ||
36 | .length = SZ_4K, | ||
37 | .type = MT_DEVICE, | ||
38 | }, { | ||
39 | .virtual = CNS3XXX_TIMER1_2_3_BASE_VIRT, | ||
40 | .pfn = __phys_to_pfn(CNS3XXX_TIMER1_2_3_BASE), | ||
41 | .length = SZ_4K, | ||
42 | .type = MT_DEVICE, | ||
43 | }, { | ||
44 | .virtual = CNS3XXX_GPIOA_BASE_VIRT, | ||
45 | .pfn = __phys_to_pfn(CNS3XXX_GPIOA_BASE), | ||
46 | .length = SZ_4K, | ||
47 | .type = MT_DEVICE, | ||
48 | }, { | ||
49 | .virtual = CNS3XXX_GPIOB_BASE_VIRT, | ||
50 | .pfn = __phys_to_pfn(CNS3XXX_GPIOB_BASE), | ||
51 | .length = SZ_4K, | ||
52 | .type = MT_DEVICE, | ||
53 | }, { | ||
54 | .virtual = CNS3XXX_MISC_BASE_VIRT, | ||
55 | .pfn = __phys_to_pfn(CNS3XXX_MISC_BASE), | ||
56 | .length = SZ_4K, | ||
57 | .type = MT_DEVICE, | ||
58 | }, { | ||
59 | .virtual = CNS3XXX_PM_BASE_VIRT, | ||
60 | .pfn = __phys_to_pfn(CNS3XXX_PM_BASE), | ||
61 | .length = SZ_4K, | ||
62 | .type = MT_DEVICE, | ||
63 | }, | ||
64 | }; | ||
65 | |||
66 | void __init cns3xxx_map_io(void) | ||
67 | { | ||
68 | iotable_init(cns3xxx_io_desc, ARRAY_SIZE(cns3xxx_io_desc)); | ||
69 | } | ||
70 | |||
71 | /* used by entry-macro.S */ | ||
72 | void __iomem *gic_cpu_base_addr; | ||
73 | |||
74 | void __init cns3xxx_init_irq(void) | ||
75 | { | ||
76 | gic_cpu_base_addr = __io(CNS3XXX_TC11MP_GIC_CPU_BASE_VIRT); | ||
77 | gic_dist_init(0, __io(CNS3XXX_TC11MP_GIC_DIST_BASE_VIRT), 29); | ||
78 | gic_cpu_init(0, gic_cpu_base_addr); | ||
79 | } | ||
80 | |||
81 | void cns3xxx_power_off(void) | ||
82 | { | ||
83 | u32 __iomem *pm_base = __io(CNS3XXX_PM_BASE_VIRT); | ||
84 | u32 clkctrl; | ||
85 | |||
86 | printk(KERN_INFO "powering system down...\n"); | ||
87 | |||
88 | clkctrl = readl(pm_base + PM_SYS_CLK_CTRL_OFFSET); | ||
89 | clkctrl &= 0xfffff1ff; | ||
90 | clkctrl |= (0x5 << 9); /* Hibernate */ | ||
91 | writel(clkctrl, pm_base + PM_SYS_CLK_CTRL_OFFSET); | ||
92 | |||
93 | } | ||
94 | |||
95 | /* | ||
96 | * Timer | ||
97 | */ | ||
98 | static void __iomem *cns3xxx_tmr1; | ||
99 | |||
100 | static void cns3xxx_timer_set_mode(enum clock_event_mode mode, | ||
101 | struct clock_event_device *clk) | ||
102 | { | ||
103 | unsigned long ctrl = readl(cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET); | ||
104 | int pclk = cns3xxx_cpu_clock() / 8; | ||
105 | int reload; | ||
106 | |||
107 | switch (mode) { | ||
108 | case CLOCK_EVT_MODE_PERIODIC: | ||
109 | reload = pclk * 20 / (3 * HZ) * 0x25000; | ||
110 | writel(reload, cns3xxx_tmr1 + TIMER1_AUTO_RELOAD_OFFSET); | ||
111 | ctrl |= (1 << 0) | (1 << 2) | (1 << 9); | ||
112 | break; | ||
113 | case CLOCK_EVT_MODE_ONESHOT: | ||
114 | /* period set, and timer enabled in 'next_event' hook */ | ||
115 | ctrl |= (1 << 2) | (1 << 9); | ||
116 | break; | ||
117 | case CLOCK_EVT_MODE_UNUSED: | ||
118 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
119 | default: | ||
120 | ctrl = 0; | ||
121 | } | ||
122 | |||
123 | writel(ctrl, cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET); | ||
124 | } | ||
125 | |||
126 | static int cns3xxx_timer_set_next_event(unsigned long evt, | ||
127 | struct clock_event_device *unused) | ||
128 | { | ||
129 | unsigned long ctrl = readl(cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET); | ||
130 | |||
131 | writel(evt, cns3xxx_tmr1 + TIMER1_AUTO_RELOAD_OFFSET); | ||
132 | writel(ctrl | (1 << 0), cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET); | ||
133 | |||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | static struct clock_event_device cns3xxx_tmr1_clockevent = { | ||
138 | .name = "cns3xxx timer1", | ||
139 | .shift = 8, | ||
140 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
141 | .set_mode = cns3xxx_timer_set_mode, | ||
142 | .set_next_event = cns3xxx_timer_set_next_event, | ||
143 | .rating = 350, | ||
144 | .cpumask = cpu_all_mask, | ||
145 | }; | ||
146 | |||
147 | static void __init cns3xxx_clockevents_init(unsigned int timer_irq) | ||
148 | { | ||
149 | cns3xxx_tmr1_clockevent.irq = timer_irq; | ||
150 | cns3xxx_tmr1_clockevent.mult = | ||
151 | div_sc((cns3xxx_cpu_clock() >> 3) * 1000000, NSEC_PER_SEC, | ||
152 | cns3xxx_tmr1_clockevent.shift); | ||
153 | cns3xxx_tmr1_clockevent.max_delta_ns = | ||
154 | clockevent_delta2ns(0xffffffff, &cns3xxx_tmr1_clockevent); | ||
155 | cns3xxx_tmr1_clockevent.min_delta_ns = | ||
156 | clockevent_delta2ns(0xf, &cns3xxx_tmr1_clockevent); | ||
157 | |||
158 | clockevents_register_device(&cns3xxx_tmr1_clockevent); | ||
159 | } | ||
160 | |||
161 | /* | ||
162 | * IRQ handler for the timer | ||
163 | */ | ||
164 | static irqreturn_t cns3xxx_timer_interrupt(int irq, void *dev_id) | ||
165 | { | ||
166 | struct clock_event_device *evt = &cns3xxx_tmr1_clockevent; | ||
167 | u32 __iomem *stat = cns3xxx_tmr1 + TIMER1_2_INTERRUPT_STATUS_OFFSET; | ||
168 | u32 val; | ||
169 | |||
170 | /* Clear the interrupt */ | ||
171 | val = readl(stat); | ||
172 | writel(val & ~(1 << 2), stat); | ||
173 | |||
174 | evt->event_handler(evt); | ||
175 | |||
176 | return IRQ_HANDLED; | ||
177 | } | ||
178 | |||
179 | static struct irqaction cns3xxx_timer_irq = { | ||
180 | .name = "timer", | ||
181 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | ||
182 | .handler = cns3xxx_timer_interrupt, | ||
183 | }; | ||
184 | |||
185 | /* | ||
186 | * Set up the clock source and clock events devices | ||
187 | */ | ||
188 | static void __init __cns3xxx_timer_init(unsigned int timer_irq) | ||
189 | { | ||
190 | u32 val; | ||
191 | u32 irq_mask; | ||
192 | |||
193 | /* | ||
194 | * Initialise to a known state (all timers off) | ||
195 | */ | ||
196 | |||
197 | /* disable timer1 and timer2 */ | ||
198 | writel(0, cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET); | ||
199 | /* stop free running timer3 */ | ||
200 | writel(0, cns3xxx_tmr1 + TIMER_FREERUN_CONTROL_OFFSET); | ||
201 | |||
202 | /* timer1 */ | ||
203 | writel(0x5C800, cns3xxx_tmr1 + TIMER1_COUNTER_OFFSET); | ||
204 | writel(0x5C800, cns3xxx_tmr1 + TIMER1_AUTO_RELOAD_OFFSET); | ||
205 | |||
206 | writel(0, cns3xxx_tmr1 + TIMER1_MATCH_V1_OFFSET); | ||
207 | writel(0, cns3xxx_tmr1 + TIMER1_MATCH_V2_OFFSET); | ||
208 | |||
209 | /* mask irq, non-mask timer1 overflow */ | ||
210 | irq_mask = readl(cns3xxx_tmr1 + TIMER1_2_INTERRUPT_MASK_OFFSET); | ||
211 | irq_mask &= ~(1 << 2); | ||
212 | irq_mask |= 0x03; | ||
213 | writel(irq_mask, cns3xxx_tmr1 + TIMER1_2_INTERRUPT_MASK_OFFSET); | ||
214 | |||
215 | /* down counter */ | ||
216 | val = readl(cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET); | ||
217 | val |= (1 << 9); | ||
218 | writel(val, cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET); | ||
219 | |||
220 | /* timer2 */ | ||
221 | writel(0, cns3xxx_tmr1 + TIMER2_MATCH_V1_OFFSET); | ||
222 | writel(0, cns3xxx_tmr1 + TIMER2_MATCH_V2_OFFSET); | ||
223 | |||
224 | /* mask irq */ | ||
225 | irq_mask = readl(cns3xxx_tmr1 + TIMER1_2_INTERRUPT_MASK_OFFSET); | ||
226 | irq_mask |= ((1 << 3) | (1 << 4) | (1 << 5)); | ||
227 | writel(irq_mask, cns3xxx_tmr1 + TIMER1_2_INTERRUPT_MASK_OFFSET); | ||
228 | |||
229 | /* down counter */ | ||
230 | val = readl(cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET); | ||
231 | val |= (1 << 10); | ||
232 | writel(val, cns3xxx_tmr1 + TIMER1_2_CONTROL_OFFSET); | ||
233 | |||
234 | /* Make irqs happen for the system timer */ | ||
235 | setup_irq(timer_irq, &cns3xxx_timer_irq); | ||
236 | |||
237 | cns3xxx_clockevents_init(timer_irq); | ||
238 | } | ||
239 | |||
240 | static void __init cns3xxx_timer_init(void) | ||
241 | { | ||
242 | cns3xxx_tmr1 = __io(CNS3XXX_TIMER1_2_3_BASE_VIRT); | ||
243 | |||
244 | __cns3xxx_timer_init(IRQ_CNS3XXX_TIMER0); | ||
245 | } | ||
246 | |||
247 | struct sys_timer cns3xxx_timer = { | ||
248 | .init = cns3xxx_timer_init, | ||
249 | }; | ||