On 21:57 Sun 07 Apr , Krzysztof Halasa wrote: > Signed-off-by: Krzysztof Hałasa <khc@xxxxxxxxx> > > diff --git a/arch/arm/mach-ixp4xx/Kconfig b/arch/arm/mach-ixp4xx/Kconfig > index 9244be9..33e8bb3 100644 > --- a/arch/arm/mach-ixp4xx/Kconfig > +++ b/arch/arm/mach-ixp4xx/Kconfig > @@ -6,4 +6,10 @@ config IXP4XX_QMGR > This driver supports IXP4xx built-in hardware queue manager > and is required by the Ethernet driver. > > +config IXP4XX_NPE > + tristate "IXP4xx Network Processor Engine support" > + help > + This driver supports IXP4xx built-in network coprocessors > + and is required by the Ethernet driver. > + > endif > diff --git a/arch/arm/mach-ixp4xx/Makefile b/arch/arm/mach-ixp4xx/Makefile > index 09a0d63..7cfc924 100644 > --- a/arch/arm/mach-ixp4xx/Makefile > +++ b/arch/arm/mach-ixp4xx/Makefile > @@ -1,2 +1,3 @@ > obj-y += generic.o > obj-$(CONFIG_IXP4XX_QMGR) += qmgr.o > +obj-$(CONFIG_IXP4XX_NPE) += npe.o > diff --git a/arch/arm/mach-ixp4xx/include/mach/npe.h b/arch/arm/mach-ixp4xx/include/mach/npe.h > new file mode 100644 > index 0000000..18bd01b > --- /dev/null > +++ b/arch/arm/mach-ixp4xx/include/mach/npe.h > @@ -0,0 +1,30 @@ > +#ifndef __IXP4XX_NPE_H > +#define __IXP4XX_NPE_H > + > +#include <common.h> > + > +#define NPE_NAME_LENGTH 5 > + > +struct npe_regs { > + u32 exec_addr, exec_data, exec_status_cmd, exec_count; > + u32 action_points[4]; > + u32 watchpoint_fifo, watch_count; > + u32 profile_count; > + u32 messaging_status, messaging_control; > + u32 mailbox_status, /*messaging_*/ in_out_fifo; > +}; > + > +struct npe { > + struct npe_regs *regs; > + int id, valid; > + const char name[NPE_NAME_LENGTH + 1]; > +}; > + > +int npe_running(struct npe *npe); > +int npe_send_message(struct npe *npe, const void *msg, const char *what); > +int npe_recv_message(struct npe *npe, void *msg, const char *what); > +int npe_send_recv_message(struct npe *npe, void *msg, const char *what); > +int npe_load_firmware(struct npe *npe); > +struct npe *npe_request(int id); > + > +#endif /* __IXP4XX_NPE_H */ > diff --git a/arch/arm/mach-ixp4xx/npe.c b/arch/arm/mach-ixp4xx/npe.c > new file mode 100644 > index 0000000..d403340 > --- /dev/null > +++ b/arch/arm/mach-ixp4xx/npe.c > @@ -0,0 +1,667 @@ > +/* > + * Intel IXP4xx Network Processor Engine driver for Linux > + * > + * Copyright (C) 2007 Krzysztof Halasa <khc@xxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of version 2 of the GNU General Public License > + * as published by the Free Software Foundation. > + */ > + > +#include <common.h> > +#include <errno.h> > +#include <fs.h> > +#include <init.h> > +#include <malloc.h> > +#include <asm/byteorder.h> > +#include <asm/io.h> > +#include <mach/ixp4xx-regs.h> > +#include <mach/cpu.h> > +#include <mach/npe.h> > + > +#define DEBUG_MSG 0 > +#define DEBUG_FW 0 > + > +#define MAX_RETRIES 1000 /* microseconds */ > +#define NPE_42X_DATA_SIZE 0x800 /* in dwords */ > +#define NPE_46X_DATA_SIZE 0x1000 > +#define NPE_A_42X_INSTR_SIZE 0x1000 > +#define NPE_B_AND_C_42X_INSTR_SIZE 0x800 > +#define NPE_46X_INSTR_SIZE 0x1000 > +#define REGS_SIZE 0x1000 > + > +#define NPE_PHYS_REG 32 > + > +#define FW_MAGIC 0xFEEDF00D > +#define FW_BLOCK_TYPE_INSTR 0x0 > +#define FW_BLOCK_TYPE_DATA 0x1 > +#define FW_BLOCK_TYPE_EOF 0xF > + > +/* NPE exec status (read) and command (write) */ > +#define CMD_NPE_STEP 0x01 > +#define CMD_NPE_START 0x02 > +#define CMD_NPE_STOP 0x03 > +#define CMD_NPE_CLR_PIPE 0x04 > +#define CMD_CLR_PROFILE_CNT 0x0C > +#define CMD_RD_INS_MEM 0x10 /* instruction memory */ > +#define CMD_WR_INS_MEM 0x11 > +#define CMD_RD_DATA_MEM 0x12 /* data memory */ > +#define CMD_WR_DATA_MEM 0x13 > +#define CMD_RD_ECS_REG 0x14 /* exec access register */ > +#define CMD_WR_ECS_REG 0x15 > + > +#define STAT_RUN 0x80000000 > +#define STAT_STOP 0x40000000 > +#define STAT_CLEAR 0x20000000 > +#define STAT_ECS_K 0x00800000 /* pipeline clean */ > + > +#define NPE_STEVT 0x1B > +#define NPE_STARTPC 0x1C > +#define NPE_REGMAP 0x1E > +#define NPE_CINDEX 0x1F > + > +#define INSTR_WR_REG_SHORT 0x0000C000 > +#define INSTR_WR_REG_BYTE 0x00004000 > +#define INSTR_RD_FIFO 0x0F888220 > +#define INSTR_RESET_MBOX 0x0FAC8210 > + > +#define ECS_BG_CTXT_REG_0 0x00 /* Background Executing Context */ > +#define ECS_BG_CTXT_REG_1 0x01 /* Stack level */ > +#define ECS_BG_CTXT_REG_2 0x02 > +#define ECS_PRI_1_CTXT_REG_0 0x04 /* Priority 1 Executing Context */ > +#define ECS_PRI_1_CTXT_REG_1 0x05 /* Stack level */ > +#define ECS_PRI_1_CTXT_REG_2 0x06 > +#define ECS_PRI_2_CTXT_REG_0 0x08 /* Priority 2 Executing Context */ > +#define ECS_PRI_2_CTXT_REG_1 0x09 /* Stack level */ > +#define ECS_PRI_2_CTXT_REG_2 0x0A > +#define ECS_DBG_CTXT_REG_0 0x0C /* Debug Executing Context */ > +#define ECS_DBG_CTXT_REG_1 0x0D /* Stack level */ > +#define ECS_DBG_CTXT_REG_2 0x0E > +#define ECS_INSTRUCT_REG 0x11 /* NPE Instruction Register */ > + > +#define ECS_REG_0_ACTIVE 0x80000000 /* all levels */ > +#define ECS_REG_0_NEXTPC_MASK 0x1FFF0000 /* BG/PRI1/PRI2 levels */ > +#define ECS_REG_0_LDUR_BITS 8 > +#define ECS_REG_0_LDUR_MASK 0x00000700 /* all levels */ > +#define ECS_REG_1_CCTXT_BITS 16 > +#define ECS_REG_1_CCTXT_MASK 0x000F0000 /* all levels */ > +#define ECS_REG_1_SELCTXT_BITS 0 > +#define ECS_REG_1_SELCTXT_MASK 0x0000000F /* all levels */ > +#define ECS_DBG_REG_2_IF 0x00100000 /* debug level */ > +#define ECS_DBG_REG_2_IE 0x00080000 /* debug level */ > + > +/* NPE watchpoint_fifo register bit */ > +#define WFIFO_VALID 0x80000000 > + > +/* NPE messaging_status register bit definitions */ > +#define MSGSTAT_OFNE 0x00010000 /* OutFifoNotEmpty */ > +#define MSGSTAT_IFNF 0x00020000 /* InFifoNotFull */ > +#define MSGSTAT_OFNF 0x00040000 /* OutFifoNotFull */ > +#define MSGSTAT_IFNE 0x00080000 /* InFifoNotEmpty */ > +#define MSGSTAT_MBINT 0x00100000 /* Mailbox interrupt */ > +#define MSGSTAT_IFINT 0x00200000 /* InFifo interrupt */ > +#define MSGSTAT_OFINT 0x00400000 /* OutFifo interrupt */ > +#define MSGSTAT_WFINT 0x00800000 /* WatchFifo interrupt */ > + > +/* NPE messaging_control register bit definitions */ > +#define MSGCTL_OUT_FIFO 0x00010000 /* enable output FIFO */ > +#define MSGCTL_IN_FIFO 0x00020000 /* enable input FIFO */ > +#define MSGCTL_OUT_FIFO_WRITE 0x01000000 /* enable FIFO + WRITE */ > +#define MSGCTL_IN_FIFO_WRITE 0x02000000 > + > +/* NPE mailbox_status value for reset */ > +#define RESET_MBOX_STAT 0x0000F0F0 > + > +#define print_npe(npe, fmt, ...) fprintf(stderr, "%s: " fmt, npe->name, ## __VA_ARGS__) no printf use dev_dbg > + > +#if DEBUG_MSG > +#define debug_msg(npe, fmt, ...) print_npe(npe, fmt, ## __VA_ARGS__) > +#else > +#define debug_msg(npe, fmt, ...) > +#endif > + > +static struct { > + u32 reg, val; > +} ecs_reset[] = { > + {ECS_BG_CTXT_REG_0, 0xA0000000}, > + {ECS_BG_CTXT_REG_1, 0x01000000}, > + {ECS_BG_CTXT_REG_2, 0x00008000}, > + {ECS_PRI_1_CTXT_REG_0, 0x20000080}, > + {ECS_PRI_1_CTXT_REG_1, 0x01000000}, > + {ECS_PRI_1_CTXT_REG_2, 0x00008000}, > + {ECS_PRI_2_CTXT_REG_0, 0x20000080}, > + {ECS_PRI_2_CTXT_REG_1, 0x01000000}, > + {ECS_PRI_2_CTXT_REG_2, 0x00008000}, > + {ECS_DBG_CTXT_REG_0, 0x20000000}, > + {ECS_DBG_CTXT_REG_1, 0x00000000}, > + {ECS_DBG_CTXT_REG_2, 0x001E0000}, > + {ECS_INSTRUCT_REG, 0x1003C00F}, > +}; > + > +static struct npe npe_tab[] = { > + { > + .regs = (struct npe_regs *)IXP4XX_NPEA_BASE, > + .id = 0, > + .name = "NPE-A", > + }, { > + .regs = (struct npe_regs *)IXP4XX_NPEB_BASE, > + .id = 1, > + .name = "NPE-B", > + }, { > + .regs = (struct npe_regs *)IXP4XX_NPEC_BASE, > + .id = 2, > + .name = "NPE-C", > + } > +}; > + > +int npe_running(struct npe *npe) > +{ > + return (__raw_readl(&npe->regs->exec_status_cmd) & STAT_RUN) != 0; > +} > + > +static void npe_cmd_write(struct npe *npe, u32 addr, int cmd, u32 data) > +{ > + __raw_writel(data, &npe->regs->exec_data); > + __raw_writel(addr, &npe->regs->exec_addr); > + __raw_writel(cmd, &npe->regs->exec_status_cmd); > +} > + > +static u32 npe_cmd_read(struct npe *npe, u32 addr, int cmd) > +{ > + __raw_writel(addr, &npe->regs->exec_addr); > + __raw_writel(cmd, &npe->regs->exec_status_cmd); > + /* Iintroduce extra read cycles after issuing read command to NPE > + so that we read the register after the NPE has updated it. > + This is to overcome race condition between XScale and NPE */ > + __raw_readl(&npe->regs->exec_data); > + __raw_readl(&npe->regs->exec_data); > + return __raw_readl(&npe->regs->exec_data); > +} > + > +static void npe_clear_active(struct npe *npe, u32 reg) > +{ > + u32 val = npe_cmd_read(npe, reg, CMD_RD_ECS_REG); > + npe_cmd_write(npe, reg, CMD_WR_ECS_REG, val & ~ECS_REG_0_ACTIVE); > +} > + > +static void npe_start(struct npe *npe) > +{ > + /* ensure only Background Context Stack Level is active */ > + npe_clear_active(npe, ECS_PRI_1_CTXT_REG_0); > + npe_clear_active(npe, ECS_PRI_2_CTXT_REG_0); > + npe_clear_active(npe, ECS_DBG_CTXT_REG_0); > + > + __raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd); > + __raw_writel(CMD_NPE_START, &npe->regs->exec_status_cmd); > +} > + > +static void npe_stop(struct npe *npe) > +{ > + __raw_writel(CMD_NPE_STOP, &npe->regs->exec_status_cmd); > + __raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd); /*FIXME?*/ > +} > + > +static int npe_debug_instr(struct npe *npe, u32 instr, u32 ctx, u32 ldur) > +{ > + u32 wc; > + int i; > + > + /* set the Active bit, and the LDUR, in the debug level */ > + npe_cmd_write(npe, ECS_DBG_CTXT_REG_0, CMD_WR_ECS_REG, > + ECS_REG_0_ACTIVE | (ldur << ECS_REG_0_LDUR_BITS)); > + > + /* set CCTXT at ECS DEBUG L3 to specify in which context to execute > + the instruction, and set SELCTXT at ECS DEBUG Level to specify > + which context store to access. > + Debug ECS Level Reg 1 has form 0x000n000n, where n = context number > + */ > + npe_cmd_write(npe, ECS_DBG_CTXT_REG_1, CMD_WR_ECS_REG, > + (ctx << ECS_REG_1_CCTXT_BITS) | > + (ctx << ECS_REG_1_SELCTXT_BITS)); > + > + /* clear the pipeline */ > + __raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd); > + > + /* load NPE instruction into the instruction register */ > + npe_cmd_write(npe, ECS_INSTRUCT_REG, CMD_WR_ECS_REG, instr); > + > + /* we need this value later to wait for completion of NPE execution > + step */ > + wc = __raw_readl(&npe->regs->watch_count); > + > + /* issue a Step One command via the Execution Control register */ > + __raw_writel(CMD_NPE_STEP, &npe->regs->exec_status_cmd); > + > + /* Watch Count register increments when NPE completes an instruction */ > + for (i = 0; i < MAX_RETRIES; i++) { > + if (wc != __raw_readl(&npe->regs->watch_count)) > + return 0; > + udelay(1); > + } > + > + print_npe(npe, "reset: npe_debug_instr(): timeout\n"); > + return -ETIMEDOUT; > +} > + > +static int npe_logical_reg_write8(struct npe *npe, u32 addr, u8 val, u32 ctx) > +{ > + /* here we build the NPE assembler instruction: mov8 d0, #0 */ > + u32 instr = INSTR_WR_REG_BYTE | /* OpCode */ > + addr << 9 | /* base Operand */ > + (val & 0x1F) << 4 | /* lower 5 bits to immediate data */ > + (val & ~0x1F) << (18 - 5);/* higher 3 bits to CoProc instr. */ > + return npe_debug_instr(npe, instr, ctx, 1); /* execute it */ > +} > + > +static int npe_logical_reg_write16(struct npe *npe, u32 addr, u16 val, u32 ctx) > +{ > + /* here we build the NPE assembler instruction: mov16 d0, #0 */ > + u32 instr = INSTR_WR_REG_SHORT | /* OpCode */ > + addr << 9 | /* base Operand */ > + (val & 0x1F) << 4 | /* lower 5 bits to immediate data */ > + (val & ~0x1F) << (18 - 5);/* higher 11 bits to CoProc instr. */ > + return npe_debug_instr(npe, instr, ctx, 1); /* execute it */ > +} > + > +static int npe_logical_reg_write32(struct npe *npe, u32 addr, u32 val, u32 ctx) > +{ > + /* write in 16 bit steps first the high and then the low value */ > + if (npe_logical_reg_write16(npe, addr, val >> 16, ctx)) > + return -ETIMEDOUT; > + return npe_logical_reg_write16(npe, addr + 2, val & 0xFFFF, ctx); > +} > + > +static int npe_reset(struct npe *npe) > +{ > + u32 val, ctl, exec_count, ctx_reg2; > + int i; > + > + ctl = (__raw_readl(&npe->regs->messaging_control) | 0x3F000000) & > + 0x3F3FFFFF; > + > + /* disable parity interrupt */ > + __raw_writel(ctl & 0x3F00FFFF, &npe->regs->messaging_control); > + > + /* pre exec - debug instruction */ > + /* turn off the halt bit by clearing Execution Count register. */ > + exec_count = __raw_readl(&npe->regs->exec_count); > + __raw_writel(0, &npe->regs->exec_count); > + /* ensure that IF and IE are on (temporarily), so that we don't end up > + stepping forever */ > + ctx_reg2 = npe_cmd_read(npe, ECS_DBG_CTXT_REG_2, CMD_RD_ECS_REG); > + npe_cmd_write(npe, ECS_DBG_CTXT_REG_2, CMD_WR_ECS_REG, ctx_reg2 | > + ECS_DBG_REG_2_IF | ECS_DBG_REG_2_IE); > + > + /* clear the FIFOs */ > + while (__raw_readl(&npe->regs->watchpoint_fifo) & WFIFO_VALID) > + ; > + while (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_OFNE) > + /* read from the outFIFO until empty */ > + print_npe(npe, "npe_reset: read FIFO = 0x%X\n", > + __raw_readl(&npe->regs->in_out_fifo)); > + > + while (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNE) > + /* step execution of the NPE intruction to read inFIFO using > + the Debug Executing Context stack */ > + if (npe_debug_instr(npe, INSTR_RD_FIFO, 0, 0)) > + return -ETIMEDOUT; > + > + /* reset the mailbox reg from the XScale side */ > + __raw_writel(RESET_MBOX_STAT, &npe->regs->mailbox_status); > + /* from NPE side */ > + if (npe_debug_instr(npe, INSTR_RESET_MBOX, 0, 0)) > + return -ETIMEDOUT; > + > + /* Reset the physical registers in the NPE register file */ > + for (val = 0; val < NPE_PHYS_REG; val++) { > + if (npe_logical_reg_write16(npe, NPE_REGMAP, val >> 1, 0)) > + return -ETIMEDOUT; > + /* address is either 0 or 4 */ > + if (npe_logical_reg_write32(npe, (val & 1) * 4, 0, 0)) > + return -ETIMEDOUT; > + } > + > + /* Reset the context store = each context's Context Store registers */ > + > + /* Context 0 has no STARTPC. Instead, this value is used to set NextPC > + for Background ECS, to set where NPE starts executing code */ > + val = npe_cmd_read(npe, ECS_BG_CTXT_REG_0, CMD_RD_ECS_REG); > + val &= ~ECS_REG_0_NEXTPC_MASK; > + val |= (0 /* NextPC */ << 16) & ECS_REG_0_NEXTPC_MASK; > + npe_cmd_write(npe, ECS_BG_CTXT_REG_0, CMD_WR_ECS_REG, val); > + > + for (i = 0; i < 16; i++) { > + if (i) { /* Context 0 has no STEVT nor STARTPC */ > + /* STEVT = off, 0x80 */ > + if (npe_logical_reg_write8(npe, NPE_STEVT, 0x80, i)) > + return -ETIMEDOUT; > + if (npe_logical_reg_write16(npe, NPE_STARTPC, 0, i)) > + return -ETIMEDOUT; > + } > + /* REGMAP = d0->p0, d8->p2, d16->p4 */ > + if (npe_logical_reg_write16(npe, NPE_REGMAP, 0x820, i)) > + return -ETIMEDOUT; > + if (npe_logical_reg_write8(npe, NPE_CINDEX, 0, i)) > + return -ETIMEDOUT; > + } > + > + /* post exec */ > + /* clear active bit in debug level */ > + npe_cmd_write(npe, ECS_DBG_CTXT_REG_0, CMD_WR_ECS_REG, 0); > + /* clear the pipeline */ > + __raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd); > + /* restore previous values */ > + __raw_writel(exec_count, &npe->regs->exec_count); > + npe_cmd_write(npe, ECS_DBG_CTXT_REG_2, CMD_WR_ECS_REG, ctx_reg2); > + > + /* write reset values to Execution Context Stack registers */ > + for (val = 0; val < ARRAY_SIZE(ecs_reset); val++) > + npe_cmd_write(npe, ecs_reset[val].reg, CMD_WR_ECS_REG, > + ecs_reset[val].val); > + > + /* clear the profile counter */ > + __raw_writel(CMD_CLR_PROFILE_CNT, &npe->regs->exec_status_cmd); > + > + __raw_writel(0, &npe->regs->exec_count); > + __raw_writel(0, &npe->regs->action_points[0]); > + __raw_writel(0, &npe->regs->action_points[1]); > + __raw_writel(0, &npe->regs->action_points[2]); > + __raw_writel(0, &npe->regs->action_points[3]); > + __raw_writel(0, &npe->regs->watch_count); > + > + val = ixp4xx_read_feature_bits(); > + /* reset the NPE */ > + ixp4xx_write_feature_bits(val & > + ~(IXP4XX_FEATURE_RESET_NPEA << npe->id)); > + /* deassert reset */ > + ixp4xx_write_feature_bits(val | > + (IXP4XX_FEATURE_RESET_NPEA << npe->id)); > + for (i = 0; i < MAX_RETRIES; i++) { > + if (ixp4xx_read_feature_bits() & > + (IXP4XX_FEATURE_RESET_NPEA << npe->id)) > + break; /* NPE is back alive */ > + udelay(1); > + } > + if (i == MAX_RETRIES) > + return -ETIMEDOUT; > + > + npe_stop(npe); > + > + /* restore NPE configuration bus Control Register - parity settings */ > + __raw_writel(ctl, &npe->regs->messaging_control); > + return 0; > +} > + > + > +int npe_send_message(struct npe *npe, const void *msg, const char *what) > +{ > + const u32 *send = msg; > + int cycles = 0; > + > + debug_msg(npe, "Trying to send message %s [%08X:%08X]\n", > + what, send[0], send[1]); > + > + if (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNE) { > + debug_msg(npe, "NPE input FIFO not empty\n"); > + return -EIO; > + } > + > + __raw_writel(send[0], &npe->regs->in_out_fifo); > + > + if (!(__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNF)) { > + debug_msg(npe, "NPE input FIFO full\n"); > + return -EIO; > + } > + > + __raw_writel(send[1], &npe->regs->in_out_fifo); > + > + while ((cycles < MAX_RETRIES) && > + (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNE)) { > + udelay(1); > + cycles++; > + } > + > + if (cycles == MAX_RETRIES) { > + debug_msg(npe, "Timeout sending message\n"); > + return -ETIMEDOUT; > + } > + > +#if DEBUG_MSG > 1 > + debug_msg(npe, "Sending a message took %i cycles\n", cycles); > +#endif > + return 0; > +} > + > +int npe_recv_message(struct npe *npe, void *msg, const char *what) > +{ > + u32 *recv = msg; > + int cycles = 0, cnt = 0; > + > + debug_msg(npe, "Trying to receive message %s\n", what); > + > + while (cycles < MAX_RETRIES) { > + if (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_OFNE) { > + recv[cnt++] = __raw_readl(&npe->regs->in_out_fifo); > + if (cnt == 2) > + break; > + } else { > + udelay(1); > + cycles++; > + } > + } > + > + switch(cnt) { > + case 1: > + debug_msg(npe, "Received [%08X]\n", recv[0]); > + break; > + case 2: > + debug_msg(npe, "Received [%08X:%08X]\n", recv[0], recv[1]); > + break; > + } > + > + if (cycles == MAX_RETRIES) { > + debug_msg(npe, "Timeout waiting for message\n"); > + return -ETIMEDOUT; > + } > + > +#if DEBUG_MSG > 1 > + debug_msg(npe, "Receiving a message took %i cycles\n", cycles); > +#endif > + return 0; > +} > + > +int npe_send_recv_message(struct npe *npe, void *msg, const char *what) > +{ > + int result; > + u32 *send = msg, recv[2]; > + > + if ((result = npe_send_message(npe, msg, what)) != 0) > + return result; > + if ((result = npe_recv_message(npe, recv, what)) != 0) > + return result; > + > + if ((recv[0] != send[0]) || (recv[1] != send[1])) { > + debug_msg(npe, "Message %s: unexpected message received\n", > + what); > + return -EIO; > + } > + return 0; > +} > + > + > +int npe_load_firmware(struct npe *npe) > +{ > + struct dl_block { > + u32 type; > + u32 offset; > + } *blk; > + > + struct dl_image { > + u32 magic; > + u32 id; > + u32 size; > + union { > + u32 data[0]; > + struct dl_block blocks[0]; > + }; > + } *image; > + > + struct dl_codeblock { > + u32 npe_addr; > + u32 size; > + u32 data[0]; > + } *cb; > + > + int i, j, err, data_size, instr_size, blocks, table_end; > + u32 cmd; > + char name[10 /* "/firmware/" */ + NPE_NAME_LENGTH + 1 /* NUL */]; > + size_t image_size; > + > + sprintf(name, "/firmware/%s", npe->name); > + if (!(image = read_file(name, &image_size))) { > + print_npe(npe, "bad or missing microcode file %s\n", name); > + return -EIO; > + } > + > + err = -EINVAL; > + if (image_size < sizeof(struct dl_image)) { > + print_npe(npe, "incomplete microcode file %s\n", name); > + goto err; > + } > + > +#if DEBUG_FW > + print_npe(npe, "microcode: %08X %08X %08X (0x%X bytes)\n", > + image->magic, image->id, image->size, image->size * 4); > +#endif > + > + if (image->magic == swab32(FW_MAGIC)) { /* swapped file */ > + image->id = swab32(image->id); > + image->size = swab32(image->size); > + } else if (image->magic != FW_MAGIC) { > + print_npe(npe, "bad microcode file %s magic: 0x%X\n", name, image->magic); > + goto err; > + } > + if ((image->size * 4 + sizeof(struct dl_image)) > image_size) { > + print_npe(npe, "incomplete microcode file %s\n", name); > + goto err; > + } > + if (((image->id >> 24) & 0xF /* NPE ID */) != npe->id) { > + print_npe(npe, "NPE ID mismatch in microcode file %s\n", name); > + goto err; > + } > + if (image->magic == swab32(FW_MAGIC)) > + for (i = 0; i < image->size; i++) > + image->data[i] = swab32(image->data[i]); > + > + if (cpu_is_ixp42x() && ((image->id >> 28) & 0xF /* device ID */)) { > + print_npe(npe, "IXP43x/IXP46x microcode ignored on IXP42x\n"); > + goto err; > + } > + > + if (npe_running(npe)) { > + print_npe(npe, "unable to load microcode file %s, NPE is already running\n", name); > + err = -EBUSY; > + goto err; > + } > + > + if (cpu_is_ixp42x()) { > + if (!npe->id) > + instr_size = NPE_A_42X_INSTR_SIZE; > + else > + instr_size = NPE_B_AND_C_42X_INSTR_SIZE; > + data_size = NPE_42X_DATA_SIZE; > + } else { > + instr_size = NPE_46X_INSTR_SIZE; > + data_size = NPE_46X_DATA_SIZE; > + } > + > + for (blocks = 0; blocks * sizeof(struct dl_block) / 4 < image->size; > + blocks++) > + if (image->blocks[blocks].type == FW_BLOCK_TYPE_EOF) > + break; > + if (blocks * sizeof(struct dl_block) / 4 >= image->size) { > + print_npe(npe, "microcode EOF block marker not found\n"); > + goto err; > + } > + > +#if DEBUG_FW > + print_npe(npe, "%i microcode blocks found\n", blocks); > +#endif > + > + table_end = blocks * sizeof(struct dl_block) / 4 + 1 /* EOF marker */; > + for (i = 0, blk = image->blocks; i < blocks; i++, blk++) { > + if (blk->offset > image->size - sizeof(struct dl_codeblock) / 4 > + || blk->offset < table_end) { > + print_npe(npe, "invalid offset 0x%X of " > + "microcode block #%i\n", blk->offset, i); > + goto err; > + } > + > + cb = (struct dl_codeblock*)&image->data[blk->offset]; > + if (blk->type == FW_BLOCK_TYPE_INSTR) { > + if (cb->npe_addr + cb->size > instr_size) > + goto too_big; > + cmd = CMD_WR_INS_MEM; > + } else if (blk->type == FW_BLOCK_TYPE_DATA) { > + if (cb->npe_addr + cb->size > data_size) > + goto too_big; > + cmd = CMD_WR_DATA_MEM; > + } else { > + print_npe(npe, "invalid microcode block #%i type 0x%X\n", > + i, blk->type); > + goto err; > + } > + if (blk->offset + sizeof(*cb) / 4 + cb->size > image->size) { > + print_npe(npe, "microcode block #%i doesn't " > + "fit in microcode image: type %c, start 0x%X," > + " length 0x%X\n", i, > + blk->type == FW_BLOCK_TYPE_INSTR ? 'I' : 'D', > + cb->npe_addr, cb->size); > + goto err; > + } > + > + for (j = 0; j < cb->size; j++) > + npe_cmd_write(npe, cb->npe_addr + j, cmd, cb->data[j]); > + } > + > + npe_start(npe); > + if (!npe_running(npe)) > + print_npe(npe, "unable to start\n"); > + free(image); > + return 0; > + > +too_big: > + print_npe(npe, "microcode block #%i doesn't fit in NPE " > + "memory: type %c, start 0x%X, length 0x%X\n", i, > + blk->type == FW_BLOCK_TYPE_INSTR ? 'I' : 'D', > + cb->npe_addr, cb->size); > +err: > + free(image); > + return err; > +} > + > + > +struct npe *npe_request(int id) > +{ > + if (id < ARRAY_SIZE(npe_tab)) > + if (npe_tab[id].valid) > + return &npe_tab[id]; > + return NULL; > +} > + > +static int __init npe_init(void) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(npe_tab); i++) { > + struct npe *npe = &npe_tab[i]; > + if (!(ixp4xx_read_feature_bits() & (IXP4XX_FEATURE_RESET_NPEA << i))) > + continue; /* NPE already disabled or not present */ > + if (npe_reset(npe)) > + continue; > + npe->valid = 1; > + } > + return 0; > +} > + > +coredevice_initcall(npe_init); > > _______________________________________________ > barebox mailing list > barebox@xxxxxxxxxxxxxxxxxxx > http://lists.infradead.org/mailman/listinfo/barebox _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox