[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