blob: dfca596a9a276b90a76b6e02a6528c80fd8acdb5 [file] [log] [blame]
Lennert Buytenhekd01e8892005-11-01 19:53:50 +00001/*
2 * Generic library functions for the microengines found on the Intel
3 * IXP2000 series of network processors.
4 *
5 * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
6 * Dedicated to Marija Kulikova.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as
10 * published by the Free Software Foundation; either version 2.1 of the
11 * License, or (at your option) any later version.
12 */
13
14#include <linux/config.h>
15#include <linux/kernel.h>
16#include <linux/init.h>
17#include <linux/slab.h>
18#include <linux/module.h>
19#include <linux/string.h>
20#include <asm/hardware.h>
Lennert Buytenhek8b76a682006-06-22 10:30:56 +010021#include <asm/arch/hardware.h>
Lennert Buytenhek18ec5c72006-03-20 17:10:17 +000022#include <asm/hardware/uengine.h>
Lennert Buytenhekd01e8892005-11-01 19:53:50 +000023#include <asm/io.h>
24
Lennert Buytenhek8b76a682006-06-22 10:30:56 +010025#if defined(CONFIG_ARCH_IXP2000)
26#define IXP_UENGINE_CSR_VIRT_BASE IXP2000_UENGINE_CSR_VIRT_BASE
27#define IXP_PRODUCT_ID IXP2000_PRODUCT_ID
28#define IXP_MISC_CONTROL IXP2000_MISC_CONTROL
29#define IXP_RESET1 IXP2000_RESET1
30#else
31#if defined(CONFIG_ARCH_IXP23XX)
32#define IXP_UENGINE_CSR_VIRT_BASE IXP23XX_UENGINE_CSR_VIRT_BASE
33#define IXP_PRODUCT_ID IXP23XX_PRODUCT_ID
34#define IXP_MISC_CONTROL IXP23XX_MISC_CONTROL
35#define IXP_RESET1 IXP23XX_RESET1
36#else
37#error unknown platform
38#endif
39#endif
40
Lennert Buytenhekd01e8892005-11-01 19:53:50 +000041#define USTORE_ADDRESS 0x000
42#define USTORE_DATA_LOWER 0x004
43#define USTORE_DATA_UPPER 0x008
44#define CTX_ENABLES 0x018
45#define CC_ENABLE 0x01c
46#define CSR_CTX_POINTER 0x020
47#define INDIRECT_CTX_STS 0x040
48#define ACTIVE_CTX_STS 0x044
49#define INDIRECT_CTX_SIG_EVENTS 0x048
50#define INDIRECT_CTX_WAKEUP_EVENTS 0x050
51#define NN_PUT 0x080
52#define NN_GET 0x084
53#define TIMESTAMP_LOW 0x0c0
54#define TIMESTAMP_HIGH 0x0c4
55#define T_INDEX_BYTE_INDEX 0x0f4
56#define LOCAL_CSR_STATUS 0x180
57
58u32 ixp2000_uengine_mask;
59
60static void *ixp2000_uengine_csr_area(int uengine)
61{
Lennert Buytenhek8b76a682006-06-22 10:30:56 +010062 return ((void *)IXP_UENGINE_CSR_VIRT_BASE) + (uengine << 10);
Lennert Buytenhekd01e8892005-11-01 19:53:50 +000063}
64
65/*
66 * LOCAL_CSR_STATUS=1 after a read or write to a microengine's CSR
67 * space means that the microengine we tried to access was also trying
68 * to access its own CSR space on the same clock cycle as we did. When
69 * this happens, we lose the arbitration process by default, and the
70 * read or write we tried to do was not actually performed, so we try
71 * again until it succeeds.
72 */
73u32 ixp2000_uengine_csr_read(int uengine, int offset)
74{
75 void *uebase;
76 u32 *local_csr_status;
77 u32 *reg;
78 u32 value;
79
80 uebase = ixp2000_uengine_csr_area(uengine);
81
82 local_csr_status = (u32 *)(uebase + LOCAL_CSR_STATUS);
83 reg = (u32 *)(uebase + offset);
84 do {
85 value = ixp2000_reg_read(reg);
86 } while (ixp2000_reg_read(local_csr_status) & 1);
87
88 return value;
89}
90EXPORT_SYMBOL(ixp2000_uengine_csr_read);
91
92void ixp2000_uengine_csr_write(int uengine, int offset, u32 value)
93{
94 void *uebase;
95 u32 *local_csr_status;
96 u32 *reg;
97
98 uebase = ixp2000_uengine_csr_area(uengine);
99
100 local_csr_status = (u32 *)(uebase + LOCAL_CSR_STATUS);
101 reg = (u32 *)(uebase + offset);
102 do {
103 ixp2000_reg_write(reg, value);
104 } while (ixp2000_reg_read(local_csr_status) & 1);
105}
106EXPORT_SYMBOL(ixp2000_uengine_csr_write);
107
108void ixp2000_uengine_reset(u32 uengine_mask)
109{
Lennert Buytenhek8b76a682006-06-22 10:30:56 +0100110 u32 value;
111
112 value = ixp2000_reg_read(IXP_RESET1) & ~ixp2000_uengine_mask;
113
114 uengine_mask &= ixp2000_uengine_mask;
115 ixp2000_reg_wrb(IXP_RESET1, value | uengine_mask);
116 ixp2000_reg_wrb(IXP_RESET1, value);
Lennert Buytenhekd01e8892005-11-01 19:53:50 +0000117}
118EXPORT_SYMBOL(ixp2000_uengine_reset);
119
120void ixp2000_uengine_set_mode(int uengine, u32 mode)
121{
122 /*
123 * CTL_STR_PAR_EN: unconditionally enable parity checking on
124 * control store.
125 */
126 mode |= 0x10000000;
127 ixp2000_uengine_csr_write(uengine, CTX_ENABLES, mode);
128
129 /*
130 * Enable updating of condition codes.
131 */
132 ixp2000_uengine_csr_write(uengine, CC_ENABLE, 0x00002000);
133
134 /*
135 * Initialise other per-microengine registers.
136 */
137 ixp2000_uengine_csr_write(uengine, NN_PUT, 0x00);
138 ixp2000_uengine_csr_write(uengine, NN_GET, 0x00);
139 ixp2000_uengine_csr_write(uengine, T_INDEX_BYTE_INDEX, 0);
140}
141EXPORT_SYMBOL(ixp2000_uengine_set_mode);
142
143static int make_even_parity(u32 x)
144{
145 return hweight32(x) & 1;
146}
147
148static void ustore_write(int uengine, u64 insn)
149{
150 /*
151 * Generate even parity for top and bottom 20 bits.
152 */
153 insn |= (u64)make_even_parity((insn >> 20) & 0x000fffff) << 41;
154 insn |= (u64)make_even_parity(insn & 0x000fffff) << 40;
155
156 /*
157 * Write to microstore. The second write auto-increments
158 * the USTORE_ADDRESS index register.
159 */
160 ixp2000_uengine_csr_write(uengine, USTORE_DATA_LOWER, (u32)insn);
161 ixp2000_uengine_csr_write(uengine, USTORE_DATA_UPPER, (u32)(insn >> 32));
162}
163
164void ixp2000_uengine_load_microcode(int uengine, u8 *ucode, int insns)
165{
166 int i;
167
168 /*
169 * Start writing to microstore at address 0.
170 */
171 ixp2000_uengine_csr_write(uengine, USTORE_ADDRESS, 0x80000000);
172 for (i = 0; i < insns; i++) {
173 u64 insn;
174
175 insn = (((u64)ucode[0]) << 32) |
176 (((u64)ucode[1]) << 24) |
177 (((u64)ucode[2]) << 16) |
178 (((u64)ucode[3]) << 8) |
179 ((u64)ucode[4]);
180 ucode += 5;
181
182 ustore_write(uengine, insn);
183 }
184
185 /*
186 * Pad with a few NOPs at the end (to avoid the microengine
187 * aborting as it prefetches beyond the last instruction), unless
188 * we run off the end of the instruction store first, at which
189 * point the address register will wrap back to zero.
190 */
191 for (i = 0; i < 4; i++) {
192 u32 addr;
193
194 addr = ixp2000_uengine_csr_read(uengine, USTORE_ADDRESS);
195 if (addr == 0x80000000)
196 break;
197 ustore_write(uengine, 0xf0000c0300ULL);
198 }
199
200 /*
201 * End programming.
202 */
203 ixp2000_uengine_csr_write(uengine, USTORE_ADDRESS, 0x00000000);
204}
205EXPORT_SYMBOL(ixp2000_uengine_load_microcode);
206
207void ixp2000_uengine_init_context(int uengine, int context, int pc)
208{
209 /*
210 * Select the right context for indirect access.
211 */
212 ixp2000_uengine_csr_write(uengine, CSR_CTX_POINTER, context);
213
214 /*
215 * Initialise signal masks to immediately go to Ready state.
216 */
217 ixp2000_uengine_csr_write(uengine, INDIRECT_CTX_SIG_EVENTS, 1);
218 ixp2000_uengine_csr_write(uengine, INDIRECT_CTX_WAKEUP_EVENTS, 1);
219
220 /*
221 * Set program counter.
222 */
223 ixp2000_uengine_csr_write(uengine, INDIRECT_CTX_STS, pc);
224}
225EXPORT_SYMBOL(ixp2000_uengine_init_context);
226
227void ixp2000_uengine_start_contexts(int uengine, u8 ctx_mask)
228{
229 u32 mask;
230
231 /*
232 * Enable the specified context to go to Executing state.
233 */
234 mask = ixp2000_uengine_csr_read(uengine, CTX_ENABLES);
235 mask |= ctx_mask << 8;
236 ixp2000_uengine_csr_write(uengine, CTX_ENABLES, mask);
237}
238EXPORT_SYMBOL(ixp2000_uengine_start_contexts);
239
240void ixp2000_uengine_stop_contexts(int uengine, u8 ctx_mask)
241{
242 u32 mask;
243
244 /*
245 * Disable the Ready->Executing transition. Note that this
246 * does not stop the context until it voluntarily yields.
247 */
248 mask = ixp2000_uengine_csr_read(uengine, CTX_ENABLES);
249 mask &= ~(ctx_mask << 8);
250 ixp2000_uengine_csr_write(uengine, CTX_ENABLES, mask);
251}
252EXPORT_SYMBOL(ixp2000_uengine_stop_contexts);
253
254static int check_ixp_type(struct ixp2000_uengine_code *c)
255{
256 u32 product_id;
257 u32 rev;
258
Lennert Buytenhek8b76a682006-06-22 10:30:56 +0100259 product_id = ixp2000_reg_read(IXP_PRODUCT_ID);
Lennert Buytenhekd01e8892005-11-01 19:53:50 +0000260 if (((product_id >> 16) & 0x1f) != 0)
261 return 0;
262
263 switch ((product_id >> 8) & 0xff) {
Lennert Buytenhek8b76a682006-06-22 10:30:56 +0100264#ifdef CONFIG_ARCH_IXP2000
Lennert Buytenhekd01e8892005-11-01 19:53:50 +0000265 case 0: /* IXP2800 */
266 if (!(c->cpu_model_bitmask & 4))
267 return 0;
268 break;
269
270 case 1: /* IXP2850 */
271 if (!(c->cpu_model_bitmask & 8))
272 return 0;
273 break;
274
275 case 2: /* IXP2400 */
276 if (!(c->cpu_model_bitmask & 2))
277 return 0;
278 break;
Lennert Buytenhek8b76a682006-06-22 10:30:56 +0100279#endif
280
281#ifdef CONFIG_ARCH_IXP23XX
282 case 4: /* IXP23xx */
283 if (!(c->cpu_model_bitmask & 0x3f0))
284 return 0;
285 break;
286#endif
Lennert Buytenhekd01e8892005-11-01 19:53:50 +0000287
288 default:
289 return 0;
290 }
291
292 rev = product_id & 0xff;
293 if (rev < c->cpu_min_revision || rev > c->cpu_max_revision)
294 return 0;
295
296 return 1;
297}
298
299static void generate_ucode(u8 *ucode, u32 *gpr_a, u32 *gpr_b)
300{
301 int offset;
302 int i;
303
304 offset = 0;
305
306 for (i = 0; i < 128; i++) {
307 u8 b3;
308 u8 b2;
309 u8 b1;
310 u8 b0;
311
312 b3 = (gpr_a[i] >> 24) & 0xff;
313 b2 = (gpr_a[i] >> 16) & 0xff;
314 b1 = (gpr_a[i] >> 8) & 0xff;
315 b0 = gpr_a[i] & 0xff;
316
317 // immed[@ai, (b1 << 8) | b0]
318 // 11110000 0000VVVV VVVV11VV VVVVVV00 1IIIIIII
319 ucode[offset++] = 0xf0;
320 ucode[offset++] = (b1 >> 4);
321 ucode[offset++] = (b1 << 4) | 0x0c | (b0 >> 6);
322 ucode[offset++] = (b0 << 2);
323 ucode[offset++] = 0x80 | i;
324
325 // immed_w1[@ai, (b3 << 8) | b2]
326 // 11110100 0100VVVV VVVV11VV VVVVVV00 1IIIIIII
327 ucode[offset++] = 0xf4;
328 ucode[offset++] = 0x40 | (b3 >> 4);
329 ucode[offset++] = (b3 << 4) | 0x0c | (b2 >> 6);
330 ucode[offset++] = (b2 << 2);
331 ucode[offset++] = 0x80 | i;
332 }
333
334 for (i = 0; i < 128; i++) {
335 u8 b3;
336 u8 b2;
337 u8 b1;
338 u8 b0;
339
340 b3 = (gpr_b[i] >> 24) & 0xff;
341 b2 = (gpr_b[i] >> 16) & 0xff;
342 b1 = (gpr_b[i] >> 8) & 0xff;
343 b0 = gpr_b[i] & 0xff;
344
345 // immed[@bi, (b1 << 8) | b0]
346 // 11110000 0000VVVV VVVV001I IIIIII11 VVVVVVVV
347 ucode[offset++] = 0xf0;
348 ucode[offset++] = (b1 >> 4);
349 ucode[offset++] = (b1 << 4) | 0x02 | (i >> 6);
350 ucode[offset++] = (i << 2) | 0x03;
351 ucode[offset++] = b0;
352
353 // immed_w1[@bi, (b3 << 8) | b2]
354 // 11110100 0100VVVV VVVV001I IIIIII11 VVVVVVVV
355 ucode[offset++] = 0xf4;
356 ucode[offset++] = 0x40 | (b3 >> 4);
357 ucode[offset++] = (b3 << 4) | 0x02 | (i >> 6);
358 ucode[offset++] = (i << 2) | 0x03;
359 ucode[offset++] = b2;
360 }
361
362 // ctx_arb[kill]
363 ucode[offset++] = 0xe0;
364 ucode[offset++] = 0x00;
365 ucode[offset++] = 0x01;
366 ucode[offset++] = 0x00;
367 ucode[offset++] = 0x00;
368}
369
370static int set_initial_registers(int uengine, struct ixp2000_uengine_code *c)
371{
372 int per_ctx_regs;
373 u32 *gpr_a;
374 u32 *gpr_b;
375 u8 *ucode;
376 int i;
377
378 gpr_a = kmalloc(128 * sizeof(u32), GFP_KERNEL);
379 gpr_b = kmalloc(128 * sizeof(u32), GFP_KERNEL);
380 ucode = kmalloc(513 * 5, GFP_KERNEL);
381 if (gpr_a == NULL || gpr_b == NULL || ucode == NULL) {
382 kfree(ucode);
383 kfree(gpr_b);
384 kfree(gpr_a);
385 return 1;
386 }
387
388 per_ctx_regs = 16;
389 if (c->uengine_parameters & IXP2000_UENGINE_4_CONTEXTS)
390 per_ctx_regs = 32;
391
392 memset(gpr_a, 0, sizeof(gpr_a));
393 memset(gpr_b, 0, sizeof(gpr_b));
394 for (i = 0; i < 256; i++) {
395 struct ixp2000_reg_value *r = c->initial_reg_values + i;
396 u32 *bank;
397 int inc;
398 int j;
399
400 if (r->reg == -1)
401 break;
402
403 bank = (r->reg & 0x400) ? gpr_b : gpr_a;
404 inc = (r->reg & 0x80) ? 128 : per_ctx_regs;
405
406 j = r->reg & 0x7f;
407 while (j < 128) {
408 bank[j] = r->value;
409 j += inc;
410 }
411 }
412
413 generate_ucode(ucode, gpr_a, gpr_b);
414 ixp2000_uengine_load_microcode(uengine, ucode, 513);
415 ixp2000_uengine_init_context(uengine, 0, 0);
416 ixp2000_uengine_start_contexts(uengine, 0x01);
417 for (i = 0; i < 100; i++) {
418 u32 status;
419
420 status = ixp2000_uengine_csr_read(uengine, ACTIVE_CTX_STS);
421 if (!(status & 0x80000000))
422 break;
423 }
424 ixp2000_uengine_stop_contexts(uengine, 0x01);
425
426 kfree(ucode);
427 kfree(gpr_b);
428 kfree(gpr_a);
429
430 return !!(i == 100);
431}
432
433int ixp2000_uengine_load(int uengine, struct ixp2000_uengine_code *c)
434{
435 int ctx;
436
437 if (!check_ixp_type(c))
438 return 1;
439
440 if (!(ixp2000_uengine_mask & (1 << uengine)))
441 return 1;
442
443 ixp2000_uengine_reset(1 << uengine);
444 ixp2000_uengine_set_mode(uengine, c->uengine_parameters);
445 if (set_initial_registers(uengine, c))
446 return 1;
447 ixp2000_uengine_load_microcode(uengine, c->insns, c->num_insns);
448
449 for (ctx = 0; ctx < 8; ctx++)
450 ixp2000_uengine_init_context(uengine, ctx, 0);
451
452 return 0;
453}
454EXPORT_SYMBOL(ixp2000_uengine_load);
455
456
457static int __init ixp2000_uengine_init(void)
458{
459 int uengine;
460 u32 value;
461
462 /*
463 * Determine number of microengines present.
464 */
Lennert Buytenhek8b76a682006-06-22 10:30:56 +0100465 switch ((ixp2000_reg_read(IXP_PRODUCT_ID) >> 8) & 0x1fff) {
466#ifdef CONFIG_ARCH_IXP2000
Lennert Buytenhekd01e8892005-11-01 19:53:50 +0000467 case 0: /* IXP2800 */
468 case 1: /* IXP2850 */
469 ixp2000_uengine_mask = 0x00ff00ff;
470 break;
471
472 case 2: /* IXP2400 */
473 ixp2000_uengine_mask = 0x000f000f;
474 break;
Lennert Buytenhek8b76a682006-06-22 10:30:56 +0100475#endif
476
477#ifdef CONFIG_ARCH_IXP23XX
478 case 4: /* IXP23xx */
479 ixp2000_uengine_mask = (*IXP23XX_EXP_CFG_FUSE >> 8) & 0xf;
480 break;
481#endif
Lennert Buytenhekd01e8892005-11-01 19:53:50 +0000482
483 default:
484 printk(KERN_INFO "Detected unknown IXP2000 model (%.8x)\n",
Lennert Buytenhek8b76a682006-06-22 10:30:56 +0100485 (unsigned int)ixp2000_reg_read(IXP_PRODUCT_ID));
Lennert Buytenhekd01e8892005-11-01 19:53:50 +0000486 ixp2000_uengine_mask = 0x00000000;
487 break;
488 }
489
490 /*
491 * Reset microengines.
492 */
Lennert Buytenhek7240f1f2005-11-06 14:34:13 +0000493 ixp2000_uengine_reset(ixp2000_uengine_mask);
Lennert Buytenhekd01e8892005-11-01 19:53:50 +0000494
495 /*
496 * Synchronise timestamp counters across all microengines.
497 */
Lennert Buytenhek8b76a682006-06-22 10:30:56 +0100498 value = ixp2000_reg_read(IXP_MISC_CONTROL);
499 ixp2000_reg_wrb(IXP_MISC_CONTROL, value & ~0x80);
Lennert Buytenhekd01e8892005-11-01 19:53:50 +0000500 for (uengine = 0; uengine < 32; uengine++) {
501 if (ixp2000_uengine_mask & (1 << uengine)) {
502 ixp2000_uengine_csr_write(uengine, TIMESTAMP_LOW, 0);
503 ixp2000_uengine_csr_write(uengine, TIMESTAMP_HIGH, 0);
504 }
505 }
Lennert Buytenhek8b76a682006-06-22 10:30:56 +0100506 ixp2000_reg_wrb(IXP_MISC_CONTROL, value | 0x80);
Lennert Buytenhekd01e8892005-11-01 19:53:50 +0000507
508 return 0;
509}
510
511subsys_initcall(ixp2000_uengine_init);