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