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