1164426Ssam/*-
2186352Ssam * Copyright (c) 2006-2008 Sam Leffler, Errno Consulting
3164426Ssam * All rights reserved.
4164426Ssam *
5164426Ssam * Redistribution and use in source and binary forms, with or without
6164426Ssam * modification, are permitted provided that the following conditions
7164426Ssam * are met:
8164426Ssam * 1. Redistributions of source code must retain the above copyright
9164426Ssam *    notice, this list of conditions and the following disclaimer,
10164426Ssam *    without modification.
11164426Ssam * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12164426Ssam *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13164426Ssam *    redistribution must be conditioned upon including a substantially
14164426Ssam *    similar Disclaimer requirement for further binary redistribution.
15164426Ssam *
16164426Ssam * NO WARRANTY
17164426Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18164426Ssam * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19164426Ssam * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20164426Ssam * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21164426Ssam * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22164426Ssam * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23164426Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24164426Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25164426Ssam * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26164426Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27164426Ssam * THE POSSIBILITY OF SUCH DAMAGES.
28164426Ssam */
29164426Ssam
30164426Ssam/*-
31164426Ssam * Copyright (c) 2001-2005, Intel Corporation.
32164426Ssam * All rights reserved.
33164426Ssam *
34164426Ssam * Redistribution and use in source and binary forms, with or without
35164426Ssam * modification, are permitted provided that the following conditions
36164426Ssam * are met:
37164426Ssam * 1. Redistributions of source code must retain the above copyright
38164426Ssam *    notice, this list of conditions and the following disclaimer.
39164426Ssam * 2. Redistributions in binary form must reproduce the above copyright
40164426Ssam *    notice, this list of conditions and the following disclaimer in the
41164426Ssam *    documentation and/or other materials provided with the distribution.
42164426Ssam * 3. Neither the name of the Intel Corporation nor the names of its contributors
43164426Ssam *    may be used to endorse or promote products derived from this software
44164426Ssam *    without specific prior written permission.
45164426Ssam *
46164426Ssam *
47164426Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
48164426Ssam * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49164426Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50164426Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
51164426Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52164426Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53164426Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54164426Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55164426Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56164426Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57164426Ssam * SUCH DAMAGE.
58164426Ssam*/
59164426Ssam#include <sys/cdefs.h>
60164426Ssam__FBSDID("$FreeBSD$");
61164426Ssam
62164426Ssam/*
63164426Ssam * Intel XScale Network Processing Engine (NPE) support.
64164426Ssam *
65164426Ssam * Each NPE has an ixpnpeX device associated with it that is
66164426Ssam * attached at boot.  Depending on the microcode loaded into
67164426Ssam * an NPE there may be an Ethernet interface (npeX) or some
68164426Ssam * other network interface (e.g. for ATM).  This file has support
69164426Ssam * for loading microcode images and the associated NPE CPU
70164426Ssam * manipulations (start, stop, reset).
71164426Ssam *
72164426Ssam * The code here basically replaces the npeDl and npeMh classes
73164426Ssam * in the Intel Access Library (IAL).
74164426Ssam *
75172358Scognet * NB: Microcode images are loaded with firmware(9).  To
76172358Scognet *     include microcode in a static kernel include the
77172358Scognet *     ixpnpe_fw device.  Otherwise the firmware will be
78172358Scognet *     automatically loaded from the filesystem.
79164426Ssam */
80164426Ssam#include <sys/param.h>
81164426Ssam#include <sys/systm.h>
82164426Ssam#include <sys/kernel.h>
83164426Ssam#include <sys/malloc.h>
84164426Ssam#include <sys/module.h>
85164426Ssam#include <sys/time.h>
86164426Ssam#include <sys/bus.h>
87164426Ssam#include <sys/resource.h>
88164426Ssam#include <sys/rman.h>
89164426Ssam#include <sys/sysctl.h>
90164426Ssam
91172358Scognet#include <sys/linker.h>
92172358Scognet#include <sys/firmware.h>
93172358Scognet
94164426Ssam#include <machine/bus.h>
95164426Ssam#include <machine/cpu.h>
96164426Ssam#include <machine/cpufunc.h>
97164426Ssam#include <machine/resource.h>
98164426Ssam#include <machine/intr.h>
99164426Ssam#include <arm/xscale/ixp425/ixp425reg.h>
100164426Ssam#include <arm/xscale/ixp425/ixp425var.h>
101164426Ssam
102164426Ssam#include <arm/xscale/ixp425/ixp425_npereg.h>
103164426Ssam#include <arm/xscale/ixp425/ixp425_npevar.h>
104164426Ssam
105164426Ssamstruct ixpnpe_softc {
106186352Ssam	device_t	sc_dev;
107186352Ssam	bus_space_tag_t	sc_iot;
108186352Ssam	bus_space_handle_t sc_ioh;
109186352Ssam	bus_size_t	sc_size;	/* size of mapped register window */
110186352Ssam	struct resource	*sc_irq;	/* IRQ resource */
111186352Ssam	void		*sc_ih;		/* interrupt handler */
112186352Ssam	struct mtx	sc_mtx;		/* mailbox lock */
113186352Ssam	uint32_t	sc_msg[2];	/* reply msg collected in ixpnpe_intr */
114186352Ssam	int		sc_msgwaiting;	/* sc_msg holds valid data */
115186420Ssam	int		sc_npeid;
116186420Ssam	int		sc_nrefs;	/* # of references */
117164426Ssam
118186352Ssam	int		validImage;	/* valid ucode image loaded */
119186352Ssam	int		started;	/* NPE is started */
120186352Ssam	uint8_t		functionalityId;/* ucode functionality ID */
121186352Ssam	int		insMemSize;	/* size of instruction memory */
122186352Ssam	int		dataMemSize;	/* size of data memory */
123186352Ssam	uint32_t	savedExecCount;
124186352Ssam	uint32_t	savedEcsDbgCtxtReg2;
125164426Ssam};
126186420Ssamstatic struct ixpnpe_softc *npes[NPE_MAX];
127164426Ssam
128164426Ssam#define	IX_NPEDL_NPEIMAGE_FIELD_MASK	0xff
129164426Ssam
130164426Ssam/* used to read download map from version in microcode image */
131164426Ssam#define IX_NPEDL_BLOCK_TYPE_INSTRUCTION	0x00000000
132164426Ssam#define IX_NPEDL_BLOCK_TYPE_DATA	0x00000001
133164426Ssam#define IX_NPEDL_BLOCK_TYPE_STATE	0x00000002
134164426Ssam#define IX_NPEDL_END_OF_DOWNLOAD_MAP	0x0000000F
135164426Ssam
136164426Ssam/*
137164426Ssam * masks used to extract address info from State information context
138164426Ssam * register addresses as read from microcode image
139164426Ssam */
140164426Ssam#define IX_NPEDL_MASK_STATE_ADDR_CTXT_REG         0x0000000F
141164426Ssam#define IX_NPEDL_MASK_STATE_ADDR_CTXT_NUM         0x000000F0
142164426Ssam
143164426Ssam/* LSB offset of Context Number field in State-Info Context Address */
144164426Ssam#define IX_NPEDL_OFFSET_STATE_ADDR_CTXT_NUM       4
145164426Ssam
146164426Ssam/* size (in words) of single State Information entry (ctxt reg address|data) */
147164426Ssam#define IX_NPEDL_STATE_INFO_ENTRY_SIZE	2
148164426Ssam
149164426Ssamtypedef struct {
150186352Ssam	uint32_t type;
151186352Ssam	uint32_t offset;
152164426Ssam} IxNpeDlNpeMgrDownloadMapBlockEntry;
153164426Ssam
154164426Ssamtypedef union {
155186352Ssam	IxNpeDlNpeMgrDownloadMapBlockEntry block;
156186352Ssam	uint32_t eodmMarker;
157164426Ssam} IxNpeDlNpeMgrDownloadMapEntry;
158164426Ssam
159164426Ssamtypedef struct {
160186352Ssam	/* 1st entry in the download map (there may be more than one) */
161186352Ssam	IxNpeDlNpeMgrDownloadMapEntry entry[1];
162164426Ssam} IxNpeDlNpeMgrDownloadMap;
163164426Ssam
164164426Ssam/* used to access an instruction or data block in a microcode image */
165164426Ssamtypedef struct {
166186352Ssam	uint32_t npeMemAddress;
167186352Ssam	uint32_t size;
168186352Ssam	uint32_t data[1];
169164426Ssam} IxNpeDlNpeMgrCodeBlock;
170164426Ssam
171164426Ssam/* used to access each Context Reg entry state-information block */
172164426Ssamtypedef struct {
173186352Ssam	uint32_t addressInfo;
174186352Ssam	uint32_t value;
175164426Ssam} IxNpeDlNpeMgrStateInfoCtxtRegEntry;
176164426Ssam
177164426Ssam/* used to access a state-information block in a microcode image */
178164426Ssamtypedef struct {
179186352Ssam	uint32_t size;
180186352Ssam	IxNpeDlNpeMgrStateInfoCtxtRegEntry ctxtRegEntry[1];
181164426Ssam} IxNpeDlNpeMgrStateInfoBlock;
182164426Ssam
183164426Ssamstatic int npe_debug = 0;
184164426SsamSYSCTL_INT(_debug, OID_AUTO, ixp425npe, CTLFLAG_RW, &npe_debug,
185186352Ssam	   0, "IXP4XX NPE debug msgs");
186164426SsamTUNABLE_INT("debug.ixp425npe", &npe_debug);
187164426Ssam#define	DPRINTF(dev, fmt, ...) do {					\
188164426Ssam	if (npe_debug) device_printf(dev, fmt, __VA_ARGS__);		\
189164426Ssam} while (0)
190164426Ssam#define	DPRINTFn(n, dev, fmt, ...) do {					\
191164426Ssam	if (npe_debug >= n) printf(fmt, __VA_ARGS__);			\
192164426Ssam} while (0)
193164426Ssam
194164426Ssamstatic int npe_checkbits(struct ixpnpe_softc *, uint32_t reg, uint32_t);
195164426Ssamstatic int npe_isstopped(struct ixpnpe_softc *);
196164426Ssamstatic int npe_load_ins(struct ixpnpe_softc *,
197164426Ssam		const IxNpeDlNpeMgrCodeBlock *bp, int verify);
198164426Ssamstatic int npe_load_data(struct ixpnpe_softc *,
199164426Ssam		const IxNpeDlNpeMgrCodeBlock *bp, int verify);
200164426Ssamstatic int npe_load_stateinfo(struct ixpnpe_softc *,
201164426Ssam		const IxNpeDlNpeMgrStateInfoBlock *bp, int verify);
202164426Ssamstatic int npe_load_image(struct ixpnpe_softc *,
203164426Ssam		const uint32_t *imageCodePtr, int verify);
204164426Ssamstatic int npe_cpu_reset(struct ixpnpe_softc *);
205164426Ssamstatic int npe_cpu_start(struct ixpnpe_softc *);
206164426Ssamstatic int npe_cpu_stop(struct ixpnpe_softc *);
207164426Ssamstatic void npe_cmd_issue_write(struct ixpnpe_softc *,
208164426Ssam		uint32_t cmd, uint32_t addr, uint32_t data);
209164426Ssamstatic uint32_t npe_cmd_issue_read(struct ixpnpe_softc *,
210164426Ssam		uint32_t cmd, uint32_t addr);
211164426Ssamstatic int npe_ins_write(struct ixpnpe_softc *,
212164426Ssam		uint32_t addr, uint32_t data, int verify);
213164426Ssamstatic int npe_data_write(struct ixpnpe_softc *,
214164426Ssam		uint32_t addr, uint32_t data, int verify);
215164426Ssamstatic void npe_ecs_reg_write(struct ixpnpe_softc *,
216164426Ssam		uint32_t reg, uint32_t data);
217164426Ssamstatic uint32_t npe_ecs_reg_read(struct ixpnpe_softc *, uint32_t reg);
218164426Ssamstatic void npe_issue_cmd(struct ixpnpe_softc *, uint32_t command);
219164426Ssamstatic void npe_cpu_step_save(struct ixpnpe_softc *);
220164426Ssamstatic int npe_cpu_step(struct ixpnpe_softc *, uint32_t npeInstruction,
221164426Ssam		uint32_t ctxtNum, uint32_t ldur);
222164426Ssamstatic void npe_cpu_step_restore(struct ixpnpe_softc *);
223164426Ssamstatic int npe_logical_reg_read(struct ixpnpe_softc *,
224164426Ssam		uint32_t regAddr, uint32_t regSize,
225164426Ssam		uint32_t ctxtNum, uint32_t *regVal);
226164426Ssamstatic int npe_logical_reg_write(struct ixpnpe_softc *,
227164426Ssam		uint32_t regAddr, uint32_t regVal,
228164426Ssam		uint32_t regSize, uint32_t ctxtNum, int verify);
229164426Ssamstatic int npe_physical_reg_write(struct ixpnpe_softc *,
230164426Ssam		uint32_t regAddr, uint32_t regValue, int verify);
231164426Ssamstatic int npe_ctx_reg_write(struct ixpnpe_softc *, uint32_t ctxtNum,
232164426Ssam		uint32_t ctxtReg, uint32_t ctxtRegVal, int verify);
233164426Ssam
234164426Ssamstatic void ixpnpe_intr(void *arg);
235164426Ssam
236164426Ssamstatic uint32_t
237164426Ssamnpe_reg_read(struct ixpnpe_softc *sc, bus_size_t off)
238164426Ssam{
239186352Ssam	uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh, off);
240186352Ssam	DPRINTFn(9, sc->sc_dev, "%s(0x%lx) => 0x%x\n", __func__, off, v);
241186352Ssam	return v;
242164426Ssam}
243164426Ssam
244164426Ssamstatic void
245164426Ssamnpe_reg_write(struct ixpnpe_softc *sc, bus_size_t off, uint32_t val)
246164426Ssam{
247186352Ssam	DPRINTFn(9, sc->sc_dev, "%s(0x%lx, 0x%x)\n", __func__, off, val);
248186352Ssam	bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, val);
249164426Ssam}
250164426Ssam
251164426Ssamstruct ixpnpe_softc *
252186352Ssamixpnpe_attach(device_t dev, int npeid)
253164426Ssam{
254186352Ssam	struct npeconfig {
255186352Ssam		uint32_t	base;
256186352Ssam		uint32_t	size;
257186352Ssam		int		irq;
258186352Ssam		uint32_t	ins_memsize;
259186352Ssam		uint32_t	data_memsize;
260186352Ssam	};
261186352Ssam	static const struct npeconfig npeconfigs[NPE_MAX] = {
262186352Ssam		[NPE_A] = {
263186352Ssam		    .base = IXP425_NPE_A_HWBASE,
264186352Ssam		    .size = IXP425_NPE_A_SIZE,
265186352Ssam		    .irq = IXP425_INT_NPE_A,
266186352Ssam		    .ins_memsize = IX_NPEDL_INS_MEMSIZE_WORDS_NPEA,
267186352Ssam		    .data_memsize = IX_NPEDL_DATA_MEMSIZE_WORDS_NPEA
268186352Ssam		},
269186352Ssam		[NPE_B] = {
270186352Ssam		    .base = IXP425_NPE_B_HWBASE,
271186352Ssam		    .size = IXP425_NPE_B_SIZE,
272186352Ssam		    .irq = IXP425_INT_NPE_B,
273186352Ssam		    .ins_memsize = IX_NPEDL_INS_MEMSIZE_WORDS_NPEB,
274186352Ssam		    .data_memsize = IX_NPEDL_DATA_MEMSIZE_WORDS_NPEB
275186352Ssam		},
276186352Ssam		[NPE_C] = {
277186352Ssam		    .base = IXP425_NPE_C_HWBASE,
278186352Ssam		    .size = IXP425_NPE_C_SIZE,
279186352Ssam		    .irq = IXP425_INT_NPE_C,
280186352Ssam		    .ins_memsize = IX_NPEDL_INS_MEMSIZE_WORDS_NPEC,
281186352Ssam		    .data_memsize = IX_NPEDL_DATA_MEMSIZE_WORDS_NPEC
282186352Ssam		},
283186352Ssam	};
284186352Ssam	struct ixp425_softc *sa = device_get_softc(device_get_parent(dev));
285186352Ssam	struct ixpnpe_softc *sc;
286186352Ssam	const struct npeconfig *config;
287186352Ssam	int rid;
288164426Ssam
289186352Ssam	if (npeid >= NPE_MAX) {
290186352Ssam		device_printf(dev, "%s: bad npeid %d\n", __func__, npeid);
291186352Ssam		return NULL;
292186352Ssam	}
293186420Ssam	sc = npes[npeid];
294186420Ssam	if (sc != NULL) {
295186420Ssam		sc->sc_nrefs++;
296186420Ssam		return sc;
297186420Ssam	}
298186352Ssam	config = &npeconfigs[npeid];
299164426Ssam
300186352Ssam	/* XXX M_BUS */
301186352Ssam	sc = malloc(sizeof(struct ixpnpe_softc), M_TEMP, M_WAITOK | M_ZERO);
302186352Ssam	sc->sc_dev = dev;
303186352Ssam	sc->sc_iot = sa->sc_iot;
304186352Ssam	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "npe driver", MTX_DEF);
305186420Ssam	sc->sc_npeid = npeid;
306186420Ssam	sc->sc_nrefs = 1;
307164426Ssam
308186352Ssam	sc->sc_size = config->size;
309194325Ssam	if (cpu_is_ixp42x()) {
310194325Ssam		/* NB: instruction/data memory sizes are NPE-dependent */
311194325Ssam		sc->insMemSize = config->ins_memsize;
312194325Ssam		sc->dataMemSize = config->data_memsize;
313194325Ssam	} else {
314194325Ssam		sc->insMemSize = IXP46X_NPEDL_INS_MEMSIZE_WORDS;
315194325Ssam		sc->dataMemSize = IXP46X_NPEDL_DATA_MEMSIZE_WORDS;
316194325Ssam	}
317164426Ssam
318186352Ssam	if (bus_space_map(sc->sc_iot, config->base, sc->sc_size, 0, &sc->sc_ioh))
319186352Ssam		panic("%s: Cannot map registers", device_get_name(dev));
320164426Ssam
321186352Ssam	/*
322186352Ssam	 * Setup IRQ and handler for NPE message support.
323186352Ssam	 */
324186352Ssam	rid = 0;
325186352Ssam	sc->sc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
326186352Ssam	    config->irq, config->irq, 1, RF_ACTIVE);
327186352Ssam	if (sc->sc_irq == NULL)
328186352Ssam		panic("%s: Unable to allocate irq %u", device_get_name(dev),
329186352Ssam		    config->irq);
330186352Ssam	/* XXX could be a source of entropy */
331186352Ssam	bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE,
332186352Ssam	    NULL, ixpnpe_intr, sc, &sc->sc_ih);
333186352Ssam	/*
334186352Ssam	 * Enable output fifo interrupts (NB: must also set OFIFO Write Enable)
335186352Ssam	 */
336186352Ssam	npe_reg_write(sc, IX_NPECTL,
337186352Ssam	    npe_reg_read(sc, IX_NPECTL) | (IX_NPECTL_OFE | IX_NPECTL_OFWE));
338164426Ssam
339186420Ssam	npes[npeid] = sc;
340186420Ssam
341186352Ssam	return sc;
342164426Ssam}
343164426Ssam
344164426Ssamvoid
345164426Ssamixpnpe_detach(struct ixpnpe_softc *sc)
346164426Ssam{
347186420Ssam	if (--sc->sc_nrefs == 0) {
348186420Ssam		npes[sc->sc_npeid] = NULL;
349164426Ssam
350186420Ssam		/* disable output fifo interrupts */
351186420Ssam		npe_reg_write(sc, IX_NPECTL,
352186420Ssam		    npe_reg_read(sc, IX_NPECTL) &~ (IX_NPECTL_OFE | IX_NPECTL_OFWE));
353186420Ssam
354186420Ssam		bus_teardown_intr(sc->sc_dev, sc->sc_irq, sc->sc_ih);
355186420Ssam		bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size);
356186420Ssam		mtx_destroy(&sc->sc_mtx);
357186420Ssam		free(sc, M_TEMP);
358186420Ssam	}
359164426Ssam}
360164426Ssam
361164426Ssamint
362164426Ssamixpnpe_stopandreset(struct ixpnpe_softc *sc)
363164426Ssam{
364186352Ssam	int error;
365164426Ssam
366186352Ssam	mtx_lock(&sc->sc_mtx);
367186352Ssam	error = npe_cpu_stop(sc);		/* stop NPE */
368186352Ssam	if (error == 0)
369186352Ssam		error = npe_cpu_reset(sc);	/* reset it */
370186352Ssam	if (error == 0)
371186352Ssam		sc->started = 0;		/* mark stopped */
372186352Ssam	mtx_unlock(&sc->sc_mtx);
373164426Ssam
374186352Ssam	DPRINTF(sc->sc_dev, "%s: error %d\n", __func__, error);
375186352Ssam	return error;
376164426Ssam}
377164426Ssam
378164426Ssamstatic int
379164426Ssamixpnpe_start_locked(struct ixpnpe_softc *sc)
380164426Ssam{
381186352Ssam	int error;
382164426Ssam
383186352Ssam	if (!sc->started) {
384186352Ssam		error = npe_cpu_start(sc);
385186352Ssam		if (error == 0)
386186420Ssam			sc->started = 1;
387186352Ssam	} else
388186352Ssam		error = 0;
389164426Ssam
390186352Ssam	DPRINTF(sc->sc_dev, "%s: error %d\n", __func__, error);
391186352Ssam	return error;
392164426Ssam}
393164426Ssam
394164426Ssamint
395164426Ssamixpnpe_start(struct ixpnpe_softc *sc)
396164426Ssam{
397164426Ssam	int ret;
398164426Ssam
399164426Ssam	mtx_lock(&sc->sc_mtx);
400164426Ssam	ret = ixpnpe_start_locked(sc);
401164426Ssam	mtx_unlock(&sc->sc_mtx);
402164426Ssam	return (ret);
403164426Ssam}
404164426Ssam
405164426Ssamint
406164426Ssamixpnpe_stop(struct ixpnpe_softc *sc)
407164426Ssam{
408186352Ssam	int error;
409164426Ssam
410186352Ssam	mtx_lock(&sc->sc_mtx);
411186352Ssam	error = npe_cpu_stop(sc);
412186352Ssam	if (error == 0)
413186352Ssam		sc->started = 0;
414186352Ssam	mtx_unlock(&sc->sc_mtx);
415164426Ssam
416186352Ssam	DPRINTF(sc->sc_dev, "%s: error %d\n", __func__, error);
417186352Ssam	return error;
418164426Ssam}
419164426Ssam
420164426Ssam/*
421164426Ssam * Indicates the start of an NPE Image, in new NPE Image Library format.
422164426Ssam * 2 consecutive occurances indicates the end of the NPE Image Library
423164426Ssam */
424164426Ssam#define NPE_IMAGE_MARKER 0xfeedf00d
425164426Ssam
426164426Ssam/*
427164426Ssam * NPE Image Header definition, used in new NPE Image Library format
428164426Ssam */
429164426Ssamtypedef struct {
430186352Ssam	uint32_t marker;
431186352Ssam	uint32_t id;
432186352Ssam	uint32_t size;
433164426Ssam} IxNpeDlImageMgrImageHeader;
434164426Ssam
435164426Ssamstatic int
436164426Ssamnpe_findimage(struct ixpnpe_softc *sc,
437164426Ssam    const uint32_t *imageLibrary, uint32_t imageId,
438164426Ssam    const uint32_t **imagePtr, uint32_t *imageSize)
439164426Ssam{
440186352Ssam	const IxNpeDlImageMgrImageHeader *image;
441186352Ssam	uint32_t offset = 0;
442164426Ssam
443186352Ssam	while (imageLibrary[offset] == NPE_IMAGE_MARKER) {
444186352Ssam		image = (const IxNpeDlImageMgrImageHeader *)
445186352Ssam		    &imageLibrary[offset];
446186352Ssam		offset += sizeof(IxNpeDlImageMgrImageHeader)/sizeof(uint32_t);
447186352Ssam
448186352Ssam		DPRINTF(sc->sc_dev, "%s: off %u mark 0x%x id 0x%x size %u\n",
449186352Ssam		    __func__, offset, image->marker, image->id, image->size);
450186352Ssam		if (image->id == imageId) {
451186352Ssam			*imagePtr = imageLibrary + offset;
452186352Ssam			*imageSize = image->size;
453186352Ssam			return 0;
454186352Ssam		}
455186352Ssam		/* 2 consecutive NPE_IMAGE_MARKER's indicates end of library */
456186352Ssam		if (image->id == NPE_IMAGE_MARKER) {
457186352Ssam			DPRINTF(sc->sc_dev, "imageId 0x%08x not found in "
458186352Ssam			    "image library header\n", imageId);
459186352Ssam			/* reached end of library, image not found */
460186352Ssam			return ESRCH;
461186352Ssam		}
462186352Ssam		offset += image->size;
463186352Ssam	}
464186352Ssam	return ESRCH;
465164426Ssam}
466164426Ssam
467186420Ssamstatic int
468186420Ssamixpnpe_load_firmware(struct ixpnpe_softc *sc, const char *imageName,
469186420Ssam    uint32_t imageId)
470164426Ssam{
471186352Ssam	static const char *devname[4] =
472186352Ssam	     { "IXP425", "IXP435/IXP465", "DeviceID#2", "DeviceID#3" };
473186352Ssam	uint32_t imageSize;
474186352Ssam	const uint32_t *imageCodePtr;
475186352Ssam	const struct firmware *fw;
476186352Ssam	int error;
477164426Ssam
478186352Ssam	DPRINTF(sc->sc_dev, "load %s, imageId 0x%08x\n", imageName, imageId);
479164426Ssam
480164426Ssam#if 0
481186352Ssam	IxFeatureCtrlDeviceId devid = IX_NPEDL_DEVICEID_FROM_IMAGEID_GET(imageId);
482186352Ssam	/*
483186352Ssam	 * Checking if image being loaded is meant for device that is running.
484186352Ssam	 * Image is forward compatible. i.e Image built for IXP42X should run
485186352Ssam	 * on IXP46X but not vice versa.
486186352Ssam	 */
487186352Ssam	if (devid > (ixFeatureCtrlDeviceRead() & IX_FEATURE_CTRL_DEVICE_TYPE_MASK))
488186352Ssam	    return EINVAL;
489164426Ssam#endif
490186352Ssam	error = ixpnpe_stopandreset(sc);		/* stop and reset the NPE */
491186352Ssam	if (error != 0)
492186352Ssam		return error;
493164426Ssam
494186352Ssam	fw = firmware_get(imageName);
495186352Ssam	if (fw == NULL)
496186352Ssam		return ENOENT;
497172358Scognet
498186352Ssam	/* Locate desired image in files w/ combined images */
499186352Ssam	error = npe_findimage(sc, fw->data, imageId, &imageCodePtr, &imageSize);
500186352Ssam	if (error != 0)
501186352Ssam		goto done;
502164426Ssam
503186352Ssam	device_printf(sc->sc_dev,
504186352Ssam	    "load fw image %s.NPE-%c Func 0x%x Rev %u.%u\n",
505186352Ssam	    devname[NPEIMAGE_DEVID(imageId)], 'A' + NPEIMAGE_NPEID(imageId),
506186352Ssam	    NPEIMAGE_FUNCID(imageId), NPEIMAGE_MAJOR(imageId),
507186352Ssam	    NPEIMAGE_MINOR(imageId));
508186352Ssam
509186352Ssam	/*
510186352Ssam	 * If download was successful, store image Id in list of
511186352Ssam	 * currently loaded images. If a critical error occured
512186352Ssam	 * during download, record that the NPE has an invalid image
513186352Ssam	 */
514186352Ssam	mtx_lock(&sc->sc_mtx);
515186352Ssam	error = npe_load_image(sc, imageCodePtr, 1 /*VERIFY*/);
516186352Ssam	if (error == 0) {
517186352Ssam		sc->validImage = 1;
518186352Ssam		error = ixpnpe_start_locked(sc);
519186352Ssam	} else {
520186352Ssam		sc->validImage = 0;
521186352Ssam	}
522186352Ssam	sc->functionalityId = IX_NPEDL_FUNCTIONID_FROM_IMAGEID_GET(imageId);
523186352Ssam	mtx_unlock(&sc->sc_mtx);
524164426Ssamdone:
525186352Ssam	firmware_put(fw, FIRMWARE_UNLOAD);
526186352Ssam	DPRINTF(sc->sc_dev, "%s: error %d\n", __func__, error);
527186352Ssam	return error;
528164426Ssam}
529164426Ssam
530186420Ssamstatic int
531186420Ssamoverride_imageid(device_t dev, const char *resname, uint32_t *val)
532186420Ssam{
533186420Ssam	int unit = device_get_unit(dev);
534186420Ssam	int resval;
535186420Ssam
536186420Ssam	if (resource_int_value("npe", unit, resname, &resval) != 0)
537186420Ssam		return 0;
538186420Ssam	/* XXX validate */
539186420Ssam	if (bootverbose)
540186420Ssam		device_printf(dev, "using npe.%d.%s=0x%x override\n",
541186420Ssam		    unit, resname, resval);
542186420Ssam	*val = resval;
543186420Ssam	return 1;
544186420Ssam}
545186420Ssam
546164426Ssamint
547186420Ssamixpnpe_init(struct ixpnpe_softc *sc)
548186420Ssam{
549186420Ssam	static const uint32_t npeconfig[NPE_MAX] = {
550186420Ssam		[NPE_A] = IXP425_NPE_A_IMAGEID,
551186420Ssam		[NPE_B] = IXP425_NPE_B_IMAGEID,
552186420Ssam		[NPE_C] = IXP425_NPE_C_IMAGEID,
553186420Ssam	};
554186420Ssam	uint32_t imageid, msg[2];
555186420Ssam	int error;
556186420Ssam
557186420Ssam	if (sc->started)
558186420Ssam		return 0;
559186420Ssam	/*
560186420Ssam	 * Load NPE firmware and start it running.  We assume
561186420Ssam	 * that minor version bumps remain compatible so probe
562186420Ssam	 * the firmware image starting with the expected version
563186420Ssam	 * and then bump the minor version up to the max.
564186420Ssam	 */
565186420Ssam	if (!override_imageid(sc->sc_dev, "imageid", &imageid))
566186420Ssam		imageid = npeconfig[sc->sc_npeid];
567186420Ssam	for (;;) {
568186420Ssam		error = ixpnpe_load_firmware(sc, "npe_fw", imageid);
569186420Ssam		if (error == 0)
570186420Ssam			break;
571186420Ssam		/*
572186420Ssam		 * ESRCH is returned when the requested image
573186420Ssam		 * is not present
574186420Ssam		 */
575186420Ssam		if (error != ESRCH) {
576186420Ssam			device_printf(sc->sc_dev,
577186420Ssam			    "cannot init NPE (error %d)\n", error);
578186420Ssam			return error;
579186420Ssam		}
580186420Ssam		/* bump the minor version up to the max possible */
581186420Ssam		if (NPEIMAGE_MINOR(imageid) == 0xff) {
582186420Ssam			device_printf(sc->sc_dev, "cannot locate firmware "
583186420Ssam			    "(imageid 0x%08x)\n", imageid);
584186420Ssam			return error;
585186420Ssam		}
586186420Ssam		imageid++;
587186420Ssam	}
588186420Ssam	/* NB: firmware should respond with a status msg */
589186420Ssam	if (ixpnpe_recvmsg_sync(sc, msg) != 0) {
590186420Ssam		device_printf(sc->sc_dev,
591186420Ssam		    "firmware did not respond as expected\n");
592186420Ssam		return EIO;
593186420Ssam	}
594186420Ssam	return 0;
595186420Ssam}
596186420Ssam
597186420Ssamint
598164426Ssamixpnpe_getfunctionality(struct ixpnpe_softc *sc)
599164426Ssam{
600186352Ssam	return (sc->validImage ? sc->functionalityId : 0);
601164426Ssam}
602164426Ssam
603164426Ssamstatic int
604164426Ssamnpe_checkbits(struct ixpnpe_softc *sc, uint32_t reg, uint32_t expectedBitsSet)
605164426Ssam{
606186352Ssam	uint32_t val;
607164426Ssam
608186352Ssam	val = npe_reg_read(sc, reg);
609186352Ssam	DPRINTFn(5, sc->sc_dev, "%s(0x%x, 0x%x) => 0x%x (%u)\n",
610186352Ssam	    __func__, reg, expectedBitsSet, val,
611186352Ssam	    (val & expectedBitsSet) == expectedBitsSet);
612186352Ssam	return ((val & expectedBitsSet) == expectedBitsSet);
613164426Ssam}
614164426Ssam
615164426Ssamstatic int
616164426Ssamnpe_isstopped(struct ixpnpe_softc *sc)
617164426Ssam{
618186352Ssam	return npe_checkbits(sc,
619186352Ssam	    IX_NPEDL_REG_OFFSET_EXCTL, IX_NPEDL_EXCTL_STATUS_STOP);
620164426Ssam}
621164426Ssam
622164426Ssamstatic int
623164426Ssamnpe_load_ins(struct ixpnpe_softc *sc,
624164426Ssam    const IxNpeDlNpeMgrCodeBlock *bp, int verify)
625164426Ssam{
626186352Ssam	uint32_t npeMemAddress;
627186352Ssam	int i, blockSize;
628164426Ssam
629186352Ssam	npeMemAddress = bp->npeMemAddress;
630186352Ssam	blockSize = bp->size;		/* NB: instruction/data count */
631186352Ssam	if (npeMemAddress + blockSize > sc->insMemSize) {
632186352Ssam		device_printf(sc->sc_dev,
633186352Ssam		    "Block size %u too big for NPE memory\n", blockSize);
634186352Ssam		return EINVAL;	/* XXX */
635164426Ssam	}
636186352Ssam	for (i = 0; i < blockSize; i++, npeMemAddress++) {
637186352Ssam		if (npe_ins_write(sc, npeMemAddress, bp->data[i], verify) != 0) {
638186352Ssam			device_printf(sc->sc_dev,
639186352Ssam			    "NPE instruction write failed");
640186352Ssam			return EIO;
641186352Ssam		}
642186352Ssam	}
643186352Ssam	return 0;
644164426Ssam}
645164426Ssam
646164426Ssamstatic int
647164426Ssamnpe_load_data(struct ixpnpe_softc *sc,
648164426Ssam    const IxNpeDlNpeMgrCodeBlock *bp, int verify)
649164426Ssam{
650186352Ssam	uint32_t npeMemAddress;
651186352Ssam	int i, blockSize;
652164426Ssam
653186352Ssam	npeMemAddress = bp->npeMemAddress;
654186352Ssam	blockSize = bp->size;		/* NB: instruction/data count */
655186352Ssam	if (npeMemAddress + blockSize > sc->dataMemSize) {
656186352Ssam		device_printf(sc->sc_dev,
657186352Ssam		    "Block size %u too big for NPE memory\n", blockSize);
658186352Ssam		return EINVAL;
659164426Ssam	}
660186352Ssam	for (i = 0; i < blockSize; i++, npeMemAddress++) {
661186352Ssam		if (npe_data_write(sc, npeMemAddress, bp->data[i], verify) != 0) {
662186352Ssam			device_printf(sc->sc_dev, "NPE data write failed\n");
663186352Ssam			return EIO;
664186352Ssam		}
665186352Ssam	}
666186352Ssam	return 0;
667164426Ssam}
668164426Ssam
669164426Ssamstatic int
670164426Ssamnpe_load_stateinfo(struct ixpnpe_softc *sc,
671164426Ssam    const IxNpeDlNpeMgrStateInfoBlock *bp, int verify)
672164426Ssam{
673186352Ssam	int i, nentries, error;
674186352Ssam
675186352Ssam	npe_cpu_step_save(sc);
676164426Ssam
677186352Ssam	/* for each state-info context register entry in block */
678186352Ssam	nentries = bp->size / IX_NPEDL_STATE_INFO_ENTRY_SIZE;
679186352Ssam	error = 0;
680186352Ssam	for (i = 0; i < nentries; i++) {
681186352Ssam		/* each state-info entry is 2 words (address, value) */
682186352Ssam		uint32_t regVal = bp->ctxtRegEntry[i].value;
683186352Ssam		uint32_t addrInfo = bp->ctxtRegEntry[i].addressInfo;
684164426Ssam
685186352Ssam		uint32_t reg = (addrInfo & IX_NPEDL_MASK_STATE_ADDR_CTXT_REG);
686186352Ssam		uint32_t cNum = (addrInfo & IX_NPEDL_MASK_STATE_ADDR_CTXT_NUM) >>
687186352Ssam		    IX_NPEDL_OFFSET_STATE_ADDR_CTXT_NUM;
688186352Ssam
689186352Ssam		/* error-check Context Register No. and Context Number values */
690186352Ssam		if (!(0 <= reg && reg < IX_NPEDL_CTXT_REG_MAX)) {
691186352Ssam			device_printf(sc->sc_dev,
692186352Ssam			    "invalid Context Register %u\n", reg);
693186352Ssam			error = EINVAL;
694186352Ssam			break;
695186352Ssam		}
696186352Ssam		if (!(0 <= cNum && cNum < IX_NPEDL_CTXT_NUM_MAX)) {
697186352Ssam			device_printf(sc->sc_dev,
698186352Ssam			    "invalid Context Number %u\n", cNum);
699186352Ssam			error = EINVAL;
700186352Ssam			break;
701186352Ssam		}
702186352Ssam		/* NOTE that there is no STEVT register for Context 0 */
703186352Ssam		if (cNum == 0 && reg == IX_NPEDL_CTXT_REG_STEVT) {
704186352Ssam			device_printf(sc->sc_dev,
705186352Ssam			    "no STEVT for Context 0\n");
706186352Ssam			error = EINVAL;
707186352Ssam			break;
708186352Ssam		}
709164426Ssam
710186352Ssam		if (npe_ctx_reg_write(sc, cNum, reg, regVal, verify) != 0) {
711186352Ssam			device_printf(sc->sc_dev,
712186352Ssam			    "write of state-info to NPE failed\n");
713186352Ssam			error = EIO;
714186352Ssam			break;
715186352Ssam		}
716164426Ssam	}
717164426Ssam
718186352Ssam	npe_cpu_step_restore(sc);
719186352Ssam	return error;
720164426Ssam}
721164426Ssam
722164426Ssamstatic int
723164426Ssamnpe_load_image(struct ixpnpe_softc *sc,
724164426Ssam    const uint32_t *imageCodePtr, int verify)
725164426Ssam{
726164426Ssam#define	EOM(marker)	((marker) == IX_NPEDL_END_OF_DOWNLOAD_MAP)
727186352Ssam	const IxNpeDlNpeMgrDownloadMap *downloadMap;
728186352Ssam	int i, error;
729164426Ssam
730186352Ssam	if (!npe_isstopped(sc)) {		/* verify NPE is stopped */
731186352Ssam		device_printf(sc->sc_dev,
732186352Ssam		    "cannot load image, NPE not stopped\n");
733186352Ssam		return EIO;
734186352Ssam	}
735164426Ssam
736186352Ssam	/*
737186352Ssam	 * Read Download Map, checking each block type and calling
738186352Ssam	 * appropriate function to perform download
739186352Ssam	 */
740186352Ssam	error = 0;
741186352Ssam	downloadMap = (const IxNpeDlNpeMgrDownloadMap *) imageCodePtr;
742186352Ssam	for (i = 0; !EOM(downloadMap->entry[i].eodmMarker); i++) {
743186352Ssam		/* calculate pointer to block to be downloaded */
744186352Ssam		const uint32_t *bp = imageCodePtr +
745186352Ssam		    downloadMap->entry[i].block.offset;
746186352Ssam		switch (downloadMap->entry[i].block.type) {
747186352Ssam		case IX_NPEDL_BLOCK_TYPE_INSTRUCTION:
748186352Ssam			error = npe_load_ins(sc,
749186352Ssam			    (const IxNpeDlNpeMgrCodeBlock *) bp, verify);
750186352Ssam			DPRINTF(sc->sc_dev, "%s: inst, error %d\n",
751186352Ssam			    __func__, error);
752186352Ssam			break;
753186352Ssam		case IX_NPEDL_BLOCK_TYPE_DATA:
754186352Ssam			error = npe_load_data(sc,
755186352Ssam			    (const IxNpeDlNpeMgrCodeBlock *) bp, verify);
756186352Ssam			DPRINTF(sc->sc_dev, "%s: data, error %d\n",
757186352Ssam			    __func__, error);
758186352Ssam			break;
759186352Ssam		case IX_NPEDL_BLOCK_TYPE_STATE:
760186352Ssam		    error = npe_load_stateinfo(sc,
761186352Ssam			(const IxNpeDlNpeMgrStateInfoBlock *) bp, verify);
762186352Ssam			DPRINTF(sc->sc_dev, "%s: state, error %d\n",
763186352Ssam			    __func__, error);
764186352Ssam			break;
765186352Ssam		default:
766186352Ssam			device_printf(sc->sc_dev,
767186352Ssam			    "unknown block type 0x%x in download map\n",
768186352Ssam			    downloadMap->entry[i].block.type);
769186352Ssam			error = EIO;		/* XXX */
770186352Ssam			break;
771186352Ssam		}
772186352Ssam		if (error != 0)
773186352Ssam			break;
774164426Ssam	}
775186352Ssam	return error;
776164426Ssam#undef EOM
777164426Ssam}
778164426Ssam
779164426Ssam/* contains Reset values for Context Store Registers  */
780164426Ssamstatic const struct {
781186352Ssam	uint32_t regAddr;
782186352Ssam	uint32_t regResetVal;
783164426Ssam} ixNpeDlEcsRegResetValues[] = {
784186352Ssam	{ IX_NPEDL_ECS_BG_CTXT_REG_0,    IX_NPEDL_ECS_BG_CTXT_REG_0_RESET },
785186352Ssam	{ IX_NPEDL_ECS_BG_CTXT_REG_1,    IX_NPEDL_ECS_BG_CTXT_REG_1_RESET },
786186352Ssam	{ IX_NPEDL_ECS_BG_CTXT_REG_2,    IX_NPEDL_ECS_BG_CTXT_REG_2_RESET },
787186352Ssam	{ IX_NPEDL_ECS_PRI_1_CTXT_REG_0, IX_NPEDL_ECS_PRI_1_CTXT_REG_0_RESET },
788186352Ssam	{ IX_NPEDL_ECS_PRI_1_CTXT_REG_1, IX_NPEDL_ECS_PRI_1_CTXT_REG_1_RESET },
789186352Ssam	{ IX_NPEDL_ECS_PRI_1_CTXT_REG_2, IX_NPEDL_ECS_PRI_1_CTXT_REG_2_RESET },
790186352Ssam	{ IX_NPEDL_ECS_PRI_2_CTXT_REG_0, IX_NPEDL_ECS_PRI_2_CTXT_REG_0_RESET },
791186352Ssam	{ IX_NPEDL_ECS_PRI_2_CTXT_REG_1, IX_NPEDL_ECS_PRI_2_CTXT_REG_1_RESET },
792186352Ssam	{ IX_NPEDL_ECS_PRI_2_CTXT_REG_2, IX_NPEDL_ECS_PRI_2_CTXT_REG_2_RESET },
793186352Ssam	{ IX_NPEDL_ECS_DBG_CTXT_REG_0,   IX_NPEDL_ECS_DBG_CTXT_REG_0_RESET },
794186352Ssam	{ IX_NPEDL_ECS_DBG_CTXT_REG_1,   IX_NPEDL_ECS_DBG_CTXT_REG_1_RESET },
795186352Ssam	{ IX_NPEDL_ECS_DBG_CTXT_REG_2,   IX_NPEDL_ECS_DBG_CTXT_REG_2_RESET },
796186352Ssam	{ IX_NPEDL_ECS_INSTRUCT_REG,     IX_NPEDL_ECS_INSTRUCT_REG_RESET }
797164426Ssam};
798164426Ssam
799164426Ssam/* contains Reset values for Context Store Registers  */
800164426Ssamstatic const uint32_t ixNpeDlCtxtRegResetValues[] = {
801186352Ssam	IX_NPEDL_CTXT_REG_RESET_STEVT,
802186352Ssam	IX_NPEDL_CTXT_REG_RESET_STARTPC,
803186352Ssam	IX_NPEDL_CTXT_REG_RESET_REGMAP,
804186352Ssam	IX_NPEDL_CTXT_REG_RESET_CINDEX,
805164426Ssam};
806164426Ssam
807164426Ssam#define	IX_NPEDL_PARITY_BIT_MASK	0x3F00FFFF
808164426Ssam#define	IX_NPEDL_CONFIG_CTRL_REG_MASK	0x3F3FFFFF
809164426Ssam
810194378Ssam#if 0
811194378Ssam/*
812194378Ssam * Reset the NPE and its coprocessor using the
813194378Ssam * fuse bits in the feature control register.
814194378Ssam */
815194378Ssamstatic void
816194378Ssamnpe_reset(int npeid)
817194378Ssam{
818194378Ssam	uint32_t mask = EXP_FCTRL_NPEA << npeid;
819194378Ssam	uint32_t v;
820194378Ssam
821194378Ssam	v = ixp4xx_read_feature_bits();
822194378Ssam	ixp4xx_write_feature_bits(v &~ mask);
823194378Ssam	/* un-fuse and un-reset the NPE & coprocessor */
824194378Ssam	ixp4xx_write_feature_bits(v | mask);
825194378Ssam}
826194378Ssam#endif
827194378Ssam
828164426Ssamstatic int
829164426Ssamnpe_cpu_reset(struct ixpnpe_softc *sc)
830164426Ssam{
831164426Ssam#define	N(a)	(sizeof(a) / sizeof(a[0]))
832186352Ssam	uint32_t ctxtReg; /* identifies Context Store reg (0-3) */
833186352Ssam	uint32_t regAddr;
834186352Ssam	uint32_t regVal;
835186352Ssam	uint32_t ixNpeConfigCtrlRegVal;
836186352Ssam	int i, error = 0;
837186352Ssam
838186352Ssam	/* pre-store the NPE Config Control Register Value */
839186352Ssam	ixNpeConfigCtrlRegVal = npe_reg_read(sc, IX_NPEDL_REG_OFFSET_CTL);
840186352Ssam	ixNpeConfigCtrlRegVal |= 0x3F000000;
841164426Ssam
842186352Ssam	/* disable the parity interrupt */
843186352Ssam	npe_reg_write(sc, IX_NPEDL_REG_OFFSET_CTL,
844186352Ssam	    (ixNpeConfigCtrlRegVal & IX_NPEDL_PARITY_BIT_MASK));
845186352Ssam	DPRINTFn(2, sc->sc_dev, "%s: dis parity int, CTL => 0x%x\n",
846186352Ssam	    __func__, ixNpeConfigCtrlRegVal & IX_NPEDL_PARITY_BIT_MASK);
847186352Ssam
848186352Ssam	npe_cpu_step_save(sc);
849164426Ssam
850164426Ssam	/*
851186352Ssam	 * Clear the FIFOs.
852164426Ssam	 */
853186352Ssam	while (npe_checkbits(sc,
854186352Ssam	      IX_NPEDL_REG_OFFSET_WFIFO, IX_NPEDL_MASK_WFIFO_VALID)) {
855186352Ssam		/* read from the Watch-point FIFO until empty */
856186352Ssam		(void) npe_reg_read(sc, IX_NPEDL_REG_OFFSET_WFIFO);
857164426Ssam	}
858164426Ssam
859186352Ssam	while (npe_checkbits(sc,
860186352Ssam	      IX_NPEDL_REG_OFFSET_STAT, IX_NPEDL_MASK_STAT_OFNE)) {
861186352Ssam		/* read from the outFIFO until empty */
862186352Ssam		(void) npe_reg_read(sc, IX_NPEDL_REG_OFFSET_FIFO);
863186352Ssam	}
864186352Ssam
865186352Ssam	while (npe_checkbits(sc,
866186352Ssam	      IX_NPEDL_REG_OFFSET_STAT, IX_NPEDL_MASK_STAT_IFNE)) {
867186352Ssam		/*
868186352Ssam		 * Step execution of the NPE intruction to read inFIFO using
869186352Ssam		 * the Debug Executing Context stack.
870186352Ssam		 */
871186352Ssam		error = npe_cpu_step(sc, IX_NPEDL_INSTR_RD_FIFO, 0, 0);
872186352Ssam		if (error != 0) {
873186352Ssam			DPRINTF(sc->sc_dev, "%s: cannot step (1), error %u\n",
874186352Ssam			    __func__, error);
875186352Ssam			npe_cpu_step_restore(sc);
876186352Ssam			return error;
877186352Ssam		}
878186352Ssam	}
879186352Ssam
880186352Ssam	/*
881186352Ssam	 * Reset the mailbox reg
882186352Ssam	 */
883186352Ssam	/* ...from XScale side */
884186352Ssam	npe_reg_write(sc, IX_NPEDL_REG_OFFSET_MBST, IX_NPEDL_REG_RESET_MBST);
885186352Ssam	/* ...from NPE side */
886186352Ssam	error = npe_cpu_step(sc, IX_NPEDL_INSTR_RESET_MBOX, 0, 0);
887164426Ssam	if (error != 0) {
888186352Ssam		DPRINTF(sc->sc_dev, "%s: cannot step (2), error %u\n",
889186352Ssam		    __func__, error);
890186352Ssam		npe_cpu_step_restore(sc);
891186352Ssam		return error;
892164426Ssam	}
893164426Ssam
894186352Ssam	/*
895186352Ssam	 * Reset the physical registers in the NPE register file:
896186352Ssam	 * Note: no need to save/restore REGMAP for Context 0 here
897186352Ssam	 * since all Context Store regs are reset in subsequent code.
898186352Ssam	 */
899186352Ssam	for (regAddr = 0;
900186352Ssam	     regAddr < IX_NPEDL_TOTAL_NUM_PHYS_REG && error == 0;
901186352Ssam	     regAddr++) {
902186352Ssam		/* for each physical register in the NPE reg file, write 0 : */
903186352Ssam		error = npe_physical_reg_write(sc, regAddr, 0, TRUE);
904164426Ssam		if (error != 0) {
905186352Ssam			DPRINTF(sc->sc_dev, "%s: cannot write phy reg,"
906186352Ssam			    "error %u\n", __func__, error);
907186352Ssam			npe_cpu_step_restore(sc);
908186352Ssam			return error;		/* abort reset */
909164426Ssam		}
910164426Ssam	}
911164426Ssam
912186352Ssam	/*
913186352Ssam	 * Reset the context store:
914186352Ssam	 */
915186352Ssam	for (i = IX_NPEDL_CTXT_NUM_MIN; i <= IX_NPEDL_CTXT_NUM_MAX; i++) {
916186352Ssam		/* set each context's Context Store registers to reset values */
917186352Ssam		for (ctxtReg = 0; ctxtReg < IX_NPEDL_CTXT_REG_MAX; ctxtReg++) {
918186352Ssam			/* NOTE that there is no STEVT register for Context 0 */
919186352Ssam			if (i == 0 && ctxtReg == IX_NPEDL_CTXT_REG_STEVT)
920186352Ssam				continue;
921186352Ssam			regVal = ixNpeDlCtxtRegResetValues[ctxtReg];
922186352Ssam			error = npe_ctx_reg_write(sc, i, ctxtReg,
923186352Ssam			    regVal, TRUE);
924186352Ssam			if (error != 0) {
925186352Ssam				DPRINTF(sc->sc_dev, "%s: cannot write ctx reg,"
926186352Ssam				    "error %u\n", __func__, error);
927186352Ssam				npe_cpu_step_restore(sc);
928186352Ssam				return error;	 /* abort reset */
929186352Ssam			}
930186352Ssam		}
931186352Ssam	}
932164426Ssam
933186352Ssam	npe_cpu_step_restore(sc);
934164426Ssam
935186352Ssam	/* write Reset values to Execution Context Stack registers */
936186352Ssam	for (i = 0; i < N(ixNpeDlEcsRegResetValues); i++)
937186352Ssam		npe_ecs_reg_write(sc,
938186352Ssam		    ixNpeDlEcsRegResetValues[i].regAddr,
939186352Ssam		    ixNpeDlEcsRegResetValues[i].regResetVal);
940164426Ssam
941186352Ssam	/* clear the profile counter */
942186352Ssam	npe_issue_cmd(sc, IX_NPEDL_EXCTL_CMD_CLR_PROFILE_CNT);
943186352Ssam
944186352Ssam	/* clear registers EXCT, AP0, AP1, AP2 and AP3 */
945186352Ssam	for (regAddr = IX_NPEDL_REG_OFFSET_EXCT;
946186352Ssam	     regAddr <= IX_NPEDL_REG_OFFSET_AP3;
947186352Ssam	     regAddr += sizeof(uint32_t))
948186352Ssam		npe_reg_write(sc, regAddr, 0);
949164426Ssam
950186352Ssam	/* Reset the Watch-count register */
951186352Ssam	npe_reg_write(sc, IX_NPEDL_REG_OFFSET_WC, 0);
952194378Ssam#if 0
953186352Ssam	/*
954186352Ssam	 * WR IXA00055043 - Remove IMEM Parity Introduced by NPE Reset Operation
955194378Ssam	 * XXX Removed because it breaks IXP435 operation; e.g. on Gateworks
956194378Ssam	 * XXX 2358 boards reseting NPE-A after NPE-C is running causes both
957194378Ssam	 * XXX npe's to stop working
958186352Ssam	 */
959194378Ssam	npe_reset(sc->sc_npeid);
960194378Ssam#endif
961186352Ssam	/*
962186352Ssam	 * Call NpeMgr function to stop the NPE again after the Feature Control
963186352Ssam	 * has unfused and Un-Reset the NPE and its associated Coprocessors.
964186352Ssam	 */
965186352Ssam	error = npe_cpu_stop(sc);
966164426Ssam
967186352Ssam	/* restore NPE configuration bus Control Register - Parity Settings  */
968186352Ssam	npe_reg_write(sc, IX_NPEDL_REG_OFFSET_CTL,
969186352Ssam	    (ixNpeConfigCtrlRegVal & IX_NPEDL_CONFIG_CTRL_REG_MASK));
970186352Ssam	DPRINTFn(2, sc->sc_dev, "%s: restore CTL => 0x%x\n",
971186352Ssam	    __func__, npe_reg_read(sc, IX_NPEDL_REG_OFFSET_CTL));
972186352Ssam
973186352Ssam	return error;
974164426Ssam#undef N
975164426Ssam}
976164426Ssam
977164426Ssamstatic int
978164426Ssamnpe_cpu_start(struct ixpnpe_softc *sc)
979164426Ssam{
980186352Ssam	uint32_t ecsRegVal;
981164426Ssam
982186352Ssam	/*
983186352Ssam	 * Ensure only Background Context Stack Level is Active by turning off
984186352Ssam	 * the Active bit in each of the other Executing Context Stack levels.
985186352Ssam	 */
986186352Ssam	ecsRegVal = npe_ecs_reg_read(sc, IX_NPEDL_ECS_PRI_1_CTXT_REG_0);
987186352Ssam	ecsRegVal &= ~IX_NPEDL_MASK_ECS_REG_0_ACTIVE;
988186352Ssam	npe_ecs_reg_write(sc, IX_NPEDL_ECS_PRI_1_CTXT_REG_0, ecsRegVal);
989164426Ssam
990186352Ssam	ecsRegVal = npe_ecs_reg_read(sc, IX_NPEDL_ECS_PRI_2_CTXT_REG_0);
991186352Ssam	ecsRegVal &= ~IX_NPEDL_MASK_ECS_REG_0_ACTIVE;
992186352Ssam	npe_ecs_reg_write(sc, IX_NPEDL_ECS_PRI_2_CTXT_REG_0, ecsRegVal);
993164426Ssam
994186352Ssam	ecsRegVal = npe_ecs_reg_read(sc, IX_NPEDL_ECS_DBG_CTXT_REG_0);
995186352Ssam	ecsRegVal &= ~IX_NPEDL_MASK_ECS_REG_0_ACTIVE;
996186352Ssam	npe_ecs_reg_write(sc, IX_NPEDL_ECS_DBG_CTXT_REG_0, ecsRegVal);
997186352Ssam
998186352Ssam	/* clear the pipeline */
999186352Ssam	npe_issue_cmd(sc, IX_NPEDL_EXCTL_CMD_NPE_CLR_PIPE);
1000186352Ssam
1001186352Ssam	/* start NPE execution by issuing cmd through EXCTL register on NPE */
1002186352Ssam	npe_issue_cmd(sc, IX_NPEDL_EXCTL_CMD_NPE_START);
1003164426Ssam
1004186352Ssam	/*
1005186352Ssam	 * Check execution status of NPE to verify operation was successful.
1006186352Ssam	 */
1007186352Ssam	return npe_checkbits(sc,
1008186352Ssam	    IX_NPEDL_REG_OFFSET_EXCTL, IX_NPEDL_EXCTL_STATUS_RUN) ? 0 : EIO;
1009164426Ssam}
1010164426Ssam
1011164426Ssamstatic int
1012164426Ssamnpe_cpu_stop(struct ixpnpe_softc *sc)
1013164426Ssam{
1014186352Ssam	/* stop NPE execution by issuing cmd through EXCTL register on NPE */
1015186352Ssam	npe_issue_cmd(sc, IX_NPEDL_EXCTL_CMD_NPE_STOP);
1016164426Ssam
1017186352Ssam	/* verify that NPE Stop was successful */
1018186352Ssam	return npe_checkbits(sc,
1019186352Ssam	    IX_NPEDL_REG_OFFSET_EXCTL, IX_NPEDL_EXCTL_STATUS_STOP) ? 0 : EIO;
1020164426Ssam}
1021164426Ssam
1022164426Ssam#define IX_NPEDL_REG_SIZE_BYTE            8
1023164426Ssam#define IX_NPEDL_REG_SIZE_SHORT           16
1024164426Ssam#define IX_NPEDL_REG_SIZE_WORD            32
1025164426Ssam
1026164426Ssam/*
1027164426Ssam * Introduce extra read cycles after issuing read command to NPE
1028164426Ssam * so that we read the register after the NPE has updated it
1029164426Ssam * This is to overcome race condition between XScale and NPE
1030164426Ssam */
1031164426Ssam#define IX_NPEDL_DELAY_READ_CYCLES        2
1032164426Ssam/*
1033164426Ssam * To mask top three MSBs of 32bit word to download into NPE IMEM
1034164426Ssam */
1035164426Ssam#define IX_NPEDL_MASK_UNUSED_IMEM_BITS    0x1FFFFFFF;
1036164426Ssam
1037164426Ssamstatic void
1038164426Ssamnpe_cmd_issue_write(struct ixpnpe_softc *sc,
1039164426Ssam    uint32_t cmd, uint32_t addr, uint32_t data)
1040164426Ssam{
1041186352Ssam	npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXDATA, data);
1042186352Ssam	npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXAD, addr);
1043186352Ssam	npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXCTL, cmd);
1044164426Ssam}
1045164426Ssam
1046164426Ssamstatic uint32_t
1047164426Ssamnpe_cmd_issue_read(struct ixpnpe_softc *sc, uint32_t cmd, uint32_t addr)
1048164426Ssam{
1049186352Ssam	uint32_t data;
1050186352Ssam	int i;
1051164426Ssam
1052186352Ssam	npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXAD, addr);
1053186352Ssam	npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXCTL, cmd);
1054186352Ssam	for (i = 0; i <= IX_NPEDL_DELAY_READ_CYCLES; i++)
1055186352Ssam		data = npe_reg_read(sc, IX_NPEDL_REG_OFFSET_EXDATA);
1056186352Ssam	return data;
1057164426Ssam}
1058164426Ssam
1059164426Ssamstatic int
1060164426Ssamnpe_ins_write(struct ixpnpe_softc *sc, uint32_t addr, uint32_t data, int verify)
1061164426Ssam{
1062186352Ssam	DPRINTFn(4, sc->sc_dev, "%s(0x%x, 0x%x)\n", __func__, addr, data);
1063186352Ssam	npe_cmd_issue_write(sc, IX_NPEDL_EXCTL_CMD_WR_INS_MEM, addr, data);
1064186352Ssam	if (verify) {
1065186352Ssam		uint32_t rdata;
1066164426Ssam
1067186352Ssam		/*
1068186352Ssam		 * Write invalid data to this reg, so we can see if we're
1069186352Ssam		 * reading the EXDATA register too early.
1070186352Ssam		 */
1071186352Ssam		npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXDATA, ~data);
1072164426Ssam
1073186352Ssam		/*
1074186352Ssam		 * Disabled since top 3 MSB are not used for Azusa
1075186352Ssam		 * hardware Refer WR:IXA00053900
1076186352Ssam		 */
1077186352Ssam		data &= IX_NPEDL_MASK_UNUSED_IMEM_BITS;
1078164426Ssam
1079186352Ssam		rdata = npe_cmd_issue_read(sc, IX_NPEDL_EXCTL_CMD_RD_INS_MEM,
1080186352Ssam		    addr);
1081186352Ssam		rdata &= IX_NPEDL_MASK_UNUSED_IMEM_BITS;
1082164426Ssam
1083186352Ssam		if (data != rdata)
1084186352Ssam			return EIO;
1085186352Ssam	}
1086186352Ssam	return 0;
1087164426Ssam}
1088164426Ssam
1089164426Ssamstatic int
1090164426Ssamnpe_data_write(struct ixpnpe_softc *sc, uint32_t addr, uint32_t data, int verify)
1091164426Ssam{
1092186352Ssam	DPRINTFn(4, sc->sc_dev, "%s(0x%x, 0x%x)\n", __func__, addr, data);
1093186352Ssam	npe_cmd_issue_write(sc, IX_NPEDL_EXCTL_CMD_WR_DATA_MEM, addr, data);
1094186352Ssam	if (verify) {
1095186352Ssam		/*
1096186352Ssam		 * Write invalid data to this reg, so we can see if we're
1097186352Ssam		 * reading the EXDATA register too early.
1098186352Ssam		 */
1099186352Ssam		npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXDATA, ~data);
1100186352Ssam		if (data != npe_cmd_issue_read(sc, IX_NPEDL_EXCTL_CMD_RD_DATA_MEM, addr))
1101186352Ssam			return EIO;
1102186352Ssam	}
1103186352Ssam	return 0;
1104164426Ssam}
1105164426Ssam
1106164426Ssamstatic void
1107164426Ssamnpe_ecs_reg_write(struct ixpnpe_softc *sc, uint32_t reg, uint32_t data)
1108164426Ssam{
1109186352Ssam	npe_cmd_issue_write(sc, IX_NPEDL_EXCTL_CMD_WR_ECS_REG, reg, data);
1110164426Ssam}
1111164426Ssam
1112164426Ssamstatic uint32_t
1113164426Ssamnpe_ecs_reg_read(struct ixpnpe_softc *sc, uint32_t reg)
1114164426Ssam{
1115186352Ssam	return npe_cmd_issue_read(sc, IX_NPEDL_EXCTL_CMD_RD_ECS_REG, reg);
1116164426Ssam}
1117164426Ssam
1118164426Ssamstatic void
1119164426Ssamnpe_issue_cmd(struct ixpnpe_softc *sc, uint32_t command)
1120164426Ssam{
1121186352Ssam	npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXCTL, command);
1122164426Ssam}
1123164426Ssam
1124164426Ssamstatic void
1125164426Ssamnpe_cpu_step_save(struct ixpnpe_softc *sc)
1126164426Ssam{
1127186352Ssam	/* turn off the halt bit by clearing Execution Count register. */
1128186352Ssam	/* save reg contents 1st and restore later */
1129186352Ssam	sc->savedExecCount = npe_reg_read(sc, IX_NPEDL_REG_OFFSET_EXCT);
1130186352Ssam	npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXCT, 0);
1131164426Ssam
1132186352Ssam	/* ensure that IF and IE are on (temporarily), so that we don't end up
1133186352Ssam	 * stepping forever */
1134186352Ssam	sc->savedEcsDbgCtxtReg2 = npe_ecs_reg_read(sc,
1135186352Ssam	    IX_NPEDL_ECS_DBG_CTXT_REG_2);
1136164426Ssam
1137186352Ssam	npe_ecs_reg_write(sc, IX_NPEDL_ECS_DBG_CTXT_REG_2,
1138186352Ssam	    (sc->savedEcsDbgCtxtReg2 | IX_NPEDL_MASK_ECS_DBG_REG_2_IF |
1139186352Ssam	     IX_NPEDL_MASK_ECS_DBG_REG_2_IE));
1140164426Ssam}
1141164426Ssam
1142164426Ssamstatic int
1143164426Ssamnpe_cpu_step(struct ixpnpe_softc *sc, uint32_t npeInstruction,
1144164426Ssam    uint32_t ctxtNum, uint32_t ldur)
1145164426Ssam{
1146164426Ssam#define	IX_NPE_DL_MAX_NUM_OF_RETRIES	1000000
1147186352Ssam	uint32_t ecsDbgRegVal;
1148186352Ssam	uint32_t oldWatchcount, newWatchcount;
1149186352Ssam	int tries;
1150164426Ssam
1151186352Ssam	/* set the Active bit, and the LDUR, in the debug level */
1152186352Ssam	ecsDbgRegVal = IX_NPEDL_MASK_ECS_REG_0_ACTIVE |
1153186352Ssam	    (ldur << IX_NPEDL_OFFSET_ECS_REG_0_LDUR);
1154164426Ssam
1155186352Ssam	npe_ecs_reg_write(sc, IX_NPEDL_ECS_DBG_CTXT_REG_0, ecsDbgRegVal);
1156164426Ssam
1157186352Ssam	/*
1158186352Ssam	 * Set CCTXT at ECS DEBUG L3 to specify in which context to execute the
1159186352Ssam	 * instruction, and set SELCTXT at ECS DEBUG Level to specify which
1160186352Ssam	 * context store to access.
1161186352Ssam	 * Debug ECS Level Reg 1 has form  0x000n000n, where n = context number
1162186352Ssam	 */
1163186352Ssam	ecsDbgRegVal = (ctxtNum << IX_NPEDL_OFFSET_ECS_REG_1_CCTXT) |
1164186352Ssam	    (ctxtNum << IX_NPEDL_OFFSET_ECS_REG_1_SELCTXT);
1165164426Ssam
1166186352Ssam	npe_ecs_reg_write(sc, IX_NPEDL_ECS_DBG_CTXT_REG_1, ecsDbgRegVal);
1167164426Ssam
1168186352Ssam	/* clear the pipeline */
1169186352Ssam	npe_issue_cmd(sc, IX_NPEDL_EXCTL_CMD_NPE_CLR_PIPE);
1170164426Ssam
1171186352Ssam	/* load NPE instruction into the instruction register */
1172186352Ssam	npe_ecs_reg_write(sc, IX_NPEDL_ECS_INSTRUCT_REG, npeInstruction);
1173164426Ssam
1174186352Ssam	/* need this value later to wait for completion of NPE execution step */
1175186352Ssam	oldWatchcount = npe_reg_read(sc, IX_NPEDL_REG_OFFSET_WC);
1176164426Ssam
1177186352Ssam	/* issue a Step One command via the Execution Control register */
1178186352Ssam	npe_issue_cmd(sc, IX_NPEDL_EXCTL_CMD_NPE_STEP);
1179164426Ssam
1180186352Ssam	/*
1181186352Ssam	 * Force the XScale to wait until the NPE has finished execution step
1182186352Ssam	 * NOTE that this delay will be very small, just long enough to allow a
1183186352Ssam	 * single NPE instruction to complete execution; if instruction
1184186352Ssam	 * execution is not completed before timeout retries, exit the while
1185186352Ssam	 * loop.
1186186352Ssam	 */
1187164426Ssam	newWatchcount = npe_reg_read(sc, IX_NPEDL_REG_OFFSET_WC);
1188186352Ssam	for (tries = 0; tries < IX_NPE_DL_MAX_NUM_OF_RETRIES &&
1189186352Ssam	    newWatchcount == oldWatchcount; tries++) {
1190186352Ssam		/* Watch Count register incr's when NPE completes an inst */
1191186352Ssam		newWatchcount = npe_reg_read(sc, IX_NPEDL_REG_OFFSET_WC);
1192186352Ssam	}
1193186352Ssam	return (tries < IX_NPE_DL_MAX_NUM_OF_RETRIES) ? 0 : EIO;
1194164426Ssam#undef IX_NPE_DL_MAX_NUM_OF_RETRIES
1195164426Ssam}
1196164426Ssam
1197164426Ssamstatic void
1198164426Ssamnpe_cpu_step_restore(struct ixpnpe_softc *sc)
1199164426Ssam{
1200186352Ssam	/* clear active bit in debug level */
1201186352Ssam	npe_ecs_reg_write(sc, IX_NPEDL_ECS_DBG_CTXT_REG_0, 0);
1202164426Ssam
1203186352Ssam	/* clear the pipeline */
1204186352Ssam	npe_issue_cmd(sc, IX_NPEDL_EXCTL_CMD_NPE_CLR_PIPE);
1205164426Ssam
1206186352Ssam	/* restore Execution Count register contents. */
1207186352Ssam	npe_reg_write(sc, IX_NPEDL_REG_OFFSET_EXCT, sc->savedExecCount);
1208164426Ssam
1209186352Ssam	/* restore IF and IE bits to original values */
1210186352Ssam	npe_ecs_reg_write(sc, IX_NPEDL_ECS_DBG_CTXT_REG_2, sc->savedEcsDbgCtxtReg2);
1211164426Ssam}
1212164426Ssam
1213164426Ssamstatic int
1214164426Ssamnpe_logical_reg_read(struct ixpnpe_softc *sc,
1215164426Ssam    uint32_t regAddr, uint32_t regSize,
1216164426Ssam    uint32_t ctxtNum, uint32_t *regVal)
1217164426Ssam{
1218186352Ssam	uint32_t npeInstruction, mask;
1219186352Ssam	int error;
1220164426Ssam
1221186352Ssam	switch (regSize) {
1222186352Ssam	case IX_NPEDL_REG_SIZE_BYTE:
1223186352Ssam		npeInstruction = IX_NPEDL_INSTR_RD_REG_BYTE;
1224186352Ssam		mask = 0xff;
1225186352Ssam		break;
1226186352Ssam	case IX_NPEDL_REG_SIZE_SHORT:
1227186352Ssam		npeInstruction = IX_NPEDL_INSTR_RD_REG_SHORT;
1228186352Ssam		mask = 0xffff;
1229186352Ssam		break;
1230186352Ssam	case IX_NPEDL_REG_SIZE_WORD:
1231186352Ssam		npeInstruction = IX_NPEDL_INSTR_RD_REG_WORD;
1232186352Ssam		mask = 0xffffffff;
1233186352Ssam		break;
1234186352Ssam	default:
1235186352Ssam		return EINVAL;
1236186352Ssam	}
1237164426Ssam
1238186352Ssam	/* make regAddr be the SRC and DEST operands (e.g. movX d0, d0) */
1239186352Ssam	npeInstruction |= (regAddr << IX_NPEDL_OFFSET_INSTR_SRC) |
1240186352Ssam	    (regAddr << IX_NPEDL_OFFSET_INSTR_DEST);
1241164426Ssam
1242186352Ssam	/* step execution of NPE inst using Debug Executing Context stack */
1243186352Ssam	error = npe_cpu_step(sc, npeInstruction, ctxtNum,
1244186352Ssam	    IX_NPEDL_RD_INSTR_LDUR);
1245186352Ssam	if (error != 0) {
1246186352Ssam		DPRINTF(sc->sc_dev, "%s(0x%x, %u, %u), cannot step, error %d\n",
1247186352Ssam		    __func__, regAddr, regSize, ctxtNum, error);
1248186352Ssam		return error;
1249186352Ssam	}
1250186352Ssam	/* read value of register from Execution Data register */
1251186352Ssam	*regVal = npe_reg_read(sc, IX_NPEDL_REG_OFFSET_EXDATA);
1252164426Ssam
1253186352Ssam	/* align value from left to right */
1254186352Ssam	*regVal = (*regVal >> (IX_NPEDL_REG_SIZE_WORD - regSize)) & mask;
1255164426Ssam
1256186352Ssam	return 0;
1257164426Ssam}
1258164426Ssam
1259164426Ssamstatic int
1260164426Ssamnpe_logical_reg_write(struct ixpnpe_softc *sc, uint32_t regAddr, uint32_t regVal,
1261164426Ssam    uint32_t regSize, uint32_t ctxtNum, int verify)
1262164426Ssam{
1263186352Ssam	int error;
1264164426Ssam
1265186352Ssam	DPRINTFn(4, sc->sc_dev, "%s(0x%x, 0x%x, %u, %u)\n",
1266186352Ssam	    __func__, regAddr, regVal, regSize, ctxtNum);
1267186352Ssam	if (regSize == IX_NPEDL_REG_SIZE_WORD) {
1268186352Ssam		/*
1269186352Ssam		 * NPE register addressing is left-to-right: e.g. |d0|d1|d2|d3|
1270186352Ssam		 * Write upper half-word (short) to |d0|d1|
1271186352Ssam		 */
1272186352Ssam		error = npe_logical_reg_write(sc, regAddr,
1273186352Ssam			     regVal >> IX_NPEDL_REG_SIZE_SHORT,
1274186352Ssam			     IX_NPEDL_REG_SIZE_SHORT, ctxtNum, verify);
1275186352Ssam		if (error != 0)
1276186352Ssam		    return error;
1277164426Ssam
1278186352Ssam		/* Write lower half-word (short) to |d2|d3| */
1279186352Ssam		error = npe_logical_reg_write(sc,
1280186352Ssam			     regAddr + sizeof(uint16_t),
1281186352Ssam			     regVal & 0xffff,
1282186352Ssam			     IX_NPEDL_REG_SIZE_SHORT, ctxtNum, verify);
1283186352Ssam	} else {
1284186352Ssam		uint32_t npeInstruction;
1285164426Ssam
1286186352Ssam		switch (regSize) {
1287186352Ssam		case IX_NPEDL_REG_SIZE_BYTE:
1288186352Ssam			npeInstruction = IX_NPEDL_INSTR_WR_REG_BYTE;
1289186352Ssam			regVal &= 0xff;
1290186352Ssam			break;
1291186352Ssam		case IX_NPEDL_REG_SIZE_SHORT:
1292186352Ssam			npeInstruction = IX_NPEDL_INSTR_WR_REG_SHORT;
1293186352Ssam			regVal &= 0xffff;
1294186352Ssam			break;
1295186352Ssam		default:
1296186352Ssam			return EINVAL;
1297186352Ssam		}
1298186352Ssam		/* fill dest operand field of inst with dest reg addr */
1299186352Ssam		npeInstruction |= (regAddr << IX_NPEDL_OFFSET_INSTR_DEST);
1300164426Ssam
1301186352Ssam		/* fill src operand field of inst with least-sig 5 bits of val*/
1302186352Ssam		npeInstruction |=
1303186352Ssam		    ((regVal & IX_NPEDL_MASK_IMMED_INSTR_SRC_DATA) <<
1304186352Ssam		     IX_NPEDL_OFFSET_INSTR_SRC);
1305164426Ssam
1306186352Ssam		/* fill coprocessor field of inst with most-sig 11 bits of val*/
1307186352Ssam		npeInstruction |=
1308186352Ssam		    ((regVal & IX_NPEDL_MASK_IMMED_INSTR_COPROC_DATA) <<
1309186352Ssam		     IX_NPEDL_DISPLACE_IMMED_INSTR_COPROC_DATA);
1310164426Ssam
1311186352Ssam		/* step execution of NPE intruction using Debug ECS */
1312186352Ssam		error = npe_cpu_step(sc, npeInstruction,
1313186352Ssam		    ctxtNum, IX_NPEDL_WR_INSTR_LDUR);
1314186352Ssam	}
1315186352Ssam	if (error != 0) {
1316186352Ssam		DPRINTF(sc->sc_dev, "%s(0x%x, 0x%x, %u, %u), error %u "
1317186352Ssam		    "writing reg\n", __func__, regAddr, regVal, regSize,
1318186352Ssam		    ctxtNum, error);
1319186352Ssam		return error;
1320186352Ssam	}
1321186352Ssam	if (verify) {
1322186352Ssam		uint32_t retRegVal;
1323186352Ssam
1324186352Ssam		error = npe_logical_reg_read(sc, regAddr, regSize, ctxtNum,
1325186352Ssam		    &retRegVal);
1326186352Ssam		if (error == 0 && regVal != retRegVal)
1327186352Ssam			error = EIO;	/* XXX ambiguous */
1328186352Ssam	}
1329164426Ssam	return error;
1330164426Ssam}
1331164426Ssam
1332164426Ssam/*
1333164426Ssam * There are 32 physical registers used in an NPE.  These are
1334164426Ssam * treated as 16 pairs of 32-bit registers.  To write one of the pair,
1335164426Ssam * write the pair number (0-16) to the REGMAP for Context 0.  Then write
1336164426Ssam * the value to register  0 or 4 in the regfile, depending on which
1337164426Ssam * register of the pair is to be written
1338164426Ssam */
1339164426Ssamstatic int
1340164426Ssamnpe_physical_reg_write(struct ixpnpe_softc *sc,
1341164426Ssam    uint32_t regAddr, uint32_t regValue, int verify)
1342164426Ssam{
1343186352Ssam	int error;
1344164426Ssam
1345186352Ssam	/*
1346186352Ssam	 * Set REGMAP for context 0 to (regAddr >> 1) to choose which pair
1347186352Ssam	 * (0-16) of physical registers to write .
1348186352Ssam	 */
1349186352Ssam	error = npe_logical_reg_write(sc, IX_NPEDL_CTXT_REG_ADDR_REGMAP,
1350186352Ssam		   (regAddr >> IX_NPEDL_OFFSET_PHYS_REG_ADDR_REGMAP),
1351186352Ssam		   IX_NPEDL_REG_SIZE_SHORT, 0, verify);
1352186352Ssam	if (error == 0) {
1353186352Ssam	    /* regAddr = 0 or 4  */
1354186352Ssam	    regAddr = (regAddr & IX_NPEDL_MASK_PHYS_REG_ADDR_LOGICAL_ADDR) *
1355186352Ssam		sizeof(uint32_t);
1356186352Ssam	    error = npe_logical_reg_write(sc, regAddr, regValue,
1357186352Ssam		IX_NPEDL_REG_SIZE_WORD, 0, verify);
1358186352Ssam	}
1359186352Ssam	return error;
1360164426Ssam}
1361164426Ssam
1362164426Ssamstatic int
1363164426Ssamnpe_ctx_reg_write(struct ixpnpe_softc *sc, uint32_t ctxtNum,
1364164426Ssam    uint32_t ctxtReg, uint32_t ctxtRegVal, int verify)
1365164426Ssam{
1366186352Ssam	DPRINTFn(4, sc->sc_dev, "%s(%u, %u, %u)\n",
1367186352Ssam	    __func__, ctxtNum, ctxtReg, ctxtRegVal);
1368186352Ssam	/*
1369186352Ssam	 * Context 0 has no STARTPC. Instead, this value is used to set
1370186352Ssam	 * NextPC for Background ECS, to set where NPE starts executing code
1371186352Ssam	 */
1372186352Ssam	if (ctxtNum == 0 && ctxtReg == IX_NPEDL_CTXT_REG_STARTPC) {
1373186352Ssam		/* read BG_CTXT_REG_0, update NEXTPC bits, & write back to reg*/
1374186352Ssam		uint32_t v = npe_ecs_reg_read(sc, IX_NPEDL_ECS_BG_CTXT_REG_0);
1375186352Ssam		v &= ~IX_NPEDL_MASK_ECS_REG_0_NEXTPC;
1376186352Ssam		v |= (ctxtRegVal << IX_NPEDL_OFFSET_ECS_REG_0_NEXTPC) &
1377186352Ssam		    IX_NPEDL_MASK_ECS_REG_0_NEXTPC;
1378164426Ssam
1379186352Ssam		npe_ecs_reg_write(sc, IX_NPEDL_ECS_BG_CTXT_REG_0, v);
1380186352Ssam		return 0;
1381186352Ssam	} else {
1382186352Ssam		static const struct {
1383186352Ssam			uint32_t regAddress;
1384186352Ssam			uint32_t regSize;
1385186352Ssam		} regAccInfo[IX_NPEDL_CTXT_REG_MAX] = {
1386186352Ssam			{ IX_NPEDL_CTXT_REG_ADDR_STEVT,
1387186352Ssam			  IX_NPEDL_REG_SIZE_BYTE },
1388186352Ssam			{ IX_NPEDL_CTXT_REG_ADDR_STARTPC,
1389186352Ssam			  IX_NPEDL_REG_SIZE_SHORT },
1390186352Ssam			{ IX_NPEDL_CTXT_REG_ADDR_REGMAP,
1391186352Ssam			  IX_NPEDL_REG_SIZE_SHORT },
1392186352Ssam			{ IX_NPEDL_CTXT_REG_ADDR_CINDEX,
1393186352Ssam			  IX_NPEDL_REG_SIZE_BYTE }
1394186352Ssam		};
1395186352Ssam		return npe_logical_reg_write(sc, regAccInfo[ctxtReg].regAddress,
1396186352Ssam			ctxtRegVal, regAccInfo[ctxtReg].regSize, ctxtNum, verify);
1397186352Ssam	}
1398164426Ssam}
1399164426Ssam
1400164426Ssam/*
1401164426Ssam * NPE Mailbox support.
1402164426Ssam */
1403164426Ssam#define	IX_NPEMH_MAXTRIES	100000
1404164426Ssam
1405164426Ssamstatic int
1406186352Ssamofifo_wait(struct ixpnpe_softc *sc)
1407164426Ssam{
1408186352Ssam	int i;
1409164426Ssam
1410186352Ssam	for (i = 0; i < IX_NPEMH_MAXTRIES; i++) {
1411186352Ssam		if (npe_reg_read(sc, IX_NPESTAT) & IX_NPESTAT_OFNE)
1412186352Ssam			return 1;
1413186352Ssam		DELAY(10);
1414186352Ssam	}
1415186352Ssam	device_printf(sc->sc_dev, "%s: timeout, last status 0x%x\n",
1416186352Ssam	    __func__, npe_reg_read(sc, IX_NPESTAT));
1417186352Ssam	return 0;
1418164426Ssam}
1419164426Ssam
1420186352Ssamstatic int
1421186352Ssamgetmsg(struct ixpnpe_softc *sc, uint32_t msg[2])
1422186352Ssam{
1423186352Ssam	mtx_assert(&sc->sc_mtx, MA_OWNED);
1424186352Ssam
1425186352Ssam	if (!ofifo_wait(sc))
1426186352Ssam		return EAGAIN;
1427186352Ssam	msg[0] = npe_reg_read(sc, IX_NPEFIFO);
1428186352Ssam	DPRINTF(sc->sc_dev, "%s: msg0 0x%x\n", __func__, msg[0]);
1429186352Ssam	if (!ofifo_wait(sc))
1430186352Ssam		return EAGAIN;
1431186352Ssam	msg[1] = npe_reg_read(sc, IX_NPEFIFO);
1432186352Ssam	DPRINTF(sc->sc_dev, "%s: msg1 0x%x\n", __func__, msg[1]);
1433186352Ssam	return 0;
1434186352Ssam}
1435186352Ssam
1436164426Ssamstatic void
1437164426Ssamixpnpe_intr(void *arg)
1438164426Ssam{
1439186352Ssam	struct ixpnpe_softc *sc = arg;
1440186352Ssam	uint32_t status;
1441164426Ssam
1442186352Ssam	mtx_lock(&sc->sc_mtx);
1443186352Ssam	status = npe_reg_read(sc, IX_NPESTAT);
1444186352Ssam	DPRINTF(sc->sc_dev, "%s: status 0x%x\n", __func__, status);
1445186352Ssam	if ((status & IX_NPESTAT_OFINT) == 0) {
1446186352Ssam		/* NB: should not happen */
1447186352Ssam		device_printf(sc->sc_dev, "%s: status 0x%x\n",
1448186352Ssam		    __func__, status);
1449186352Ssam		/* XXX must silence interrupt? */
1450186352Ssam		mtx_unlock(&sc->sc_mtx);
1451186352Ssam		return;
1452164426Ssam	}
1453186352Ssam	/*
1454186352Ssam	 * A message is waiting in the output FIFO, copy it so
1455186352Ssam	 * the interrupt will be silenced.
1456186352Ssam	 */
1457186352Ssam	if (getmsg(sc, sc->sc_msg) == 0)
1458186352Ssam		sc->sc_msgwaiting = 1;
1459186352Ssam	mtx_unlock(&sc->sc_mtx);
1460164426Ssam}
1461164426Ssam
1462164426Ssamstatic int
1463186352Ssamififo_wait(struct ixpnpe_softc *sc)
1464164426Ssam{
1465186352Ssam	int i;
1466164426Ssam
1467186352Ssam	for (i = 0; i < IX_NPEMH_MAXTRIES; i++) {
1468186352Ssam		if (npe_reg_read(sc, IX_NPESTAT) & IX_NPESTAT_IFNF)
1469186352Ssam			return 1;
1470186352Ssam		DELAY(10);
1471186352Ssam	}
1472186352Ssam	device_printf(sc->sc_dev, "%s: timeout, last status 0x%x\n",
1473186352Ssam	    __func__, npe_reg_read(sc, IX_NPESTAT));
1474186352Ssam	return 0;
1475164426Ssam}
1476164426Ssam
1477164426Ssamstatic int
1478186352Ssamputmsg(struct ixpnpe_softc *sc, const uint32_t msg[2])
1479164426Ssam{
1480186352Ssam	mtx_assert(&sc->sc_mtx, MA_OWNED);
1481164426Ssam
1482186352Ssam	DPRINTF(sc->sc_dev, "%s: msg 0x%x:0x%x\n", __func__, msg[0], msg[1]);
1483186352Ssam	if (!ififo_wait(sc))
1484186352Ssam		return EIO;
1485164426Ssam	npe_reg_write(sc, IX_NPEFIFO, msg[0]);
1486186352Ssam	if (!ififo_wait(sc))
1487186352Ssam		return EIO;
1488186352Ssam	npe_reg_write(sc, IX_NPEFIFO, msg[1]);
1489164426Ssam
1490186352Ssam	return 0;
1491164426Ssam}
1492164426Ssam
1493186352Ssam/*
1494186352Ssam * Send a msg to the NPE and wait for a reply.  We spin as
1495186352Ssam * we may be called early with interrupts not properly setup.
1496186352Ssam */
1497186352Ssamint
1498186352Ssamixpnpe_sendandrecvmsg_sync(struct ixpnpe_softc *sc,
1499186352Ssam	const uint32_t send[2], uint32_t recv[2])
1500164426Ssam{
1501186352Ssam	int error;
1502164426Ssam
1503186352Ssam	mtx_lock(&sc->sc_mtx);
1504186352Ssam	error = putmsg(sc, send);
1505186352Ssam	if (error == 0)
1506186352Ssam		error = getmsg(sc, recv);
1507186352Ssam	mtx_unlock(&sc->sc_mtx);
1508186352Ssam
1509186352Ssam	return error;
1510164426Ssam}
1511164426Ssam
1512164426Ssam/*
1513186352Ssam * Send a msg to the NPE w/o waiting for a reply.
1514164426Ssam */
1515164426Ssamint
1516186352Ssamixpnpe_sendmsg_async(struct ixpnpe_softc *sc, const uint32_t msg[2])
1517164426Ssam{
1518186352Ssam	int error;
1519164426Ssam
1520186352Ssam	mtx_lock(&sc->sc_mtx);
1521186352Ssam	error = putmsg(sc, msg);
1522186352Ssam	mtx_unlock(&sc->sc_mtx);
1523164426Ssam
1524186352Ssam	return error;
1525164426Ssam}
1526164426Ssam
1527186352Ssamstatic int
1528186352Ssamrecvmsg_locked(struct ixpnpe_softc *sc, uint32_t msg[2])
1529186352Ssam{
1530186352Ssam	mtx_assert(&sc->sc_mtx, MA_OWNED);
1531164426Ssam
1532186352Ssam	DPRINTF(sc->sc_dev, "%s: msgwaiting %d\n", __func__, sc->sc_msgwaiting);
1533186352Ssam	if (sc->sc_msgwaiting) {
1534186352Ssam		msg[0] = sc->sc_msg[0];
1535186352Ssam		msg[1] = sc->sc_msg[1];
1536186352Ssam		sc->sc_msgwaiting = 0;
1537186352Ssam		return 0;
1538186352Ssam	}
1539186352Ssam	return EAGAIN;
1540186352Ssam}
1541186352Ssam
1542186352Ssam/*
1543186352Ssam * Receive any msg previously received from the NPE. If nothing
1544186352Ssam * is available we return EAGAIN and the caller is required to
1545186352Ssam * do a synchronous receive or try again later.
1546186352Ssam */
1547164426Ssamint
1548186352Ssamixpnpe_recvmsg_async(struct ixpnpe_softc *sc, uint32_t msg[2])
1549164426Ssam{
1550186352Ssam	int error;
1551164426Ssam
1552186352Ssam	mtx_lock(&sc->sc_mtx);
1553186352Ssam	error = recvmsg_locked(sc, msg);
1554186352Ssam	mtx_unlock(&sc->sc_mtx);
1555164426Ssam
1556186352Ssam	return error;
1557164426Ssam}
1558164426Ssam
1559186352Ssam/*
1560186352Ssam * Receive a msg from the NPE.  If one was received asynchronously
1561186352Ssam * then it's returned; otherwise we poll synchronously.
1562186352Ssam */
1563164426Ssamint
1564186352Ssamixpnpe_recvmsg_sync(struct ixpnpe_softc *sc, uint32_t msg[2])
1565164426Ssam{
1566186352Ssam	int error;
1567164426Ssam
1568186352Ssam	mtx_lock(&sc->sc_mtx);
1569186352Ssam	error = recvmsg_locked(sc, msg);
1570186352Ssam	if (error == EAGAIN)
1571186352Ssam		error = getmsg(sc, msg);
1572186352Ssam	mtx_unlock(&sc->sc_mtx);
1573164426Ssam
1574186352Ssam	return error;
1575164426Ssam}
1576