[PATCH] ARM: Support for IXP4xx Network Processor Engines (NPEs).
Jean-Christophe PLAGNIOL-VILLARD
plagnioj at jcrosoft.com
Mon Apr 8 03:30:27 EDT 2013
On 21:57 Sun 07 Apr , Krzysztof Halasa wrote:
> Signed-off-by: Krzysztof Hałasa <khc at pm.waw.pl>
>
> 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 at pm.waw.pl>
> + *
> + * 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 at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/barebox
More information about the barebox
mailing list