/* * Generic library functions for the microengines found on the Intel * IXP2000 series of network processors. * * Copyright (C) 2004, 2005 Lennert Buytenhek * Dedicated to Marija Kulikova. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #if defined(CONFIG_ARCH_IXP2000) #define IXP_UENGINE_CSR_VIRT_BASE IXP2000_UENGINE_CSR_VIRT_BASE #define IXP_PRODUCT_ID IXP2000_PRODUCT_ID #define IXP_MISC_CONTROL IXP2000_MISC_CONTROL #define IXP_RESET1 IXP2000_RESET1 #else #if defined(CONFIG_ARCH_IXP23XX) #define IXP_UENGINE_CSR_VIRT_BASE IXP23XX_UENGINE_CSR_VIRT_BASE #define IXP_PRODUCT_ID IXP23XX_PRODUCT_ID #define IXP_MISC_CONTROL IXP23XX_MISC_CONTROL #define IXP_RESET1 IXP23XX_RESET1 #else #error unknown platform #endif #endif #define USTORE_ADDRESS 0x000 #define USTORE_DATA_LOWER 0x004 #define USTORE_DATA_UPPER 0x008 #define CTX_ENABLES 0x018 #define CC_ENABLE 0x01c #define CSR_CTX_POINTER 0x020 #define INDIRECT_CTX_STS 0x040 #define ACTIVE_CTX_STS 0x044 #define INDIRECT_CTX_SIG_EVENTS 0x048 #define INDIRECT_CTX_WAKEUP_EVENTS 0x050 #define NN_PUT 0x080 #define NN_GET 0x084 #define TIMESTAMP_LOW 0x0c0 #define TIMESTAMP_HIGH 0x0c4 #define T_INDEX_BYTE_INDEX 0x0f4 #define LOCAL_CSR_STATUS 0x180 u32 ixp2000_uengine_mask; static void *ixp2000_uengine_csr_area(int uengine) { return ((void *)IXP_UENGINE_CSR_VIRT_BASE) + (uengine << 10); } /* * LOCAL_CSR_STATUS=1 after a read or write to a microengine's CSR * space means that the microengine we tried to access was also trying * to access its own CSR space on the same clock cycle as we did. When * this happens, we lose the arbitration process by default, and the * read or write we tried to do was not actually performed, so we try * again until it succeeds. */ u32 ixp2000_uengine_csr_read(int uengine, int offset) { void *uebase; u32 *local_csr_status; u32 *reg; u32 value; uebase = ixp2000_uengine_csr_area(uengine); local_csr_status = (u32 *)(uebase + LOCAL_CSR_STATUS); reg = (u32 *)(uebase + offset); do { value = ixp2000_reg_read(reg); } while (ixp2000_reg_read(local_csr_status) & 1); return value; } EXPORT_SYMBOL(ixp2000_uengine_csr_read); void ixp2000_uengine_csr_write(int uengine, int offset, u32 value) { void *uebase; u32 *local_csr_status; u32 *reg; uebase = ixp2000_uengine_csr_area(uengine); local_csr_status = (u32 *)(uebase + LOCAL_CSR_STATUS); reg = (u32 *)(uebase + offset); do { ixp2000_reg_write(reg, value); } while (ixp2000_reg_read(local_csr_status) & 1); } EXPORT_SYMBOL(ixp2000_uengine_csr_write); void ixp2000_uengine_reset(u32 uengine_mask) { u32 value; value = ixp2000_reg_read(IXP_RESET1) & ~ixp2000_uengine_mask; uengine_mask &= ixp2000_uengine_mask; ixp2000_reg_wrb(IXP_RESET1, value | uengine_mask); ixp2000_reg_wrb(IXP_RESET1, value); } EXPORT_SYMBOL(ixp2000_uengine_reset); void ixp2000_uengine_set_mode(int uengine, u32 mode) { /* * CTL_STR_PAR_EN: unconditionally enable parity checking on * control store. */ mode |= 0x10000000; ixp2000_uengine_csr_write(uengine, CTX_ENABLES, mode); /* * Enable updating of condition codes. */ ixp2000_uengine_csr_write(uengine, CC_ENABLE, 0x00002000); /* * Initialise other per-microengine registers. */ ixp2000_uengine_csr_write(uengine, NN_PUT, 0x00); ixp2000_uengine_csr_write(uengine, NN_GET, 0x00); ixp2000_uengine_csr_write(uengine, T_INDEX_BYTE_INDEX, 0); } EXPORT_SYMBOL(ixp2000_uengine_set_mode); static int make_even_parity(u32 x) { return hweight32(x) & 1; } static void ustore_write(int uengine, u64 insn) { /* * Generate even parity for top and bottom 20 bits. */ insn |= (u64)make_even_parity((insn >> 20) & 0x000fffff) << 41; insn |= (u64)make_even_parity(insn & 0x000fffff) << 40; /* * Write to microstore. The second write auto-increments * the USTORE_ADDRESS index register. */ ixp2000_uengine_csr_write(uengine, USTORE_DATA_LOWER, (u32)insn); ixp2000_uengine_csr_write(uengine, USTORE_DATA_UPPER, (u32)(insn >> 32)); } void ixp2000_uengine_load_microcode(int uengine, u8 *ucode, int insns) { int i; /* * Start writing to microstore at address 0. */ ixp2000_uengine_csr_write(uengine, USTORE_ADDRESS, 0x80000000); for (i = 0; i < insns; i++) { u64 insn; insn = (((u64)ucode[0]) << 32) | (((u64)ucode[1]) << 24) | (((u64)ucode[2]) << 16) | (((u64)ucode[3]) << 8) | ((u64)ucode[4]); ucode += 5; ustore_write(uengine, insn); } /* * Pad with a few NOPs at the end (to avoid the microengine * aborting as it prefetches beyond the last instruction), unless * we run off the end of the instruction store first, at which * point the address register will wrap back to zero. */ for (i = 0; i < 4; i++) { u32 addr; addr = ixp2000_uengine_csr_read(uengine, USTORE_ADDRESS); if (addr == 0x80000000) break; ustore_write(uengine, 0xf0000c0300ULL); } /* * End programming. */ ixp2000_uengine_csr_write(uengine, USTORE_ADDRESS, 0x00000000); } EXPORT_SYMBOL(ixp2000_uengine_load_microcode); void ixp2000_uengine_init_context(int uengine, int context, int pc) { /* * Select the right context for indirect access. */ ixp2000_uengine_csr_write(uengine, CSR_CTX_POINTER, context); /* * Initialise signal masks to immediately go to Ready state. */ ixp2000_uengine_csr_write(uengine, INDIRECT_CTX_SIG_EVENTS, 1); ixp2000_uengine_csr_write(uengine, INDIRECT_CTX_WAKEUP_EVENTS, 1); /* * Set program counter. */ ixp2000_uengine_csr_write(uengine, INDIRECT_CTX_STS, pc); } EXPORT_SYMBOL(ixp2000_uengine_init_context); void ixp2000_uengine_start_contexts(int uengine, u8 ctx_mask) { u32 mask; /* * Enable the specified context to go to Executing state. */ mask = ixp2000_uengine_csr_read(uengine, CTX_ENABLES); mask |= ctx_mask << 8; ixp2000_uengine_csr_write(uengine, CTX_ENABLES, mask); } EXPORT_SYMBOL(ixp2000_uengine_start_contexts); void ixp2000_uengine_stop_contexts(int uengine, u8 ctx_mask) { u32 mask; /* * Disable the Ready->Executing transition. Note that this * does not stop the context until it voluntarily yields. */ mask = ixp2000_uengine_csr_read(uengine, CTX_ENABLES); mask &= ~(ctx_mask << 8); ixp2000_uengine_csr_write(uengine, CTX_ENABLES, mask); } EXPORT_SYMBOL(ixp2000_uengine_stop_contexts); static int check_ixp_type(struct ixp2000_uengine_code *c) { u32 product_id; u32 rev; product_id = ixp2000_reg_read(IXP_PRODUCT_ID); if (((product_id >> 16) & 0x1f) != 0) return 0; switch ((product_id >> 8) & 0xff) { #ifdef CONFIG_ARCH_IXP2000 case 0: /* IXP2800 */ if (!(c->cpu_model_bitmask & 4)) return 0; break; case 1: /* IXP2850 */ if (!(c->cpu_model_bitmask & 8)) return 0; break; case 2: /* IXP2400 */ if (!(c->cpu_model_bitmask & 2)) return 0; break; #endif #ifdef CONFIG_ARCH_IXP23XX case 4: /* IXP23xx */ if (!(c->cpu_model_bitmask & 0x3f0)) return 0; break; #endif default: return 0; } rev = product_id & 0xff; if (rev < c->cpu_min_revision || rev > c->cpu_max_revision) return 0; return 1; } static void generate_ucode(u8 *ucode, u32 *gpr_a, u32 *gpr_b) { int offset; int i; offset = 0; for (i = 0; i < 128; i++) { u8 b3; u8 b2; u8 b1; u8 b0; b3 = (gpr_a[i] >> 24) & 0xff; b2 = (gpr_a[i] >> 16) & 0xff; b1 = (gpr_a[i] >> 8) & 0xff; b0 = gpr_a[i] & 0xff; // immed[@ai, (b1 << 8) | b0] // 11110000 0000VVVV VVVV11VV VVVVVV00 1IIIIIII ucode[offset++] = 0xf0; ucode[offset++] = (b1 >> 4); ucode[offset++] = (b1 << 4) | 0x0c | (b0 >> 6); ucode[offset++] = (b0 << 2); ucode[offset++] = 0x80 | i; // immed_w1[@ai, (b3 << 8) | b2] // 11110100 0100VVVV VVVV11VV VVVVVV00 1IIIIIII ucode[offset++] = 0xf4; ucode[offset++] = 0x40 | (b3 >> 4); ucode[offset++] = (b3 << 4) | 0x0c | (b2 >> 6); ucode[offset++] = (b2 << 2); ucode[offset++] = 0x80 | i; } for (i = 0; i < 128; i++) { u8 b3; u8 b2; u8 b1; u8 b0; b3 = (gpr_b[i] >> 24) & 0xff; b2 = (gpr_b[i] >> 16) & 0xff; b1 = (gpr_b[i] >> 8) & 0xff; b0 = gpr_b[i] & 0xff; // immed[@bi, (b1 << 8) | b0] // 11110000 0000VVVV VVVV001I IIIIII11 VVVVVVVV ucode[offset++] = 0xf0; ucode[offset++] = (b1 >> 4); ucode[offset++] = (b1 << 4) | 0x02 | (i >> 6); ucode[offset++] = (i << 2) | 0x03; ucode[offset++] = b0; // immed_w1[@bi, (b3 << 8) | b2] // 11110100 0100VVVV VVVV001I IIIIII11 VVVVVVVV ucode[offset++] = 0xf4; ucode[offset++] = 0x40 | (b3 >> 4); ucode[offset++] = (b3 << 4) | 0x02 | (i >> 6); ucode[offset++] = (i << 2) | 0x03; ucode[offset++] = b2; } // ctx_arb[kill] ucode[offset++] = 0xe0; ucode[offset++] = 0x00; ucode[offset++] = 0x01; ucode[offset++] = 0x00; ucode[offset++] = 0x00; } static int set_initial_registers(int uengine, struct ixp2000_uengine_code *c) { int per_ctx_regs; u32 *gpr_a; u32 *gpr_b; u8 *ucode; int i; gpr_a = kzalloc(128 * sizeof(u32), GFP_KERNEL); gpr_b = kzalloc(128 * sizeof(u32), GFP_KERNEL); ucode = kmalloc(513 * 5, GFP_KERNEL); if (gpr_a == NULL || gpr_b == NULL || ucode == NULL) { kfree(ucode); kfree(gpr_b); kfree(gpr_a); return 1; } per_ctx_regs = 16; if (c->uengine_parameters & IXP2000_UENGINE_4_CONTEXTS) per_ctx_regs = 32; for (i = 0; i < 256; i++) { struct ixp2000_reg_value *r = c->initial_reg_values + i; u32 *bank; int inc; int j; if (r->reg == -1) break; bank = (r->reg & 0x400) ? gpr_b : gpr_a; inc = (r->reg & 0x80) ? 128 : per_ctx_regs; j = r->reg & 0x7f; while (j < 128) { bank[j] = r->value; j += inc; } } generate_ucode(ucode, gpr_a, gpr_b); ixp2000_uengine_load_microcode(uengine, ucode, 513); ixp2000_uengine_init_context(uengine, 0, 0); ixp2000_uengine_start_contexts(uengine, 0x01); for (i = 0; i < 100; i++) { u32 status; status = ixp2000_uengine_csr_read(uengine, ACTIVE_CTX_STS); if (!(status & 0x80000000)) break; } ixp2000_uengine_stop_contexts(uengine, 0x01); kfree(ucode); kfree(gpr_b); kfree(gpr_a); return !!(i == 100); } int ixp2000_uengine_load(int uengine, struct ixp2000_uengine_code *c) { int ctx; if (!check_ixp_type(c)) return 1; if (!(ixp2000_uengine_mask & (1 << uengine))) return 1; ixp2000_uengine_reset(1 << uengine); ixp2000_uengine_set_mode(uengine, c->uengine_parameters); if (set_initial_registers(uengine, c)) return 1; ixp2000_uengine_load_microcode(uengine, c->insns, c->num_insns); for (ctx = 0; ctx < 8; ctx++) ixp2000_uengine_init_context(uengine, ctx, 0); return 0; } EXPORT_SYMBOL(ixp2000_uengine_load); static int __init ixp2000_uengine_init(void) { int uengine; u32 value; /* * Determine number of microengines present. */ switch ((ixp2000_reg_read(IXP_PRODUCT_ID) >> 8) & 0x1fff) { #ifdef CONFIG_ARCH_IXP2000 case 0: /* IXP2800 */ case 1: /* IXP2850 */ ixp2000_uengine_mask = 0x00ff00ff; break; case 2: /* IXP2400 */ ixp2000_uengine_mask = 0x000f000f; break; #endif #ifdef CONFIG_ARCH_IXP23XX case 4: /* IXP23xx */ ixp2000_uengine_mask = (*IXP23XX_EXP_CFG_FUSE >> 8) & 0xf; break; #endif default: printk(KERN_INFO "Detected unknown IXP2000 model (%.8x)\n", (unsigned int)ixp2000_reg_read(IXP_PRODUCT_ID)); ixp2000_uengine_mask = 0x00000000; break; } /* * Reset microengines. */ ixp2000_uengine_reset(ixp2000_uengine_mask); /* * Synchronise timestamp counters across all microengines. */ value = ixp2000_reg_read(IXP_MISC_CONTROL); ixp2000_reg_wrb(IXP_MISC_CONTROL, value & ~0x80); for (uengine = 0; uengine < 32; uengine++) { if (ixp2000_uengine_mask & (1 << uengine)) { ixp2000_uengine_csr_write(uengine, TIMESTAMP_LOW, 0); ixp2000_uengine_csr_write(uengine, TIMESTAMP_HIGH, 0); } } ixp2000_reg_wrb(IXP_MISC_CONTROL, value | 0x80); return 0; } subsys_initcall(ixp2000_uengine_init);