xref: /linux/drivers/soc/ixp4xx/ixp4xx-npe.c (revision a862a3f7b1b7f71211ac0ab7ab6dbb4afd8e50c5)
1eace21faSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2fcf2d897SLinus Walleij /*
3fcf2d897SLinus Walleij  * Intel IXP4xx Network Processor Engine driver for Linux
4fcf2d897SLinus Walleij  *
5fcf2d897SLinus Walleij  * Copyright (C) 2007 Krzysztof Halasa <khc@pm.waw.pl>
6fcf2d897SLinus Walleij  *
7fcf2d897SLinus Walleij  * The code is based on publicly available information:
8fcf2d897SLinus Walleij  * - Intel IXP4xx Developer's Manual and other e-papers
9fcf2d897SLinus Walleij  * - Intel IXP400 Access Library Software (BSD license)
10fcf2d897SLinus Walleij  * - previous works by Christian Hohnstaedt <chohnstaedt@innominate.com>
11fcf2d897SLinus Walleij  *   Thanks, Christian.
12fcf2d897SLinus Walleij  */
13fcf2d897SLinus Walleij 
14fcf2d897SLinus Walleij #include <linux/delay.h>
15fcf2d897SLinus Walleij #include <linux/dma-mapping.h>
16fcf2d897SLinus Walleij #include <linux/firmware.h>
17fcf2d897SLinus Walleij #include <linux/io.h>
18fcf2d897SLinus Walleij #include <linux/kernel.h>
198754a7e6SLinus Walleij #include <linux/mfd/syscon.h>
20fcf2d897SLinus Walleij #include <linux/module.h>
21fcf2d897SLinus Walleij #include <linux/of.h>
2276f24b4fSLinus Walleij #include <linux/of_platform.h>
23bc4d7eafSLinus Walleij #include <linux/platform_device.h>
244af20dc5SLinus Walleij #include <linux/soc/ixp4xx/npe.h>
2509aa9aabSArnd Bergmann #include <linux/soc/ixp4xx/cpu.h>
26fcf2d897SLinus Walleij 
27fcf2d897SLinus Walleij #define DEBUG_MSG			0
28fcf2d897SLinus Walleij #define DEBUG_FW			0
29fcf2d897SLinus Walleij 
30fcf2d897SLinus Walleij #define NPE_COUNT			3
31fcf2d897SLinus Walleij #define MAX_RETRIES			1000	/* microseconds */
32fcf2d897SLinus Walleij #define NPE_42X_DATA_SIZE		0x800	/* in dwords */
33fcf2d897SLinus Walleij #define NPE_46X_DATA_SIZE		0x1000
34fcf2d897SLinus Walleij #define NPE_A_42X_INSTR_SIZE		0x1000
35fcf2d897SLinus Walleij #define NPE_B_AND_C_42X_INSTR_SIZE	0x800
36fcf2d897SLinus Walleij #define NPE_46X_INSTR_SIZE		0x1000
37fcf2d897SLinus Walleij #define REGS_SIZE			0x1000
38fcf2d897SLinus Walleij 
39fcf2d897SLinus Walleij #define NPE_PHYS_REG			32
40fcf2d897SLinus Walleij 
41fcf2d897SLinus Walleij #define FW_MAGIC			0xFEEDF00D
42fcf2d897SLinus Walleij #define FW_BLOCK_TYPE_INSTR		0x0
43fcf2d897SLinus Walleij #define FW_BLOCK_TYPE_DATA		0x1
44fcf2d897SLinus Walleij #define FW_BLOCK_TYPE_EOF		0xF
45fcf2d897SLinus Walleij 
46fcf2d897SLinus Walleij /* NPE exec status (read) and command (write) */
47fcf2d897SLinus Walleij #define CMD_NPE_STEP			0x01
48fcf2d897SLinus Walleij #define CMD_NPE_START			0x02
49fcf2d897SLinus Walleij #define CMD_NPE_STOP			0x03
50fcf2d897SLinus Walleij #define CMD_NPE_CLR_PIPE		0x04
51fcf2d897SLinus Walleij #define CMD_CLR_PROFILE_CNT		0x0C
52fcf2d897SLinus Walleij #define CMD_RD_INS_MEM			0x10 /* instruction memory */
53fcf2d897SLinus Walleij #define CMD_WR_INS_MEM			0x11
54fcf2d897SLinus Walleij #define CMD_RD_DATA_MEM			0x12 /* data memory */
55fcf2d897SLinus Walleij #define CMD_WR_DATA_MEM			0x13
56fcf2d897SLinus Walleij #define CMD_RD_ECS_REG			0x14 /* exec access register */
57fcf2d897SLinus Walleij #define CMD_WR_ECS_REG			0x15
58fcf2d897SLinus Walleij 
59fcf2d897SLinus Walleij #define STAT_RUN			0x80000000
60fcf2d897SLinus Walleij #define STAT_STOP			0x40000000
61fcf2d897SLinus Walleij #define STAT_CLEAR			0x20000000
62fcf2d897SLinus Walleij #define STAT_ECS_K			0x00800000 /* pipeline clean */
63fcf2d897SLinus Walleij 
64fcf2d897SLinus Walleij #define NPE_STEVT			0x1B
65fcf2d897SLinus Walleij #define NPE_STARTPC			0x1C
66fcf2d897SLinus Walleij #define NPE_REGMAP			0x1E
67fcf2d897SLinus Walleij #define NPE_CINDEX			0x1F
68fcf2d897SLinus Walleij 
69fcf2d897SLinus Walleij #define INSTR_WR_REG_SHORT		0x0000C000
70fcf2d897SLinus Walleij #define INSTR_WR_REG_BYTE		0x00004000
71fcf2d897SLinus Walleij #define INSTR_RD_FIFO			0x0F888220
72fcf2d897SLinus Walleij #define INSTR_RESET_MBOX		0x0FAC8210
73fcf2d897SLinus Walleij 
74fcf2d897SLinus Walleij #define ECS_BG_CTXT_REG_0		0x00 /* Background Executing Context */
75fcf2d897SLinus Walleij #define ECS_BG_CTXT_REG_1		0x01 /*		Stack level */
76fcf2d897SLinus Walleij #define ECS_BG_CTXT_REG_2		0x02
77fcf2d897SLinus Walleij #define ECS_PRI_1_CTXT_REG_0		0x04 /* Priority 1 Executing Context */
78fcf2d897SLinus Walleij #define ECS_PRI_1_CTXT_REG_1		0x05 /*		Stack level */
79fcf2d897SLinus Walleij #define ECS_PRI_1_CTXT_REG_2		0x06
80fcf2d897SLinus Walleij #define ECS_PRI_2_CTXT_REG_0		0x08 /* Priority 2 Executing Context */
81fcf2d897SLinus Walleij #define ECS_PRI_2_CTXT_REG_1		0x09 /*		Stack level */
82fcf2d897SLinus Walleij #define ECS_PRI_2_CTXT_REG_2		0x0A
83fcf2d897SLinus Walleij #define ECS_DBG_CTXT_REG_0		0x0C /* Debug Executing Context */
84fcf2d897SLinus Walleij #define ECS_DBG_CTXT_REG_1		0x0D /*		Stack level */
85fcf2d897SLinus Walleij #define ECS_DBG_CTXT_REG_2		0x0E
86fcf2d897SLinus Walleij #define ECS_INSTRUCT_REG		0x11 /* NPE Instruction Register */
87fcf2d897SLinus Walleij 
88fcf2d897SLinus Walleij #define ECS_REG_0_ACTIVE		0x80000000 /* all levels */
89fcf2d897SLinus Walleij #define ECS_REG_0_NEXTPC_MASK		0x1FFF0000 /* BG/PRI1/PRI2 levels */
90fcf2d897SLinus Walleij #define ECS_REG_0_LDUR_BITS		8
91fcf2d897SLinus Walleij #define ECS_REG_0_LDUR_MASK		0x00000700 /* all levels */
92fcf2d897SLinus Walleij #define ECS_REG_1_CCTXT_BITS		16
93fcf2d897SLinus Walleij #define ECS_REG_1_CCTXT_MASK		0x000F0000 /* all levels */
94fcf2d897SLinus Walleij #define ECS_REG_1_SELCTXT_BITS		0
95fcf2d897SLinus Walleij #define ECS_REG_1_SELCTXT_MASK		0x0000000F /* all levels */
96fcf2d897SLinus Walleij #define ECS_DBG_REG_2_IF		0x00100000 /* debug level */
97fcf2d897SLinus Walleij #define ECS_DBG_REG_2_IE		0x00080000 /* debug level */
98fcf2d897SLinus Walleij 
99fcf2d897SLinus Walleij /* NPE watchpoint_fifo register bit */
100fcf2d897SLinus Walleij #define WFIFO_VALID			0x80000000
101fcf2d897SLinus Walleij 
102fcf2d897SLinus Walleij /* NPE messaging_status register bit definitions */
103fcf2d897SLinus Walleij #define MSGSTAT_OFNE	0x00010000 /* OutFifoNotEmpty */
104fcf2d897SLinus Walleij #define MSGSTAT_IFNF	0x00020000 /* InFifoNotFull */
105fcf2d897SLinus Walleij #define MSGSTAT_OFNF	0x00040000 /* OutFifoNotFull */
106fcf2d897SLinus Walleij #define MSGSTAT_IFNE	0x00080000 /* InFifoNotEmpty */
107fcf2d897SLinus Walleij #define MSGSTAT_MBINT	0x00100000 /* Mailbox interrupt */
108fcf2d897SLinus Walleij #define MSGSTAT_IFINT	0x00200000 /* InFifo interrupt */
109fcf2d897SLinus Walleij #define MSGSTAT_OFINT	0x00400000 /* OutFifo interrupt */
110fcf2d897SLinus Walleij #define MSGSTAT_WFINT	0x00800000 /* WatchFifo interrupt */
111fcf2d897SLinus Walleij 
112fcf2d897SLinus Walleij /* NPE messaging_control register bit definitions */
113fcf2d897SLinus Walleij #define MSGCTL_OUT_FIFO			0x00010000 /* enable output FIFO */
114fcf2d897SLinus Walleij #define MSGCTL_IN_FIFO			0x00020000 /* enable input FIFO */
115fcf2d897SLinus Walleij #define MSGCTL_OUT_FIFO_WRITE		0x01000000 /* enable FIFO + WRITE */
116fcf2d897SLinus Walleij #define MSGCTL_IN_FIFO_WRITE		0x02000000
117fcf2d897SLinus Walleij 
118fcf2d897SLinus Walleij /* NPE mailbox_status value for reset */
119fcf2d897SLinus Walleij #define RESET_MBOX_STAT			0x0000F0F0
120fcf2d897SLinus Walleij 
121fcf2d897SLinus Walleij #define NPE_A_FIRMWARE "NPE-A"
122fcf2d897SLinus Walleij #define NPE_B_FIRMWARE "NPE-B"
123fcf2d897SLinus Walleij #define NPE_C_FIRMWARE "NPE-C"
124fcf2d897SLinus Walleij 
125fcf2d897SLinus Walleij const char *npe_names[] = { NPE_A_FIRMWARE, NPE_B_FIRMWARE, NPE_C_FIRMWARE };
126fcf2d897SLinus Walleij 
127fcf2d897SLinus Walleij #define print_npe(pri, npe, fmt, ...)					\
128fcf2d897SLinus Walleij 	printk(pri "%s: " fmt, npe_name(npe), ## __VA_ARGS__)
129fcf2d897SLinus Walleij 
130fcf2d897SLinus Walleij #if DEBUG_MSG
131fcf2d897SLinus Walleij #define debug_msg(npe, fmt, ...)					\
132fcf2d897SLinus Walleij 	print_npe(KERN_DEBUG, npe, fmt, ## __VA_ARGS__)
133fcf2d897SLinus Walleij #else
134fcf2d897SLinus Walleij #define debug_msg(npe, fmt, ...)
135fcf2d897SLinus Walleij #endif
136fcf2d897SLinus Walleij 
137fcf2d897SLinus Walleij static struct {
138fcf2d897SLinus Walleij 	u32 reg, val;
139fcf2d897SLinus Walleij } ecs_reset[] = {
140fcf2d897SLinus Walleij 	{ ECS_BG_CTXT_REG_0,	0xA0000000 },
141fcf2d897SLinus Walleij 	{ ECS_BG_CTXT_REG_1,	0x01000000 },
142fcf2d897SLinus Walleij 	{ ECS_BG_CTXT_REG_2,	0x00008000 },
143fcf2d897SLinus Walleij 	{ ECS_PRI_1_CTXT_REG_0,	0x20000080 },
144fcf2d897SLinus Walleij 	{ ECS_PRI_1_CTXT_REG_1,	0x01000000 },
145fcf2d897SLinus Walleij 	{ ECS_PRI_1_CTXT_REG_2,	0x00008000 },
146fcf2d897SLinus Walleij 	{ ECS_PRI_2_CTXT_REG_0,	0x20000080 },
147fcf2d897SLinus Walleij 	{ ECS_PRI_2_CTXT_REG_1,	0x01000000 },
148fcf2d897SLinus Walleij 	{ ECS_PRI_2_CTXT_REG_2,	0x00008000 },
149fcf2d897SLinus Walleij 	{ ECS_DBG_CTXT_REG_0,	0x20000000 },
150fcf2d897SLinus Walleij 	{ ECS_DBG_CTXT_REG_1,	0x00000000 },
151fcf2d897SLinus Walleij 	{ ECS_DBG_CTXT_REG_2,	0x001E0000 },
152fcf2d897SLinus Walleij 	{ ECS_INSTRUCT_REG,	0x1003C00F },
153fcf2d897SLinus Walleij };
154fcf2d897SLinus Walleij 
155fcf2d897SLinus Walleij static struct npe npe_tab[NPE_COUNT] = {
156fcf2d897SLinus Walleij 	{
157fcf2d897SLinus Walleij 		.id	= 0,
158fcf2d897SLinus Walleij 	}, {
159fcf2d897SLinus Walleij 		.id	= 1,
160fcf2d897SLinus Walleij 	}, {
161fcf2d897SLinus Walleij 		.id	= 2,
162fcf2d897SLinus Walleij 	}
163fcf2d897SLinus Walleij };
164fcf2d897SLinus Walleij 
165fcf2d897SLinus Walleij int npe_running(struct npe *npe)
166fcf2d897SLinus Walleij {
167fcf2d897SLinus Walleij 	return (__raw_readl(&npe->regs->exec_status_cmd) & STAT_RUN) != 0;
168fcf2d897SLinus Walleij }
169fcf2d897SLinus Walleij 
170fcf2d897SLinus Walleij static void npe_cmd_write(struct npe *npe, u32 addr, int cmd, u32 data)
171fcf2d897SLinus Walleij {
172fcf2d897SLinus Walleij 	__raw_writel(data, &npe->regs->exec_data);
173fcf2d897SLinus Walleij 	__raw_writel(addr, &npe->regs->exec_addr);
174fcf2d897SLinus Walleij 	__raw_writel(cmd, &npe->regs->exec_status_cmd);
175fcf2d897SLinus Walleij }
176fcf2d897SLinus Walleij 
177fcf2d897SLinus Walleij static u32 npe_cmd_read(struct npe *npe, u32 addr, int cmd)
178fcf2d897SLinus Walleij {
179fcf2d897SLinus Walleij 	__raw_writel(addr, &npe->regs->exec_addr);
180fcf2d897SLinus Walleij 	__raw_writel(cmd, &npe->regs->exec_status_cmd);
181fcf2d897SLinus Walleij 	/* Iintroduce extra read cycles after issuing read command to NPE
182fcf2d897SLinus Walleij 	   so that we read the register after the NPE has updated it.
183fcf2d897SLinus Walleij 	   This is to overcome race condition between XScale and NPE */
184fcf2d897SLinus Walleij 	__raw_readl(&npe->regs->exec_data);
185fcf2d897SLinus Walleij 	__raw_readl(&npe->regs->exec_data);
186fcf2d897SLinus Walleij 	return __raw_readl(&npe->regs->exec_data);
187fcf2d897SLinus Walleij }
188fcf2d897SLinus Walleij 
189fcf2d897SLinus Walleij static void npe_clear_active(struct npe *npe, u32 reg)
190fcf2d897SLinus Walleij {
191fcf2d897SLinus Walleij 	u32 val = npe_cmd_read(npe, reg, CMD_RD_ECS_REG);
192fcf2d897SLinus Walleij 	npe_cmd_write(npe, reg, CMD_WR_ECS_REG, val & ~ECS_REG_0_ACTIVE);
193fcf2d897SLinus Walleij }
194fcf2d897SLinus Walleij 
195fcf2d897SLinus Walleij static void npe_start(struct npe *npe)
196fcf2d897SLinus Walleij {
197fcf2d897SLinus Walleij 	/* ensure only Background Context Stack Level is active */
198fcf2d897SLinus Walleij 	npe_clear_active(npe, ECS_PRI_1_CTXT_REG_0);
199fcf2d897SLinus Walleij 	npe_clear_active(npe, ECS_PRI_2_CTXT_REG_0);
200fcf2d897SLinus Walleij 	npe_clear_active(npe, ECS_DBG_CTXT_REG_0);
201fcf2d897SLinus Walleij 
202fcf2d897SLinus Walleij 	__raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd);
203fcf2d897SLinus Walleij 	__raw_writel(CMD_NPE_START, &npe->regs->exec_status_cmd);
204fcf2d897SLinus Walleij }
205fcf2d897SLinus Walleij 
206fcf2d897SLinus Walleij static void npe_stop(struct npe *npe)
207fcf2d897SLinus Walleij {
208fcf2d897SLinus Walleij 	__raw_writel(CMD_NPE_STOP, &npe->regs->exec_status_cmd);
209fcf2d897SLinus Walleij 	__raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd); /*FIXME?*/
210fcf2d897SLinus Walleij }
211fcf2d897SLinus Walleij 
212fcf2d897SLinus Walleij static int __must_check npe_debug_instr(struct npe *npe, u32 instr, u32 ctx,
213fcf2d897SLinus Walleij 					u32 ldur)
214fcf2d897SLinus Walleij {
215fcf2d897SLinus Walleij 	u32 wc;
216fcf2d897SLinus Walleij 	int i;
217fcf2d897SLinus Walleij 
218fcf2d897SLinus Walleij 	/* set the Active bit, and the LDUR, in the debug level */
219fcf2d897SLinus Walleij 	npe_cmd_write(npe, ECS_DBG_CTXT_REG_0, CMD_WR_ECS_REG,
220fcf2d897SLinus Walleij 		      ECS_REG_0_ACTIVE | (ldur << ECS_REG_0_LDUR_BITS));
221fcf2d897SLinus Walleij 
222fcf2d897SLinus Walleij 	/* set CCTXT at ECS DEBUG L3 to specify in which context to execute
223fcf2d897SLinus Walleij 	   the instruction, and set SELCTXT at ECS DEBUG Level to specify
224fcf2d897SLinus Walleij 	   which context store to access.
225fcf2d897SLinus Walleij 	   Debug ECS Level Reg 1 has form 0x000n000n, where n = context number
226fcf2d897SLinus Walleij 	*/
227fcf2d897SLinus Walleij 	npe_cmd_write(npe, ECS_DBG_CTXT_REG_1, CMD_WR_ECS_REG,
228fcf2d897SLinus Walleij 		      (ctx << ECS_REG_1_CCTXT_BITS) |
229fcf2d897SLinus Walleij 		      (ctx << ECS_REG_1_SELCTXT_BITS));
230fcf2d897SLinus Walleij 
231fcf2d897SLinus Walleij 	/* clear the pipeline */
232fcf2d897SLinus Walleij 	__raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd);
233fcf2d897SLinus Walleij 
234fcf2d897SLinus Walleij 	/* load NPE instruction into the instruction register */
235fcf2d897SLinus Walleij 	npe_cmd_write(npe, ECS_INSTRUCT_REG, CMD_WR_ECS_REG, instr);
236fcf2d897SLinus Walleij 
237fcf2d897SLinus Walleij 	/* we need this value later to wait for completion of NPE execution
238fcf2d897SLinus Walleij 	   step */
239fcf2d897SLinus Walleij 	wc = __raw_readl(&npe->regs->watch_count);
240fcf2d897SLinus Walleij 
241fcf2d897SLinus Walleij 	/* issue a Step One command via the Execution Control register */
242fcf2d897SLinus Walleij 	__raw_writel(CMD_NPE_STEP, &npe->regs->exec_status_cmd);
243fcf2d897SLinus Walleij 
244fcf2d897SLinus Walleij 	/* Watch Count register increments when NPE completes an instruction */
245fcf2d897SLinus Walleij 	for (i = 0; i < MAX_RETRIES; i++) {
246fcf2d897SLinus Walleij 		if (wc != __raw_readl(&npe->regs->watch_count))
247fcf2d897SLinus Walleij 			return 0;
248fcf2d897SLinus Walleij 		udelay(1);
249fcf2d897SLinus Walleij 	}
250fcf2d897SLinus Walleij 
251fcf2d897SLinus Walleij 	print_npe(KERN_ERR, npe, "reset: npe_debug_instr(): timeout\n");
252fcf2d897SLinus Walleij 	return -ETIMEDOUT;
253fcf2d897SLinus Walleij }
254fcf2d897SLinus Walleij 
255fcf2d897SLinus Walleij static int __must_check npe_logical_reg_write8(struct npe *npe, u32 addr,
256fcf2d897SLinus Walleij 					       u8 val, u32 ctx)
257fcf2d897SLinus Walleij {
258fcf2d897SLinus Walleij 	/* here we build the NPE assembler instruction: mov8 d0, #0 */
259fcf2d897SLinus Walleij 	u32 instr = INSTR_WR_REG_BYTE |	/* OpCode */
260fcf2d897SLinus Walleij 		addr << 9 |		/* base Operand */
261fcf2d897SLinus Walleij 		(val & 0x1F) << 4 |	/* lower 5 bits to immediate data */
262fcf2d897SLinus Walleij 		(val & ~0x1F) << (18 - 5);/* higher 3 bits to CoProc instr. */
263fcf2d897SLinus Walleij 	return npe_debug_instr(npe, instr, ctx, 1); /* execute it */
264fcf2d897SLinus Walleij }
265fcf2d897SLinus Walleij 
266fcf2d897SLinus Walleij static int __must_check npe_logical_reg_write16(struct npe *npe, u32 addr,
267fcf2d897SLinus Walleij 						u16 val, u32 ctx)
268fcf2d897SLinus Walleij {
269fcf2d897SLinus Walleij 	/* here we build the NPE assembler instruction: mov16 d0, #0 */
270fcf2d897SLinus Walleij 	u32 instr = INSTR_WR_REG_SHORT | /* OpCode */
271fcf2d897SLinus Walleij 		addr << 9 |		/* base Operand */
272fcf2d897SLinus Walleij 		(val & 0x1F) << 4 |	/* lower 5 bits to immediate data */
273fcf2d897SLinus Walleij 		(val & ~0x1F) << (18 - 5);/* higher 11 bits to CoProc instr. */
274fcf2d897SLinus Walleij 	return npe_debug_instr(npe, instr, ctx, 1); /* execute it */
275fcf2d897SLinus Walleij }
276fcf2d897SLinus Walleij 
277fcf2d897SLinus Walleij static int __must_check npe_logical_reg_write32(struct npe *npe, u32 addr,
278fcf2d897SLinus Walleij 						u32 val, u32 ctx)
279fcf2d897SLinus Walleij {
280fcf2d897SLinus Walleij 	/* write in 16 bit steps first the high and then the low value */
281fcf2d897SLinus Walleij 	if (npe_logical_reg_write16(npe, addr, val >> 16, ctx))
282fcf2d897SLinus Walleij 		return -ETIMEDOUT;
283fcf2d897SLinus Walleij 	return npe_logical_reg_write16(npe, addr + 2, val & 0xFFFF, ctx);
284fcf2d897SLinus Walleij }
285fcf2d897SLinus Walleij 
286fcf2d897SLinus Walleij static int npe_reset(struct npe *npe)
287fcf2d897SLinus Walleij {
2888754a7e6SLinus Walleij 	u32 reset_bit = (IXP4XX_FEATURE_RESET_NPEA << npe->id);
289fcf2d897SLinus Walleij 	u32 val, ctl, exec_count, ctx_reg2;
290fcf2d897SLinus Walleij 	int i;
291fcf2d897SLinus Walleij 
292fcf2d897SLinus Walleij 	ctl = (__raw_readl(&npe->regs->messaging_control) | 0x3F000000) &
293fcf2d897SLinus Walleij 		0x3F3FFFFF;
294fcf2d897SLinus Walleij 
295fcf2d897SLinus Walleij 	/* disable parity interrupt */
296fcf2d897SLinus Walleij 	__raw_writel(ctl & 0x3F00FFFF, &npe->regs->messaging_control);
297fcf2d897SLinus Walleij 
298fcf2d897SLinus Walleij 	/* pre exec - debug instruction */
299fcf2d897SLinus Walleij 	/* turn off the halt bit by clearing Execution Count register. */
300fcf2d897SLinus Walleij 	exec_count = __raw_readl(&npe->regs->exec_count);
301fcf2d897SLinus Walleij 	__raw_writel(0, &npe->regs->exec_count);
302fcf2d897SLinus Walleij 	/* ensure that IF and IE are on (temporarily), so that we don't end up
303fcf2d897SLinus Walleij 	   stepping forever */
304fcf2d897SLinus Walleij 	ctx_reg2 = npe_cmd_read(npe, ECS_DBG_CTXT_REG_2, CMD_RD_ECS_REG);
305fcf2d897SLinus Walleij 	npe_cmd_write(npe, ECS_DBG_CTXT_REG_2, CMD_WR_ECS_REG, ctx_reg2 |
306fcf2d897SLinus Walleij 		      ECS_DBG_REG_2_IF | ECS_DBG_REG_2_IE);
307fcf2d897SLinus Walleij 
308fcf2d897SLinus Walleij 	/* clear the FIFOs */
309fcf2d897SLinus Walleij 	while (__raw_readl(&npe->regs->watchpoint_fifo) & WFIFO_VALID)
310fcf2d897SLinus Walleij 		;
311fcf2d897SLinus Walleij 	while (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_OFNE)
312fcf2d897SLinus Walleij 		/* read from the outFIFO until empty */
313fcf2d897SLinus Walleij 		print_npe(KERN_DEBUG, npe, "npe_reset: read FIFO = 0x%X\n",
314fcf2d897SLinus Walleij 			  __raw_readl(&npe->regs->in_out_fifo));
315fcf2d897SLinus Walleij 
316fcf2d897SLinus Walleij 	while (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNE)
317fcf2d897SLinus Walleij 		/* step execution of the NPE intruction to read inFIFO using
318fcf2d897SLinus Walleij 		   the Debug Executing Context stack */
319fcf2d897SLinus Walleij 		if (npe_debug_instr(npe, INSTR_RD_FIFO, 0, 0))
320fcf2d897SLinus Walleij 			return -ETIMEDOUT;
321fcf2d897SLinus Walleij 
322fcf2d897SLinus Walleij 	/* reset the mailbox reg from the XScale side */
323fcf2d897SLinus Walleij 	__raw_writel(RESET_MBOX_STAT, &npe->regs->mailbox_status);
324fcf2d897SLinus Walleij 	/* from NPE side */
325fcf2d897SLinus Walleij 	if (npe_debug_instr(npe, INSTR_RESET_MBOX, 0, 0))
326fcf2d897SLinus Walleij 		return -ETIMEDOUT;
327fcf2d897SLinus Walleij 
328fcf2d897SLinus Walleij 	/* Reset the physical registers in the NPE register file */
329fcf2d897SLinus Walleij 	for (val = 0; val < NPE_PHYS_REG; val++) {
330fcf2d897SLinus Walleij 		if (npe_logical_reg_write16(npe, NPE_REGMAP, val >> 1, 0))
331fcf2d897SLinus Walleij 			return -ETIMEDOUT;
332fcf2d897SLinus Walleij 		/* address is either 0 or 4 */
333fcf2d897SLinus Walleij 		if (npe_logical_reg_write32(npe, (val & 1) * 4, 0, 0))
334fcf2d897SLinus Walleij 			return -ETIMEDOUT;
335fcf2d897SLinus Walleij 	}
336fcf2d897SLinus Walleij 
337fcf2d897SLinus Walleij 	/* Reset the context store = each context's Context Store registers */
338fcf2d897SLinus Walleij 
339fcf2d897SLinus Walleij 	/* Context 0 has no STARTPC. Instead, this value is used to set NextPC
340fcf2d897SLinus Walleij 	   for Background ECS, to set where NPE starts executing code */
341fcf2d897SLinus Walleij 	val = npe_cmd_read(npe, ECS_BG_CTXT_REG_0, CMD_RD_ECS_REG);
342fcf2d897SLinus Walleij 	val &= ~ECS_REG_0_NEXTPC_MASK;
343fcf2d897SLinus Walleij 	val |= (0 /* NextPC */ << 16) & ECS_REG_0_NEXTPC_MASK;
344fcf2d897SLinus Walleij 	npe_cmd_write(npe, ECS_BG_CTXT_REG_0, CMD_WR_ECS_REG, val);
345fcf2d897SLinus Walleij 
346fcf2d897SLinus Walleij 	for (i = 0; i < 16; i++) {
347fcf2d897SLinus Walleij 		if (i) {	/* Context 0 has no STEVT nor STARTPC */
348fcf2d897SLinus Walleij 			/* STEVT = off, 0x80 */
349fcf2d897SLinus Walleij 			if (npe_logical_reg_write8(npe, NPE_STEVT, 0x80, i))
350fcf2d897SLinus Walleij 				return -ETIMEDOUT;
351fcf2d897SLinus Walleij 			if (npe_logical_reg_write16(npe, NPE_STARTPC, 0, i))
352fcf2d897SLinus Walleij 				return -ETIMEDOUT;
353fcf2d897SLinus Walleij 		}
354fcf2d897SLinus Walleij 		/* REGMAP = d0->p0, d8->p2, d16->p4 */
355fcf2d897SLinus Walleij 		if (npe_logical_reg_write16(npe, NPE_REGMAP, 0x820, i))
356fcf2d897SLinus Walleij 			return -ETIMEDOUT;
357fcf2d897SLinus Walleij 		if (npe_logical_reg_write8(npe, NPE_CINDEX, 0, i))
358fcf2d897SLinus Walleij 			return -ETIMEDOUT;
359fcf2d897SLinus Walleij 	}
360fcf2d897SLinus Walleij 
361fcf2d897SLinus Walleij 	/* post exec */
362fcf2d897SLinus Walleij 	/* clear active bit in debug level */
363fcf2d897SLinus Walleij 	npe_cmd_write(npe, ECS_DBG_CTXT_REG_0, CMD_WR_ECS_REG, 0);
364fcf2d897SLinus Walleij 	/* clear the pipeline */
365fcf2d897SLinus Walleij 	__raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd);
366fcf2d897SLinus Walleij 	/* restore previous values */
367fcf2d897SLinus Walleij 	__raw_writel(exec_count, &npe->regs->exec_count);
368fcf2d897SLinus Walleij 	npe_cmd_write(npe, ECS_DBG_CTXT_REG_2, CMD_WR_ECS_REG, ctx_reg2);
369fcf2d897SLinus Walleij 
370fcf2d897SLinus Walleij 	/* write reset values to Execution Context Stack registers */
371fcf2d897SLinus Walleij 	for (val = 0; val < ARRAY_SIZE(ecs_reset); val++)
372fcf2d897SLinus Walleij 		npe_cmd_write(npe, ecs_reset[val].reg, CMD_WR_ECS_REG,
373fcf2d897SLinus Walleij 			      ecs_reset[val].val);
374fcf2d897SLinus Walleij 
375fcf2d897SLinus Walleij 	/* clear the profile counter */
376fcf2d897SLinus Walleij 	__raw_writel(CMD_CLR_PROFILE_CNT, &npe->regs->exec_status_cmd);
377fcf2d897SLinus Walleij 
378fcf2d897SLinus Walleij 	__raw_writel(0, &npe->regs->exec_count);
379fcf2d897SLinus Walleij 	__raw_writel(0, &npe->regs->action_points[0]);
380fcf2d897SLinus Walleij 	__raw_writel(0, &npe->regs->action_points[1]);
381fcf2d897SLinus Walleij 	__raw_writel(0, &npe->regs->action_points[2]);
382fcf2d897SLinus Walleij 	__raw_writel(0, &npe->regs->action_points[3]);
383fcf2d897SLinus Walleij 	__raw_writel(0, &npe->regs->watch_count);
384fcf2d897SLinus Walleij 
3858754a7e6SLinus Walleij 	/*
3868754a7e6SLinus Walleij 	 * We need to work on cached values here because the register
3878754a7e6SLinus Walleij 	 * will read inverted but needs to be written non-inverted.
3888754a7e6SLinus Walleij 	 */
3898754a7e6SLinus Walleij 	val = cpu_ixp4xx_features(npe->rmap);
390fcf2d897SLinus Walleij 	/* reset the NPE */
3918754a7e6SLinus Walleij 	regmap_write(npe->rmap, IXP4XX_EXP_CNFG2, val & ~reset_bit);
392fcf2d897SLinus Walleij 	/* deassert reset */
3938754a7e6SLinus Walleij 	regmap_write(npe->rmap, IXP4XX_EXP_CNFG2, val | reset_bit);
3948754a7e6SLinus Walleij 
395fcf2d897SLinus Walleij 	for (i = 0; i < MAX_RETRIES; i++) {
3968754a7e6SLinus Walleij 		val = cpu_ixp4xx_features(npe->rmap);
3978754a7e6SLinus Walleij 		if (val & reset_bit)
398fcf2d897SLinus Walleij 			break;	/* NPE is back alive */
399fcf2d897SLinus Walleij 		udelay(1);
400fcf2d897SLinus Walleij 	}
401fcf2d897SLinus Walleij 	if (i == MAX_RETRIES)
402fcf2d897SLinus Walleij 		return -ETIMEDOUT;
403fcf2d897SLinus Walleij 
404fcf2d897SLinus Walleij 	npe_stop(npe);
405fcf2d897SLinus Walleij 
406fcf2d897SLinus Walleij 	/* restore NPE configuration bus Control Register - parity settings */
407fcf2d897SLinus Walleij 	__raw_writel(ctl, &npe->regs->messaging_control);
408fcf2d897SLinus Walleij 	return 0;
409fcf2d897SLinus Walleij }
410fcf2d897SLinus Walleij 
411fcf2d897SLinus Walleij 
412fcf2d897SLinus Walleij int npe_send_message(struct npe *npe, const void *msg, const char *what)
413fcf2d897SLinus Walleij {
414fcf2d897SLinus Walleij 	const u32 *send = msg;
415fcf2d897SLinus Walleij 	int cycles = 0;
416fcf2d897SLinus Walleij 
417fcf2d897SLinus Walleij 	debug_msg(npe, "Trying to send message %s [%08X:%08X]\n",
418fcf2d897SLinus Walleij 		  what, send[0], send[1]);
419fcf2d897SLinus Walleij 
420fcf2d897SLinus Walleij 	if (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNE) {
421fcf2d897SLinus Walleij 		debug_msg(npe, "NPE input FIFO not empty\n");
422fcf2d897SLinus Walleij 		return -EIO;
423fcf2d897SLinus Walleij 	}
424fcf2d897SLinus Walleij 
425fcf2d897SLinus Walleij 	__raw_writel(send[0], &npe->regs->in_out_fifo);
426fcf2d897SLinus Walleij 
427fcf2d897SLinus Walleij 	if (!(__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNF)) {
428fcf2d897SLinus Walleij 		debug_msg(npe, "NPE input FIFO full\n");
429fcf2d897SLinus Walleij 		return -EIO;
430fcf2d897SLinus Walleij 	}
431fcf2d897SLinus Walleij 
432fcf2d897SLinus Walleij 	__raw_writel(send[1], &npe->regs->in_out_fifo);
433fcf2d897SLinus Walleij 
434fcf2d897SLinus Walleij 	while ((cycles < MAX_RETRIES) &&
435fcf2d897SLinus Walleij 	       (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNE)) {
436fcf2d897SLinus Walleij 		udelay(1);
437fcf2d897SLinus Walleij 		cycles++;
438fcf2d897SLinus Walleij 	}
439fcf2d897SLinus Walleij 
440fcf2d897SLinus Walleij 	if (cycles == MAX_RETRIES) {
441fcf2d897SLinus Walleij 		debug_msg(npe, "Timeout sending message\n");
442fcf2d897SLinus Walleij 		return -ETIMEDOUT;
443fcf2d897SLinus Walleij 	}
444fcf2d897SLinus Walleij 
445fcf2d897SLinus Walleij #if DEBUG_MSG > 1
446fcf2d897SLinus Walleij 	debug_msg(npe, "Sending a message took %i cycles\n", cycles);
447fcf2d897SLinus Walleij #endif
448fcf2d897SLinus Walleij 	return 0;
449fcf2d897SLinus Walleij }
450fcf2d897SLinus Walleij 
451fcf2d897SLinus Walleij int npe_recv_message(struct npe *npe, void *msg, const char *what)
452fcf2d897SLinus Walleij {
453fcf2d897SLinus Walleij 	u32 *recv = msg;
454fcf2d897SLinus Walleij 	int cycles = 0, cnt = 0;
455fcf2d897SLinus Walleij 
456fcf2d897SLinus Walleij 	debug_msg(npe, "Trying to receive message %s\n", what);
457fcf2d897SLinus Walleij 
458fcf2d897SLinus Walleij 	while (cycles < MAX_RETRIES) {
459fcf2d897SLinus Walleij 		if (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_OFNE) {
460fcf2d897SLinus Walleij 			recv[cnt++] = __raw_readl(&npe->regs->in_out_fifo);
461fcf2d897SLinus Walleij 			if (cnt == 2)
462fcf2d897SLinus Walleij 				break;
463fcf2d897SLinus Walleij 		} else {
464fcf2d897SLinus Walleij 			udelay(1);
465fcf2d897SLinus Walleij 			cycles++;
466fcf2d897SLinus Walleij 		}
467fcf2d897SLinus Walleij 	}
468fcf2d897SLinus Walleij 
469fcf2d897SLinus Walleij 	switch(cnt) {
470fcf2d897SLinus Walleij 	case 1:
471fcf2d897SLinus Walleij 		debug_msg(npe, "Received [%08X]\n", recv[0]);
472fcf2d897SLinus Walleij 		break;
473fcf2d897SLinus Walleij 	case 2:
474fcf2d897SLinus Walleij 		debug_msg(npe, "Received [%08X:%08X]\n", recv[0], recv[1]);
475fcf2d897SLinus Walleij 		break;
476fcf2d897SLinus Walleij 	}
477fcf2d897SLinus Walleij 
478fcf2d897SLinus Walleij 	if (cycles == MAX_RETRIES) {
479fcf2d897SLinus Walleij 		debug_msg(npe, "Timeout waiting for message\n");
480fcf2d897SLinus Walleij 		return -ETIMEDOUT;
481fcf2d897SLinus Walleij 	}
482fcf2d897SLinus Walleij 
483fcf2d897SLinus Walleij #if DEBUG_MSG > 1
484fcf2d897SLinus Walleij 	debug_msg(npe, "Receiving a message took %i cycles\n", cycles);
485fcf2d897SLinus Walleij #endif
486fcf2d897SLinus Walleij 	return 0;
487fcf2d897SLinus Walleij }
488fcf2d897SLinus Walleij 
489fcf2d897SLinus Walleij int npe_send_recv_message(struct npe *npe, void *msg, const char *what)
490fcf2d897SLinus Walleij {
491fcf2d897SLinus Walleij 	int result;
492fcf2d897SLinus Walleij 	u32 *send = msg, recv[2];
493fcf2d897SLinus Walleij 
494fcf2d897SLinus Walleij 	if ((result = npe_send_message(npe, msg, what)) != 0)
495fcf2d897SLinus Walleij 		return result;
496fcf2d897SLinus Walleij 	if ((result = npe_recv_message(npe, recv, what)) != 0)
497fcf2d897SLinus Walleij 		return result;
498fcf2d897SLinus Walleij 
499fcf2d897SLinus Walleij 	if ((recv[0] != send[0]) || (recv[1] != send[1])) {
500fcf2d897SLinus Walleij 		debug_msg(npe, "Message %s: unexpected message received\n",
501fcf2d897SLinus Walleij 			  what);
502fcf2d897SLinus Walleij 		return -EIO;
503fcf2d897SLinus Walleij 	}
504fcf2d897SLinus Walleij 	return 0;
505fcf2d897SLinus Walleij }
506fcf2d897SLinus Walleij 
507fcf2d897SLinus Walleij 
508fcf2d897SLinus Walleij int npe_load_firmware(struct npe *npe, const char *name, struct device *dev)
509fcf2d897SLinus Walleij {
510fcf2d897SLinus Walleij 	const struct firmware *fw_entry;
511fcf2d897SLinus Walleij 
512fcf2d897SLinus Walleij 	struct dl_block {
513fcf2d897SLinus Walleij 		u32 type;
514fcf2d897SLinus Walleij 		u32 offset;
515fcf2d897SLinus Walleij 	} *blk;
516fcf2d897SLinus Walleij 
517fcf2d897SLinus Walleij 	struct dl_image {
518fcf2d897SLinus Walleij 		u32 magic;
519fcf2d897SLinus Walleij 		u32 id;
520fcf2d897SLinus Walleij 		u32 size;
521fcf2d897SLinus Walleij 		union {
522b5fc3ca3SKees Cook 			DECLARE_FLEX_ARRAY(u32, data);
523b5fc3ca3SKees Cook 			DECLARE_FLEX_ARRAY(struct dl_block, blocks);
524fcf2d897SLinus Walleij 		};
525fcf2d897SLinus Walleij 	} *image;
526fcf2d897SLinus Walleij 
527fcf2d897SLinus Walleij 	struct dl_codeblock {
528fcf2d897SLinus Walleij 		u32 npe_addr;
529fcf2d897SLinus Walleij 		u32 size;
530b5fc3ca3SKees Cook 		u32 data[];
531fcf2d897SLinus Walleij 	} *cb;
532fcf2d897SLinus Walleij 
533fcf2d897SLinus Walleij 	int i, j, err, data_size, instr_size, blocks, table_end;
534fcf2d897SLinus Walleij 	u32 cmd;
535fcf2d897SLinus Walleij 
536fcf2d897SLinus Walleij 	if ((err = request_firmware(&fw_entry, name, dev)) != 0)
537fcf2d897SLinus Walleij 		return err;
538fcf2d897SLinus Walleij 
539fcf2d897SLinus Walleij 	err = -EINVAL;
540fcf2d897SLinus Walleij 	if (fw_entry->size < sizeof(struct dl_image)) {
541fcf2d897SLinus Walleij 		print_npe(KERN_ERR, npe, "incomplete firmware file\n");
542fcf2d897SLinus Walleij 		goto err;
543fcf2d897SLinus Walleij 	}
544fcf2d897SLinus Walleij 	image = (struct dl_image*)fw_entry->data;
545fcf2d897SLinus Walleij 
546fcf2d897SLinus Walleij #if DEBUG_FW
547fcf2d897SLinus Walleij 	print_npe(KERN_DEBUG, npe, "firmware: %08X %08X %08X (0x%X bytes)\n",
548fcf2d897SLinus Walleij 		  image->magic, image->id, image->size, image->size * 4);
549fcf2d897SLinus Walleij #endif
550fcf2d897SLinus Walleij 
551fcf2d897SLinus Walleij 	if (image->magic == swab32(FW_MAGIC)) { /* swapped file */
552fcf2d897SLinus Walleij 		image->id = swab32(image->id);
553fcf2d897SLinus Walleij 		image->size = swab32(image->size);
554fcf2d897SLinus Walleij 	} else if (image->magic != FW_MAGIC) {
555fcf2d897SLinus Walleij 		print_npe(KERN_ERR, npe, "bad firmware file magic: 0x%X\n",
556fcf2d897SLinus Walleij 			  image->magic);
557fcf2d897SLinus Walleij 		goto err;
558fcf2d897SLinus Walleij 	}
559fcf2d897SLinus Walleij 	if ((image->size * 4 + sizeof(struct dl_image)) != fw_entry->size) {
560fcf2d897SLinus Walleij 		print_npe(KERN_ERR, npe,
561fcf2d897SLinus Walleij 			  "inconsistent size of firmware file\n");
562fcf2d897SLinus Walleij 		goto err;
563fcf2d897SLinus Walleij 	}
564fcf2d897SLinus Walleij 	if (((image->id >> 24) & 0xF /* NPE ID */) != npe->id) {
565fcf2d897SLinus Walleij 		print_npe(KERN_ERR, npe, "firmware file NPE ID mismatch\n");
566fcf2d897SLinus Walleij 		goto err;
567fcf2d897SLinus Walleij 	}
568fcf2d897SLinus Walleij 	if (image->magic == swab32(FW_MAGIC))
569fcf2d897SLinus Walleij 		for (i = 0; i < image->size; i++)
570fcf2d897SLinus Walleij 			image->data[i] = swab32(image->data[i]);
571fcf2d897SLinus Walleij 
572fcf2d897SLinus Walleij 	if (cpu_is_ixp42x() && ((image->id >> 28) & 0xF /* device ID */)) {
573fcf2d897SLinus Walleij 		print_npe(KERN_INFO, npe, "IXP43x/IXP46x firmware ignored on "
574fcf2d897SLinus Walleij 			  "IXP42x\n");
575fcf2d897SLinus Walleij 		goto err;
576fcf2d897SLinus Walleij 	}
577fcf2d897SLinus Walleij 
578fcf2d897SLinus Walleij 	if (npe_running(npe)) {
579fcf2d897SLinus Walleij 		print_npe(KERN_INFO, npe, "unable to load firmware, NPE is "
580fcf2d897SLinus Walleij 			  "already running\n");
581fcf2d897SLinus Walleij 		err = -EBUSY;
582fcf2d897SLinus Walleij 		goto err;
583fcf2d897SLinus Walleij 	}
584fcf2d897SLinus Walleij #if 0
585fcf2d897SLinus Walleij 	npe_stop(npe);
586fcf2d897SLinus Walleij 	npe_reset(npe);
587fcf2d897SLinus Walleij #endif
588fcf2d897SLinus Walleij 
589fcf2d897SLinus Walleij 	print_npe(KERN_INFO, npe, "firmware functionality 0x%X, "
590fcf2d897SLinus Walleij 		  "revision 0x%X:%X\n", (image->id >> 16) & 0xFF,
591fcf2d897SLinus Walleij 		  (image->id >> 8) & 0xFF, image->id & 0xFF);
592fcf2d897SLinus Walleij 
593fcf2d897SLinus Walleij 	if (cpu_is_ixp42x()) {
594fcf2d897SLinus Walleij 		if (!npe->id)
595fcf2d897SLinus Walleij 			instr_size = NPE_A_42X_INSTR_SIZE;
596fcf2d897SLinus Walleij 		else
597fcf2d897SLinus Walleij 			instr_size = NPE_B_AND_C_42X_INSTR_SIZE;
598fcf2d897SLinus Walleij 		data_size = NPE_42X_DATA_SIZE;
599fcf2d897SLinus Walleij 	} else {
600fcf2d897SLinus Walleij 		instr_size = NPE_46X_INSTR_SIZE;
601fcf2d897SLinus Walleij 		data_size = NPE_46X_DATA_SIZE;
602fcf2d897SLinus Walleij 	}
603fcf2d897SLinus Walleij 
604fcf2d897SLinus Walleij 	for (blocks = 0; blocks * sizeof(struct dl_block) / 4 < image->size;
605fcf2d897SLinus Walleij 	     blocks++)
606fcf2d897SLinus Walleij 		if (image->blocks[blocks].type == FW_BLOCK_TYPE_EOF)
607fcf2d897SLinus Walleij 			break;
608fcf2d897SLinus Walleij 	if (blocks * sizeof(struct dl_block) / 4 >= image->size) {
609fcf2d897SLinus Walleij 		print_npe(KERN_INFO, npe, "firmware EOF block marker not "
610fcf2d897SLinus Walleij 			  "found\n");
611fcf2d897SLinus Walleij 		goto err;
612fcf2d897SLinus Walleij 	}
613fcf2d897SLinus Walleij 
614fcf2d897SLinus Walleij #if DEBUG_FW
615fcf2d897SLinus Walleij 	print_npe(KERN_DEBUG, npe, "%i firmware blocks found\n", blocks);
616fcf2d897SLinus Walleij #endif
617fcf2d897SLinus Walleij 
618fcf2d897SLinus Walleij 	table_end = blocks * sizeof(struct dl_block) / 4 + 1 /* EOF marker */;
619fcf2d897SLinus Walleij 	for (i = 0, blk = image->blocks; i < blocks; i++, blk++) {
620fcf2d897SLinus Walleij 		if (blk->offset > image->size - sizeof(struct dl_codeblock) / 4
621fcf2d897SLinus Walleij 		    || blk->offset < table_end) {
622fcf2d897SLinus Walleij 			print_npe(KERN_INFO, npe, "invalid offset 0x%X of "
623fcf2d897SLinus Walleij 				  "firmware block #%i\n", blk->offset, i);
624fcf2d897SLinus Walleij 			goto err;
625fcf2d897SLinus Walleij 		}
626fcf2d897SLinus Walleij 
627fcf2d897SLinus Walleij 		cb = (struct dl_codeblock*)&image->data[blk->offset];
628fcf2d897SLinus Walleij 		if (blk->type == FW_BLOCK_TYPE_INSTR) {
629fcf2d897SLinus Walleij 			if (cb->npe_addr + cb->size > instr_size)
630fcf2d897SLinus Walleij 				goto too_big;
631fcf2d897SLinus Walleij 			cmd = CMD_WR_INS_MEM;
632fcf2d897SLinus Walleij 		} else if (blk->type == FW_BLOCK_TYPE_DATA) {
633fcf2d897SLinus Walleij 			if (cb->npe_addr + cb->size > data_size)
634fcf2d897SLinus Walleij 				goto too_big;
635fcf2d897SLinus Walleij 			cmd = CMD_WR_DATA_MEM;
636fcf2d897SLinus Walleij 		} else {
637fcf2d897SLinus Walleij 			print_npe(KERN_INFO, npe, "invalid firmware block #%i "
638fcf2d897SLinus Walleij 				  "type 0x%X\n", i, blk->type);
639fcf2d897SLinus Walleij 			goto err;
640fcf2d897SLinus Walleij 		}
641fcf2d897SLinus Walleij 		if (blk->offset + sizeof(*cb) / 4 + cb->size > image->size) {
642fcf2d897SLinus Walleij 			print_npe(KERN_INFO, npe, "firmware block #%i doesn't "
643fcf2d897SLinus Walleij 				  "fit in firmware image: type %c, start 0x%X,"
644fcf2d897SLinus Walleij 				  " length 0x%X\n", i,
645fcf2d897SLinus Walleij 				  blk->type == FW_BLOCK_TYPE_INSTR ? 'I' : 'D',
646fcf2d897SLinus Walleij 				  cb->npe_addr, cb->size);
647fcf2d897SLinus Walleij 			goto err;
648fcf2d897SLinus Walleij 		}
649fcf2d897SLinus Walleij 
650fcf2d897SLinus Walleij 		for (j = 0; j < cb->size; j++)
651fcf2d897SLinus Walleij 			npe_cmd_write(npe, cb->npe_addr + j, cmd, cb->data[j]);
652fcf2d897SLinus Walleij 	}
653fcf2d897SLinus Walleij 
654fcf2d897SLinus Walleij 	npe_start(npe);
655fcf2d897SLinus Walleij 	if (!npe_running(npe))
656fcf2d897SLinus Walleij 		print_npe(KERN_ERR, npe, "unable to start\n");
657fcf2d897SLinus Walleij 	release_firmware(fw_entry);
658fcf2d897SLinus Walleij 	return 0;
659fcf2d897SLinus Walleij 
660fcf2d897SLinus Walleij too_big:
661fcf2d897SLinus Walleij 	print_npe(KERN_INFO, npe, "firmware block #%i doesn't fit in NPE "
662fcf2d897SLinus Walleij 		  "memory: type %c, start 0x%X, length 0x%X\n", i,
663fcf2d897SLinus Walleij 		  blk->type == FW_BLOCK_TYPE_INSTR ? 'I' : 'D',
664fcf2d897SLinus Walleij 		  cb->npe_addr, cb->size);
665fcf2d897SLinus Walleij err:
666fcf2d897SLinus Walleij 	release_firmware(fw_entry);
667fcf2d897SLinus Walleij 	return err;
668fcf2d897SLinus Walleij }
669fcf2d897SLinus Walleij 
670fcf2d897SLinus Walleij 
671fcf2d897SLinus Walleij struct npe *npe_request(unsigned id)
672fcf2d897SLinus Walleij {
673fcf2d897SLinus Walleij 	if (id < NPE_COUNT)
674fcf2d897SLinus Walleij 		if (npe_tab[id].valid)
675fcf2d897SLinus Walleij 			if (try_module_get(THIS_MODULE))
676fcf2d897SLinus Walleij 				return &npe_tab[id];
677fcf2d897SLinus Walleij 	return NULL;
678fcf2d897SLinus Walleij }
679fcf2d897SLinus Walleij 
680fcf2d897SLinus Walleij void npe_release(struct npe *npe)
681fcf2d897SLinus Walleij {
682fcf2d897SLinus Walleij 	module_put(THIS_MODULE);
683fcf2d897SLinus Walleij }
684fcf2d897SLinus Walleij 
685bc4d7eafSLinus Walleij static int ixp4xx_npe_probe(struct platform_device *pdev)
686fcf2d897SLinus Walleij {
687fcf2d897SLinus Walleij 	int i, found = 0;
6880b458d7bSLinus Walleij 	struct device *dev = &pdev->dev;
68976f24b4fSLinus Walleij 	struct device_node *np = dev->of_node;
6900b458d7bSLinus Walleij 	struct resource *res;
6918754a7e6SLinus Walleij 	struct regmap *rmap;
6928754a7e6SLinus Walleij 	u32 val;
6938754a7e6SLinus Walleij 
6948754a7e6SLinus Walleij 	/* This system has only one syscon, so fetch it */
6958754a7e6SLinus Walleij 	rmap = syscon_regmap_lookup_by_compatible("syscon");
6968754a7e6SLinus Walleij 	if (IS_ERR(rmap))
6978754a7e6SLinus Walleij 		return dev_err_probe(dev, PTR_ERR(rmap),
6988754a7e6SLinus Walleij 				     "failed to look up syscon\n");
699fcf2d897SLinus Walleij 
700fcf2d897SLinus Walleij 	for (i = 0; i < NPE_COUNT; i++) {
701fcf2d897SLinus Walleij 		struct npe *npe = &npe_tab[i];
7020b458d7bSLinus Walleij 
7030b458d7bSLinus Walleij 		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
7040b458d7bSLinus Walleij 		if (!res)
7050b458d7bSLinus Walleij 			return -ENODEV;
7060b458d7bSLinus Walleij 
7078754a7e6SLinus Walleij 		val = cpu_ixp4xx_features(rmap);
7088754a7e6SLinus Walleij 
7098754a7e6SLinus Walleij 		if (!(val & (IXP4XX_FEATURE_RESET_NPEA << i))) {
7108861452bSArnd Bergmann 			dev_info(dev, "NPE%d at %pR not available\n",
7118861452bSArnd Bergmann 				 i, res);
712fcf2d897SLinus Walleij 			continue; /* NPE already disabled or not present */
7130b458d7bSLinus Walleij 		}
7140b458d7bSLinus Walleij 		npe->regs = devm_ioremap_resource(dev, res);
715cd3967beSDan Carpenter 		if (IS_ERR(npe->regs))
716cd3967beSDan Carpenter 			return PTR_ERR(npe->regs);
7178754a7e6SLinus Walleij 		npe->rmap = rmap;
7180b458d7bSLinus Walleij 
7190b458d7bSLinus Walleij 		if (npe_reset(npe)) {
7208861452bSArnd Bergmann 			dev_info(dev, "NPE%d at %pR does not reset\n",
7218861452bSArnd Bergmann 				 i, res);
722fcf2d897SLinus Walleij 			continue;
723fcf2d897SLinus Walleij 		}
724fcf2d897SLinus Walleij 		npe->valid = 1;
7258861452bSArnd Bergmann 		dev_info(dev, "NPE%d at %pR registered\n", i, res);
726fcf2d897SLinus Walleij 		found++;
727fcf2d897SLinus Walleij 	}
728fcf2d897SLinus Walleij 
729fcf2d897SLinus Walleij 	if (!found)
730fcf2d897SLinus Walleij 		return -ENODEV;
73176f24b4fSLinus Walleij 
73276f24b4fSLinus Walleij 	/* Spawn crypto subdevice if using device tree */
73376f24b4fSLinus Walleij 	if (IS_ENABLED(CONFIG_OF) && np)
73476f24b4fSLinus Walleij 		devm_of_platform_populate(dev);
73576f24b4fSLinus Walleij 
736fcf2d897SLinus Walleij 	return 0;
737fcf2d897SLinus Walleij }
738fcf2d897SLinus Walleij 
739a25e745cSUwe Kleine-König static void ixp4xx_npe_remove(struct platform_device *pdev)
740fcf2d897SLinus Walleij {
741fcf2d897SLinus Walleij 	int i;
742fcf2d897SLinus Walleij 
743fcf2d897SLinus Walleij 	for (i = 0; i < NPE_COUNT; i++)
7440b458d7bSLinus Walleij 		if (npe_tab[i].regs) {
745fcf2d897SLinus Walleij 			npe_reset(&npe_tab[i]);
746fcf2d897SLinus Walleij 		}
747fcf2d897SLinus Walleij }
748fcf2d897SLinus Walleij 
749e00797f2SLinus Walleij static const struct of_device_id ixp4xx_npe_of_match[] = {
750e00797f2SLinus Walleij 	{
751e00797f2SLinus Walleij 		.compatible = "intel,ixp4xx-network-processing-engine",
752e00797f2SLinus Walleij         },
753e00797f2SLinus Walleij 	{},
754e00797f2SLinus Walleij };
755e00797f2SLinus Walleij 
756bc4d7eafSLinus Walleij static struct platform_driver ixp4xx_npe_driver = {
757bc4d7eafSLinus Walleij 	.driver = {
758bc4d7eafSLinus Walleij 		.name           = "ixp4xx-npe",
759620f83b8SLinus Walleij 		.of_match_table = ixp4xx_npe_of_match,
760bc4d7eafSLinus Walleij 	},
761bc4d7eafSLinus Walleij 	.probe = ixp4xx_npe_probe,
762a25e745cSUwe Kleine-König 	.remove_new = ixp4xx_npe_remove,
763bc4d7eafSLinus Walleij };
764bc4d7eafSLinus Walleij module_platform_driver(ixp4xx_npe_driver);
765fcf2d897SLinus Walleij 
766fcf2d897SLinus Walleij MODULE_AUTHOR("Krzysztof Halasa");
767*a862a3f7SJeff Johnson MODULE_DESCRIPTION("Intel IXP4xx Network Processor Engine driver");
768fcf2d897SLinus Walleij MODULE_LICENSE("GPL v2");
769fcf2d897SLinus Walleij MODULE_FIRMWARE(NPE_A_FIRMWARE);
770fcf2d897SLinus Walleij MODULE_FIRMWARE(NPE_B_FIRMWARE);
771fcf2d897SLinus Walleij MODULE_FIRMWARE(NPE_C_FIRMWARE);
772fcf2d897SLinus Walleij 
773fcf2d897SLinus Walleij EXPORT_SYMBOL(npe_names);
774fcf2d897SLinus Walleij EXPORT_SYMBOL(npe_running);
775fcf2d897SLinus Walleij EXPORT_SYMBOL(npe_request);
776fcf2d897SLinus Walleij EXPORT_SYMBOL(npe_release);
777fcf2d897SLinus Walleij EXPORT_SYMBOL(npe_load_firmware);
778fcf2d897SLinus Walleij EXPORT_SYMBOL(npe_send_message);
779fcf2d897SLinus Walleij EXPORT_SYMBOL(npe_recv_message);
780fcf2d897SLinus Walleij EXPORT_SYMBOL(npe_send_recv_message);
781