armadaxp.c revision 1.1 1 /*******************************************************************************
2 Copyright (C) Marvell International Ltd. and its affiliates
3
4 Developed by Semihalf
5
6 ********************************************************************************
7 Marvell BSD License
8
9 If you received this File from Marvell, you may opt to use, redistribute and/or
10 modify this File under the following licensing terms.
11 Redistribution and use in source and binary forms, with or without modification,
12 are permitted provided that the following conditions are met:
13
14 * Redistributions of source code must retain the above copyright notice,
15 this list of conditions and the following disclaimer.
16
17 * Redistributions in binary form must reproduce the above copyright
18 notice, this list of conditions and the following disclaimer in the
19 documentation and/or other materials provided with the distribution.
20
21 * Neither the name of Marvell nor the names of its contributors may be
22 used to endorse or promote products derived from this software without
23 specific prior written permission.
24
25 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
26 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
29 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
32 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
36 *******************************************************************************/
37
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: armadaxp.c,v 1.1 2013/05/29 22:35:34 rkujawa Exp $");
40
41 #define _INTR_PRIVATE
42
43 #include "opt_mvsoc.h"
44
45 #include <sys/param.h>
46 #include <sys/bus.h>
47
48 #include <machine/intr.h>
49
50 #include <arm/pic/picvar.h>
51 #include <arm/pic/picvar.h>
52
53 #include <arm/armreg.h>
54 #include <arm/cpu.h>
55 #include <arm/cpufunc.h>
56
57 #include <arm/marvell/mvsocreg.h>
58 #include <arm/marvell/mvsocvar.h>
59 #include <evbarm/armadaxp/armadaxpreg.h>
60
61 #include <evbarm/marvell/marvellreg.h>
62 #include <dev/marvell/marvellreg.h>
63
64 #define EXTRACT_CPU_FREQ_FIELD(sar) (((0x01 & (sar >> 52)) << 3) | \
65 (0x07 & (sar >> 21)))
66 #define EXTRACT_FAB_FREQ_FIELD(sar) (((0x01 & (sar >> 51)) << 4) | \
67 (0x0F & (sar >> 24)))
68
69 #define MPIC_WRITE(reg, val) (bus_space_write_4(&mvsoc_bs_tag, \
70 mpic_handle, reg, val))
71 #define MPIC_CPU_WRITE(reg, val) (bus_space_write_4(&mvsoc_bs_tag, \
72 mpic_cpu_handle, reg, val))
73
74 #define MPIC_READ(reg) (bus_space_read_4(&mvsoc_bs_tag, \
75 mpic_handle, reg))
76 #define MPIC_CPU_READ(reg) (bus_space_read_4(&mvsoc_bs_tag, \
77 mpic_cpu_handle, reg))
78
79 #define L2_WRITE(reg, val) (bus_space_write_4(&mvsoc_bs_tag, \
80 l2_handle, reg, val))
81 #define L2_READ(reg) (bus_space_read_4(&mvsoc_bs_tag, \
82 l2_handle, reg))
83 bus_space_handle_t mpic_cpu_handle;
84 static bus_space_handle_t mpic_handle, l2_handle;
85 int l2cache_state = 0;
86 int iocc_state = 0;
87
88 extern void (*mvsoc_intr_init)(void);
89 static void armadaxp_intr_init(void);
90
91 static void armadaxp_pic_unblock_irqs(struct pic_softc *, size_t, uint32_t);
92 static void armadaxp_pic_block_irqs(struct pic_softc *, size_t, uint32_t);
93 static void armadaxp_pic_establish_irq(struct pic_softc *, struct intrsource *);
94
95 void armadaxp_handle_irq(void *);
96 void armadaxp_io_coherency_init(void);
97 int armadaxp_l2_init(void);
98
99 struct vco_freq_ratio {
100 uint8_t vco_cpu; /* VCO to CLK0(CPU) clock ratio */
101 uint8_t vco_l2c; /* VCO to NB(L2 cache) clock ratio */
102 uint8_t vco_hcl; /* VCO to HCLK(DDR controller) clock ratio */
103 uint8_t vco_ddr; /* VCO to DR(DDR memory) clock ratio */
104 };
105
106 static struct vco_freq_ratio freq_conf_table[] = {
107 /*00*/ { 1, 1, 4, 2 },
108 /*01*/ { 1, 2, 2, 2 },
109 /*02*/ { 2, 2, 6, 3 },
110 /*03*/ { 2, 2, 3, 3 },
111 /*04*/ { 1, 2, 3, 3 },
112 /*05*/ { 1, 2, 4, 2 },
113 /*06*/ { 1, 1, 2, 2 },
114 /*07*/ { 2, 3, 6, 6 },
115 /*08*/ { 2, 3, 5, 5 },
116 /*09*/ { 1, 2, 6, 3 },
117 /*10*/ { 2, 4, 10, 5 },
118 /*11*/ { 1, 3, 6, 6 },
119 /*12*/ { 1, 2, 5, 5 },
120 /*13*/ { 1, 3, 6, 3 },
121 /*14*/ { 1, 2, 5, 5 },
122 /*15*/ { 2, 2, 5, 5 },
123 /*16*/ { 1, 1, 3, 3 },
124 /*17*/ { 2, 5, 10, 10 },
125 /*18*/ { 1, 3, 8, 4 },
126 /*19*/ { 1, 1, 2, 1 },
127 /*20*/ { 2, 3, 6, 3 },
128 /*21*/ { 1, 2, 8, 4 },
129 /*22*/ { 2, 5, 10, 5 }
130 };
131
132 static uint16_t cpu_clock_table[] = {
133 1000, 1066, 1200, 1333, 1500, 1666, 1800, 2000, 600, 667, 800, 1600,
134 2133, 2200, 2400 };
135
136 static struct pic_ops armadaxp_picops = {
137 .pic_unblock_irqs = armadaxp_pic_unblock_irqs,
138 .pic_block_irqs = armadaxp_pic_block_irqs,
139 .pic_establish_irq = armadaxp_pic_establish_irq,
140 };
141
142 static struct pic_softc armadaxp_pic = {
143 .pic_ops = &armadaxp_picops,
144 .pic_name = "armadaxp",
145 };
146
147 /*
148 * armadaxp_intr_bootstrap:
149 *
150 * Initialize the rest of the interrupt subsystem, making it
151 * ready to handle interrupts from devices.
152 */
153 void
154 armadaxp_intr_bootstrap(void)
155 {
156 int i;
157
158 /* Map MPIC base and MPIC percpu base registers */
159 if (bus_space_map(&mvsoc_bs_tag, MARVELL_INTERREGS_PBASE +
160 ARMADAXP_MLMB_MPIC_BASE, 0x500, 0, &mpic_handle) != 0)
161 panic("%s: Could not map MPIC registers", __func__);
162 if (bus_space_map(&mvsoc_bs_tag, MARVELL_INTERREGS_PBASE +
163 ARMADAXP_MLMB_MPIC_CPU_BASE, 0x800, 0, &mpic_cpu_handle) != 0)
164 panic("%s: Could not map MPIC percpu registers", __func__);
165
166 /* Disable all interrupts */
167 for (i = 0; i < 116; i++)
168 MPIC_WRITE(ARMADAXP_MLMB_MPIC_ICE, i);
169
170 mvsoc_intr_init = armadaxp_intr_init;
171 }
172
173 static void
174 armadaxp_intr_init(void)
175 {
176 int ctrl;
177
178 /* Get max interrupts */
179 armadaxp_pic.pic_maxsources =
180 ((MPIC_READ(ARMADAXP_MLMB_MPIC_CTRL) >> 2) & 0x7FF);
181
182 if (!armadaxp_pic.pic_maxsources)
183 armadaxp_pic.pic_maxsources = 116;
184
185 pic_add(&armadaxp_pic, 0);
186
187 ctrl = MPIC_READ(ARMADAXP_MLMB_MPIC_CTRL);
188 /* Enable IRQ prioritization */
189 ctrl |= (1 << 0);
190 MPIC_WRITE(ARMADAXP_MLMB_MPIC_CTRL, ctrl);
191 MPIC_CPU_WRITE(ARMADAXP_MLMB_MPIC_CTP, curcpl() << MPIC_CTP_SHIFT);
192 }
193
194 static void
195 armadaxp_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase,
196 uint32_t irq_mask)
197 {
198 int n;
199
200 while (irq_mask != 0) {
201 n = ffs(irq_mask) - 1;
202 KASSERT(pic->pic_maxsources >= n + irqbase);
203 MPIC_WRITE(ARMADAXP_MLMB_MPIC_ISE, n + irqbase);
204 MPIC_CPU_WRITE(ARMADAXP_MLMB_MPIC_ICM, n + irqbase);
205 if ((n + irqbase) == 0)
206 MPIC_CPU_WRITE(ARMADAXP_MLMB_MPIC_DOORBELL_MASK,
207 0xffffffff);
208 irq_mask &= ~__BIT(n);
209 }
210 }
211
212 static void
213 armadaxp_pic_block_irqs(struct pic_softc *pic, size_t irqbase,
214 uint32_t irq_mask)
215 {
216 int n;
217
218 while (irq_mask != 0) {
219 n = ffs(irq_mask) - 1;
220 KASSERT(pic->pic_maxsources >= n + irqbase);
221 MPIC_WRITE(ARMADAXP_MLMB_MPIC_ICE, n + irqbase);
222 MPIC_CPU_WRITE(ARMADAXP_MLMB_MPIC_ISM, n + irqbase);
223 irq_mask &= ~__BIT(n);
224 }
225 }
226
227 static void
228 armadaxp_pic_establish_irq(struct pic_softc *pic, struct intrsource *is)
229 {
230 int tmp;
231 KASSERT(pic->pic_maxsources >= is->is_irq);
232 tmp = MPIC_READ(ARMADAXP_MLMB_MPIC_ISCR_BASE + is->is_irq * 4);
233 /* Clear previous priority */
234 tmp &= ~(0xf << MPIC_ISCR_SHIFT);
235 MPIC_WRITE(ARMADAXP_MLMB_MPIC_ISCR_BASE + is->is_irq * 4,
236 tmp | (is->is_ipl << MPIC_ISCR_SHIFT));
237 }
238
239 void
240 armadaxp_handle_irq(void *frame)
241 {
242 struct intrsource *is;
243 int irq;
244 u_int irqstate;
245
246 irq = MPIC_CPU_READ(ARMADAXP_MLMB_MPIC_IIACK) & 0x3ff;
247
248 /* Is it a spurious interrupt ?*/
249 if (irq == 0x3ff)
250 return;
251
252 is = armadaxp_pic.pic_sources[irq];
253 if (is != NULL) {
254 KASSERT(is->is_ipl > curcpu()->ci_cpl);
255 /* Dispatch irq */
256 irqstate = disable_interrupts(I32_bit);
257 pic_dispatch(is, frame);
258 restore_interrupts(irqstate);
259 }
260 #ifdef __HAVE_FAST_SOFTINTS
261 cpu_dosoftints();
262 #endif
263 }
264
265 /*
266 * Clock functions
267 */
268
269 void
270 armadaxp_getclks(void)
271 {
272 uint64_t sar_reg;
273 uint8_t sar_cpu_freq, sar_fab_freq, array_size;
274
275 if (cputype == CPU_ID_MV88SV584X_V7)
276 mvTclk = 250000000; /* 250 MHz */
277 else
278 mvTclk = 200000000; /* 200 MHz */
279
280 sar_reg = (read_miscreg(ARMADAXP_MISC_SAR_HI) << 31) |
281 read_miscreg(ARMADAXP_MISC_SAR_LO);
282
283 sar_cpu_freq = EXTRACT_CPU_FREQ_FIELD(sar_reg);
284 sar_fab_freq = EXTRACT_FAB_FREQ_FIELD(sar_reg);
285
286 /* Check if CPU frequency field has correct value */
287 array_size = sizeof(cpu_clock_table) / sizeof(cpu_clock_table[0]);
288 if (sar_cpu_freq >= array_size)
289 panic("Reserved value in cpu frequency configuration field: "
290 "%d", sar_cpu_freq);
291
292 /* Check if fabric frequency field has correct value */
293 array_size = sizeof(freq_conf_table) / sizeof(freq_conf_table[0]);
294 if (sar_fab_freq >= array_size)
295 panic("Reserved value in fabric frequency configuration field: "
296 "%d", sar_fab_freq);
297
298 /* Get CPU clock frequency */
299 mvPclk = cpu_clock_table[sar_cpu_freq] *
300 freq_conf_table[sar_fab_freq].vco_cpu;
301
302 /* Get L2CLK clock frequency and use as system clock (mvSysclk) */
303 mvSysclk = mvPclk / freq_conf_table[sar_fab_freq].vco_l2c;
304
305 /* Round mvSysclk value to integer MHz */
306 if (((mvPclk % freq_conf_table[sar_fab_freq].vco_l2c) * 10 /
307 freq_conf_table[sar_fab_freq].vco_l2c) >= 5)
308 mvSysclk++;
309
310 mvPclk = mvPclk * 1000000;
311 mvSysclk = mvSysclk * 1000000;
312 }
313
314 /*
315 * L2 Cache initialization
316 */
317
318 int
319 armadaxp_l2_init(void)
320 {
321 u_int32_t reg;
322 int ret;
323
324 /* Map L2 space */
325 ret = bus_space_map(&mvsoc_bs_tag, MARVELL_INTERREGS_PBASE +
326 ARMADAXP_L2_BASE, 0x1000, 0, &l2_handle);
327 if (ret) {
328 printf("%s: Cannot map L2 register space, ret:%d\n",
329 __func__, ret);
330 return (-1);
331 }
332
333 /* Set L2 policy */
334 reg = L2_READ(ARMADAXP_L2_AUX_CTRL);
335 reg &= ~(L2_WBWT_MODE_MASK);
336 reg &= ~(L2_REP_STRAT_MASK);
337 reg |= L2_REP_STRAT_SEMIPLRU;
338 L2_WRITE(ARMADAXP_L2_AUX_CTRL, reg);
339
340 /* Invalidate L2 cache */
341 L2_WRITE(ARMADAXP_L2_INV_WAY, L2_ALL_WAYS);
342
343 /* Clear pending L2 interrupts */
344 L2_WRITE(ARMADAXP_L2_INT_CAUSE, 0x1ff);
345
346 /* Enable Cache and TLB maintenance broadcast */
347 __asm__ __volatile__ ("mrc p15, 1, %0, c15, c2, 0" : "=r"(reg));
348 reg |= (1 << 8);
349 __asm__ __volatile__ ("mcr p15, 1, %0, c15, c2, 0" : :"r"(reg));
350
351 /*
352 * Set the Point of Coherency and Point of Unification to DRAM.
353 * This is a reset value but anyway, configure this just in case.
354 */
355 reg = read_mlmbreg(ARMADAXP_L2_CFU);
356 reg |= (1 << 17) | (1 << 18);
357 write_mlmbreg(ARMADAXP_L2_CFU, reg);
358
359 /* Enable L2 cache */
360 reg = L2_READ(ARMADAXP_L2_CTRL);
361 L2_WRITE(ARMADAXP_L2_CTRL, reg | L2_ENABLE);
362
363 /* Mark as enabled */
364 l2cache_state = 1;
365
366 #ifdef DEBUG
367 /* Configure and enable counter */
368 L2_WRITE(ARMADAXP_L2_CNTR_CONF(0), 0xf0000 | (4 << 2));
369 L2_WRITE(ARMADAXP_L2_CNTR_CONF(1), 0xf0000 | (2 << 2));
370 L2_WRITE(ARMADAXP_L2_CNTR_CTRL, 0x303);
371 #endif
372
373 return (0);
374 }
375
376 void
377 armadaxp_io_coherency_init(void)
378 {
379 uint32_t reg;
380
381 /* set CIB read snoop command to ReadUnique */
382 reg = read_mlmbreg(MVSOC_MLMB_CIB_CTRL_CFG);
383 reg &= ~(7 << 16);
384 reg |= (7 << 16);
385 write_mlmbreg(MVSOC_MLMB_CIB_CTRL_CFG, reg);
386 /* enable CPUs in SMP group on Fabric coherency */
387 reg = read_mlmbreg(MVSOC_MLMB_COHERENCY_FABRIC_CTRL);
388 reg &= ~(0x3 << 24);
389 reg |= (1 << 24);
390 write_mlmbreg(MVSOC_MLMB_COHERENCY_FABRIC_CTRL, reg);
391
392 reg = read_mlmbreg(MVSOC_MLMB_COHERENCY_FABRIC_CFG);
393 reg &= ~(0x3 << 24);
394 reg |= (1 << 24);
395 write_mlmbreg(MVSOC_MLMB_COHERENCY_FABRIC_CFG, reg);
396
397 /* Mark as enabled */
398 iocc_state = 1;
399 }
400