1164426Ssam/*-
2164426Ssam * Copyright (c) 2006 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 Queue Manager support.
64164426Ssam *
65164426Ssam * Each IXP4XXX device has a hardware block that implements a priority
66164426Ssam * queue manager that is shared between the XScale cpu and the backend
67164426Ssam * devices (such as the NPE).  Queues are accessed by reading/writing
68164426Ssam * special memory locations.  The queue contents are mapped into a shared
69164426Ssam * SRAM region with entries managed in a circular buffer.  The XScale
70164426Ssam * processor can receive interrupts based on queue contents (a condition
71164426Ssam * code determines when interrupts should be delivered).
72164426Ssam *
73164426Ssam * The code here basically replaces the qmgr class in the Intel Access
74164426Ssam * Library (IAL).
75164426Ssam */
76164426Ssam#include <sys/param.h>
77164426Ssam#include <sys/systm.h>
78164426Ssam#include <sys/kernel.h>
79164426Ssam#include <sys/module.h>
80164426Ssam#include <sys/time.h>
81164426Ssam#include <sys/bus.h>
82164426Ssam#include <sys/resource.h>
83164426Ssam#include <sys/rman.h>
84164426Ssam#include <sys/sysctl.h>
85164426Ssam
86164426Ssam#include <machine/bus.h>
87164426Ssam#include <machine/cpu.h>
88164426Ssam#include <machine/cpufunc.h>
89164426Ssam#include <machine/resource.h>
90164426Ssam#include <machine/intr.h>
91164426Ssam#include <arm/xscale/ixp425/ixp425reg.h>
92164426Ssam#include <arm/xscale/ixp425/ixp425var.h>
93164426Ssam
94164426Ssam#include <arm/xscale/ixp425/ixp425_qmgr.h>
95164426Ssam
96164426Ssam/*
97164426Ssam * State per AQM hw queue.
98164426Ssam * This structure holds q configuration and dispatch state.
99164426Ssam */
100164426Ssamstruct qmgrInfo {
101164426Ssam	int		qSizeInWords;		/* queue size in words */
102164426Ssam
103164426Ssam	uint32_t	qOflowStatBitMask;	/* overflow status mask */
104164426Ssam	int		qWriteCount;		/* queue write count */
105164426Ssam
106164426Ssam	bus_size_t	qAccRegAddr;		/* access register */
107164426Ssam	bus_size_t	qUOStatRegAddr;		/* status register */
108164426Ssam	bus_size_t	qConfigRegAddr;		/* config register */
109164426Ssam	int		qSizeInEntries;		/* queue size in entries */
110164426Ssam
111164426Ssam	uint32_t	qUflowStatBitMask;	/* underflow status mask */
112164426Ssam	int		qReadCount;		/* queue read count */
113164426Ssam
114164426Ssam	/* XXX union */
115164426Ssam	uint32_t	qStatRegAddr;
116164426Ssam	uint32_t	qStatBitsOffset;
117164426Ssam	uint32_t	qStat0BitMask;
118164426Ssam	uint32_t	qStat1BitMask;
119164426Ssam
120164426Ssam	uint32_t	intRegCheckMask;	/* interrupt reg check mask */
121164426Ssam	void		(*cb)(int, void *);	/* callback function */
122164426Ssam	void		*cbarg;			/* callback argument */
123164426Ssam	int 		priority;		/* dispatch priority */
124164426Ssam#if 0
125164426Ssam	/* NB: needed only for A0 parts */
126164426Ssam	u_int		statusWordOffset;	/* status word offset */
127164426Ssam	uint32_t	statusMask;             /* status mask */
128164426Ssam	uint32_t	statusCheckValue;	/* status check value */
129164426Ssam#endif
130164426Ssam};
131164426Ssam
132164426Ssamstruct ixpqmgr_softc {
133164426Ssam	device_t		sc_dev;
134164426Ssam	bus_space_tag_t		sc_iot;
135164426Ssam	bus_space_handle_t	sc_ioh;
136164426Ssam
137182932Sraj	struct resource		*sc_irq1;	/* IRQ resource */
138182932Sraj	void			*sc_ih1;	/* interrupt handler */
139182932Sraj	int			sc_rid1;	/* resource id for irq */
140182932Sraj
141182932Sraj	struct resource		*sc_irq2;
142182932Sraj	void			*sc_ih2;
143182932Sraj	int			sc_rid2;
144182932Sraj
145164426Ssam	struct qmgrInfo		qinfo[IX_QMGR_MAX_NUM_QUEUES];
146164426Ssam	/*
147164426Ssam	 * This array contains a list of queue identifiers ordered by
148164426Ssam	 * priority. The table is split logically between queue
149164426Ssam	 * identifiers 0-31 and 32-63.  To optimize lookups bit masks
150164426Ssam	 * are kept for the first-32 and last-32 q's.  When the
151164426Ssam	 * table needs to be rebuilt mark rebuildTable and it'll
152164426Ssam	 * happen after the next interrupt.
153164426Ssam	 */
154164426Ssam	int			priorityTable[IX_QMGR_MAX_NUM_QUEUES];
155164426Ssam	uint32_t		lowPriorityTableFirstHalfMask;
156164426Ssam	uint32_t		uppPriorityTableFirstHalfMask;
157164426Ssam	int			rebuildTable;	/* rebuild priorityTable */
158164426Ssam
159164426Ssam	uint32_t		aqmFreeSramAddress;	/* SRAM free space */
160164426Ssam};
161164426Ssam
162164426Ssamstatic int qmgr_debug = 0;
163164426SsamSYSCTL_INT(_debug, OID_AUTO, qmgr, CTLFLAG_RW, &qmgr_debug,
164186352Ssam	   0, "IXP4XX Q-Manager debug msgs");
165164426SsamTUNABLE_INT("debug.qmgr", &qmgr_debug);
166164426Ssam#define	DPRINTF(dev, fmt, ...) do {					\
167164426Ssam	if (qmgr_debug) printf(fmt, __VA_ARGS__);			\
168164426Ssam} while (0)
169164426Ssam#define	DPRINTFn(n, dev, fmt, ...) do {					\
170164426Ssam	if (qmgr_debug >= n) printf(fmt, __VA_ARGS__);			\
171164426Ssam} while (0)
172164426Ssam
173164426Ssamstatic struct ixpqmgr_softc *ixpqmgr_sc = NULL;
174164426Ssam
175164426Ssamstatic void ixpqmgr_rebuild(struct ixpqmgr_softc *);
176164426Ssamstatic void ixpqmgr_intr(void *);
177164426Ssam
178164426Ssamstatic void aqm_int_enable(struct ixpqmgr_softc *sc, int qId);
179164426Ssamstatic void aqm_int_disable(struct ixpqmgr_softc *sc, int qId);
180164426Ssamstatic void aqm_qcfg(struct ixpqmgr_softc *sc, int qId, u_int ne, u_int nf);
181164426Ssamstatic void aqm_srcsel_write(struct ixpqmgr_softc *sc, int qId, int sourceId);
182164426Ssamstatic void aqm_reset(struct ixpqmgr_softc *sc);
183164426Ssam
184164426Ssamstatic void
185164426SsamdummyCallback(int qId, void *arg)
186164426Ssam{
187164426Ssam	/* XXX complain */
188164426Ssam}
189164426Ssam
190164426Ssamstatic uint32_t
191164426Ssamaqm_reg_read(struct ixpqmgr_softc *sc, bus_size_t off)
192164426Ssam{
193164426Ssam	DPRINTFn(9, sc->sc_dev, "%s(0x%x)\n", __func__, (int)off);
194164426Ssam	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, off);
195164426Ssam}
196164426Ssam
197164426Ssamstatic void
198164426Ssamaqm_reg_write(struct ixpqmgr_softc *sc, bus_size_t off, uint32_t val)
199164426Ssam{
200164426Ssam	DPRINTFn(9, sc->sc_dev, "%s(0x%x, 0x%x)\n", __func__, (int)off, val);
201164426Ssam	bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, val);
202164426Ssam}
203164426Ssam
204164426Ssamstatic int
205164426Ssamixpqmgr_probe(device_t dev)
206164426Ssam{
207186352Ssam	device_set_desc(dev, "IXP4XX Q-Manager");
208164426Ssam	return 0;
209164426Ssam}
210164426Ssam
211182932Srajstatic int
212164426Ssamixpqmgr_attach(device_t dev)
213164426Ssam{
214164426Ssam	struct ixpqmgr_softc *sc = device_get_softc(dev);
215164426Ssam	struct ixp425_softc *sa = device_get_softc(device_get_parent(dev));
216182932Sraj	int i, err;
217164426Ssam
218164426Ssam	ixpqmgr_sc = sc;
219164426Ssam
220164426Ssam	sc->sc_dev = dev;
221164426Ssam	sc->sc_iot = sa->sc_iot;
222164426Ssam	if (bus_space_map(sc->sc_iot, IXP425_QMGR_HWBASE, IXP425_QMGR_SIZE,
223164426Ssam	    0, &sc->sc_ioh))
224164426Ssam		panic("%s: Cannot map registers", device_get_name(dev));
225164426Ssam
226164426Ssam	/* NB: we only use the lower 32 q's */
227182932Sraj
228182932Sraj	/* Set up QMGR interrupts */
229182932Sraj	sc->sc_rid1 = 0;
230182932Sraj	sc->sc_irq1 = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->sc_rid1,
231182932Sraj	    IXP425_INT_QUE1_32, IXP425_INT_QUE1_32, 1, RF_ACTIVE);
232182932Sraj	sc->sc_rid2 = 1;
233182932Sraj	sc->sc_irq2 = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->sc_rid2,
234182932Sraj	    IXP425_INT_QUE33_64, IXP425_INT_QUE33_64, 1, RF_ACTIVE);
235182932Sraj
236182932Sraj	if (sc->sc_irq1 == NULL || sc->sc_irq2 == NULL)
237164426Ssam		panic("Unable to allocate the qmgr irqs.\n");
238164426Ssam
239182932Sraj	err = bus_setup_intr(dev, sc->sc_irq1, INTR_TYPE_NET | INTR_MPSAFE,
240182932Sraj	    NULL, ixpqmgr_intr, NULL, &sc->sc_ih1);
241182932Sraj	if (err) {
242182932Sraj		device_printf(dev, "failed to set up qmgr irq=%d\n",
243182932Sraj		   IXP425_INT_QUE1_32);
244182932Sraj		return (ENXIO);
245182932Sraj	}
246182932Sraj	err = bus_setup_intr(dev, sc->sc_irq2, INTR_TYPE_NET | INTR_MPSAFE,
247182932Sraj	    NULL, ixpqmgr_intr, NULL, &sc->sc_ih2);
248182932Sraj	if (err) {
249182932Sraj		device_printf(dev, "failed to set up qmgr irq=%d\n",
250182932Sraj		   IXP425_INT_QUE33_64);
251182932Sraj		return (ENXIO);
252182932Sraj	}
253182932Sraj
254164426Ssam	/* NB: softc is pre-zero'd */
255164426Ssam	for (i = 0; i < IX_QMGR_MAX_NUM_QUEUES; i++) {
256164426Ssam	    struct qmgrInfo *qi = &sc->qinfo[i];
257164426Ssam
258164426Ssam	    qi->cb = dummyCallback;
259164426Ssam	    qi->priority = IX_QMGR_Q_PRIORITY_0;	/* default priority */
260164426Ssam	    /*
261164426Ssam	     * There are two interrupt registers, 32 bits each. One
262164426Ssam	     * for the lower queues(0-31) and one for the upper
263164426Ssam	     * queues(32-63). Therefore need to mod by 32 i.e the
264164426Ssam	     * min upper queue identifier.
265164426Ssam	     */
266164426Ssam	    qi->intRegCheckMask = (1<<(i%(IX_QMGR_MIN_QUEUPP_QID)));
267164426Ssam
268164426Ssam	    /*
269164426Ssam	     * Register addresses and bit masks are calculated and
270164426Ssam	     * stored here to optimize QRead, QWrite and QStatusGet
271164426Ssam	     * functions.
272164426Ssam	     */
273164426Ssam
274164426Ssam	    /* AQM Queue access reg addresses, per queue */
275164426Ssam	    qi->qAccRegAddr = IX_QMGR_Q_ACCESS_ADDR_GET(i);
276164426Ssam	    qi->qAccRegAddr = IX_QMGR_Q_ACCESS_ADDR_GET(i);
277164426Ssam	    qi->qConfigRegAddr = IX_QMGR_Q_CONFIG_ADDR_GET(i);
278164426Ssam
279164426Ssam	    /* AQM Queue lower-group (0-31), only */
280164426Ssam	    if (i < IX_QMGR_MIN_QUEUPP_QID) {
281164426Ssam		/* AQM Q underflow/overflow status reg address, per queue */
282164426Ssam		qi->qUOStatRegAddr = IX_QMGR_QUEUOSTAT0_OFFSET +
283164426Ssam		    ((i / IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD) *
284164426Ssam		     sizeof(uint32_t));
285164426Ssam
286164426Ssam		/* AQM Q underflow status bit masks for status reg per queue */
287164426Ssam		qi->qUflowStatBitMask =
288164426Ssam		    (IX_QMGR_UNDERFLOW_BIT_OFFSET + 1) <<
289164426Ssam		    ((i & (IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD - 1)) *
290164426Ssam		     (32 / IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD));
291164426Ssam
292164426Ssam		/* AQM Q overflow status bit masks for status reg, per queue */
293164426Ssam		qi->qOflowStatBitMask =
294164426Ssam		    (IX_QMGR_OVERFLOW_BIT_OFFSET + 1) <<
295164426Ssam		    ((i & (IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD - 1)) *
296164426Ssam		     (32 / IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD));
297164426Ssam
298164426Ssam		/* AQM Q lower-group (0-31) status reg addresses, per queue */
299164426Ssam		qi->qStatRegAddr = IX_QMGR_QUELOWSTAT0_OFFSET +
300164426Ssam		    ((i / IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD) *
301164426Ssam		     sizeof(uint32_t));
302164426Ssam
303164426Ssam		/* AQM Q lower-group (0-31) status register bit offset */
304164426Ssam		qi->qStatBitsOffset =
305164426Ssam		    (i & (IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD - 1)) *
306164426Ssam		    (32 / IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD);
307164426Ssam	    } else { /* AQM Q upper-group (32-63), only */
308164426Ssam		qi->qUOStatRegAddr = 0;		/* XXX */
309164426Ssam
310164426Ssam		/* AQM Q upper-group (32-63) Nearly Empty status reg bitmasks */
311164426Ssam		qi->qStat0BitMask = (1 << (i - IX_QMGR_MIN_QUEUPP_QID));
312164426Ssam
313164426Ssam		/* AQM Q upper-group (32-63) Full status register bitmasks */
314164426Ssam		qi->qStat1BitMask = (1 << (i - IX_QMGR_MIN_QUEUPP_QID));
315164426Ssam	    }
316164426Ssam	}
317164426Ssam
318164426Ssam	sc->aqmFreeSramAddress = 0x100;	/* Q buffer space starts at 0x2100 */
319164426Ssam
320215034Sbrucec	ixpqmgr_rebuild(sc);		/* build initial priority table */
321164426Ssam	aqm_reset(sc);			/* reset h/w */
322182932Sraj	return (0);
323164426Ssam}
324164426Ssam
325182932Srajstatic int
326164426Ssamixpqmgr_detach(device_t dev)
327164426Ssam{
328164426Ssam	struct ixpqmgr_softc *sc = device_get_softc(dev);
329164426Ssam
330164426Ssam	aqm_reset(sc);		/* disable interrupts */
331182932Sraj	bus_teardown_intr(dev, sc->sc_irq1, sc->sc_ih1);
332182932Sraj	bus_teardown_intr(dev, sc->sc_irq2, sc->sc_ih2);
333182932Sraj	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid1, sc->sc_irq1);
334182932Sraj	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid2, sc->sc_irq2);
335164426Ssam	bus_space_unmap(sc->sc_iot, sc->sc_ioh, IXP425_QMGR_SIZE);
336182932Sraj	return (0);
337164426Ssam}
338164426Ssam
339164426Ssamint
340164426Ssamixpqmgr_qconfig(int qId, int qEntries, int ne, int nf, int srcSel,
341193096Sattilio    qconfig_hand_t *cb, void *cbarg)
342164426Ssam{
343164426Ssam	struct ixpqmgr_softc *sc = ixpqmgr_sc;
344164426Ssam	struct qmgrInfo *qi = &sc->qinfo[qId];
345164426Ssam
346164426Ssam	DPRINTF(sc->sc_dev, "%s(%u, %u, %u, %u, %u, %p, %p)\n",
347164426Ssam	    __func__, qId, qEntries, ne, nf, srcSel, cb, cbarg);
348164426Ssam
349164426Ssam	/* NB: entry size is always 1 */
350164426Ssam	qi->qSizeInWords = qEntries;
351164426Ssam
352164426Ssam	qi->qReadCount = 0;
353164426Ssam	qi->qWriteCount = 0;
354164426Ssam	qi->qSizeInEntries = qEntries;	/* XXX kept for code clarity */
355164426Ssam
356164426Ssam	if (cb == NULL) {
357164426Ssam	    /* Reset to dummy callback */
358164426Ssam	    qi->cb = dummyCallback;
359164426Ssam	    qi->cbarg = 0;
360164426Ssam	} else {
361164426Ssam	    qi->cb = cb;
362164426Ssam	    qi->cbarg = cbarg;
363164426Ssam	}
364164426Ssam
365164426Ssam	/* Write the config register; NB must be AFTER qinfo setup */
366164426Ssam	aqm_qcfg(sc, qId, ne, nf);
367164426Ssam	/*
368164426Ssam	 * Account for space just allocated to queue.
369164426Ssam	 */
370164426Ssam	sc->aqmFreeSramAddress += (qi->qSizeInWords * sizeof(uint32_t));
371164426Ssam
372172568Skevlo	/* Set the interrupt source if this queue is in the range 0-31 */
373164426Ssam	if (qId < IX_QMGR_MIN_QUEUPP_QID)
374164426Ssam	    aqm_srcsel_write(sc, qId, srcSel);
375164426Ssam
376164426Ssam	if (cb != NULL)				/* Enable the interrupt */
377164426Ssam	    aqm_int_enable(sc, qId);
378164426Ssam
379164426Ssam	sc->rebuildTable = TRUE;
380164426Ssam
381164426Ssam	return 0;		/* XXX */
382164426Ssam}
383164426Ssam
384164426Ssamint
385164426Ssamixpqmgr_qwrite(int qId, uint32_t entry)
386164426Ssam{
387164426Ssam	struct ixpqmgr_softc *sc = ixpqmgr_sc;
388164426Ssam	struct qmgrInfo *qi = &sc->qinfo[qId];
389164426Ssam
390164426Ssam	DPRINTFn(3, sc->sc_dev, "%s(%u, 0x%x) writeCount %u size %u\n",
391164426Ssam	    __func__, qId, entry, qi->qWriteCount, qi->qSizeInEntries);
392164426Ssam
393164426Ssam	/* write the entry */
394164426Ssam	aqm_reg_write(sc, qi->qAccRegAddr, entry);
395164426Ssam
396164426Ssam	/* NB: overflow is available for lower queues only */
397164426Ssam	if (qId < IX_QMGR_MIN_QUEUPP_QID) {
398164426Ssam	    int qSize = qi->qSizeInEntries;
399164426Ssam	    /*
400164426Ssam	     * Increment the current number of entries in the queue
401164426Ssam	     * and check for overflow .
402164426Ssam	     */
403164426Ssam	    if (qi->qWriteCount++ == qSize) {	/* check for overflow */
404164426Ssam		uint32_t status = aqm_reg_read(sc, qi->qUOStatRegAddr);
405164426Ssam		int qPtrs;
406164426Ssam
407164426Ssam		/*
408164426Ssam		 * Read the status twice because the status may
409164426Ssam		 * not be immediately ready after the write operation
410164426Ssam		 */
411164426Ssam		if ((status & qi->qOflowStatBitMask) ||
412164426Ssam		    ((status = aqm_reg_read(sc, qi->qUOStatRegAddr)) & qi->qOflowStatBitMask)) {
413164426Ssam		    /*
414164426Ssam		     * The queue is full, clear the overflow status bit if set.
415164426Ssam		     */
416164426Ssam		    aqm_reg_write(sc, qi->qUOStatRegAddr,
417164426Ssam			status & ~qi->qOflowStatBitMask);
418164426Ssam		    qi->qWriteCount = qSize;
419164426Ssam		    DPRINTFn(5, sc->sc_dev,
420164426Ssam			"%s(%u, 0x%x) Q full, overflow status cleared\n",
421164426Ssam			__func__, qId, entry);
422164426Ssam		    return ENOSPC;
423164426Ssam		}
424164426Ssam		/*
425164426Ssam		 * No overflow occured : someone is draining the queue
426164426Ssam		 * and the current counter needs to be
427164426Ssam		 * updated from the current number of entries in the queue
428164426Ssam		 */
429164426Ssam
430164426Ssam		/* calculate number of words in q */
431164426Ssam		qPtrs = aqm_reg_read(sc, qi->qConfigRegAddr);
432164426Ssam		DPRINTFn(2, sc->sc_dev,
433164426Ssam		    "%s(%u, 0x%x) Q full, no overflow status, qConfig 0x%x\n",
434164426Ssam		    __func__, qId, entry, qPtrs);
435164426Ssam		qPtrs = (qPtrs - (qPtrs >> 7)) & 0x7f;
436164426Ssam
437164426Ssam		if (qPtrs == 0) {
438164426Ssam		    /*
439164426Ssam		     * The queue may be full at the time of the
440164426Ssam		     * snapshot. Next access will check
441164426Ssam		     * the overflow status again.
442164426Ssam		     */
443164426Ssam		    qi->qWriteCount = qSize;
444164426Ssam		} else {
445164426Ssam		    /* convert the number of words to a number of entries */
446164426Ssam		    qi->qWriteCount = qPtrs & (qSize - 1);
447164426Ssam		}
448164426Ssam	    }
449164426Ssam	}
450164426Ssam	return 0;
451164426Ssam}
452164426Ssam
453164426Ssamint
454164426Ssamixpqmgr_qread(int qId, uint32_t *entry)
455164426Ssam{
456164426Ssam	struct ixpqmgr_softc *sc = ixpqmgr_sc;
457164426Ssam	struct qmgrInfo *qi = &sc->qinfo[qId];
458164426Ssam	bus_size_t off = qi->qAccRegAddr;
459164426Ssam
460164426Ssam	*entry = aqm_reg_read(sc, off);
461164426Ssam
462164426Ssam	/*
463164426Ssam	 * Reset the current read count : next access to the read function
464164426Ssam	 * will force a underflow status check.
465164426Ssam	 */
466164426Ssam	qi->qReadCount = 0;
467164426Ssam
468164426Ssam	/* Check if underflow occurred on the read */
469164426Ssam	if (*entry == 0 && qId < IX_QMGR_MIN_QUEUPP_QID) {
470164426Ssam	    /* get the queue status */
471164426Ssam	    uint32_t status = aqm_reg_read(sc, qi->qUOStatRegAddr);
472164426Ssam
473164426Ssam	    if (status & qi->qUflowStatBitMask) { /* clear underflow status */
474164426Ssam		aqm_reg_write(sc, qi->qUOStatRegAddr,
475164426Ssam		    status &~ qi->qUflowStatBitMask);
476164426Ssam		return ENOSPC;
477164426Ssam	    }
478164426Ssam	}
479164426Ssam	return 0;
480164426Ssam}
481164426Ssam
482164426Ssamint
483164426Ssamixpqmgr_qreadm(int qId, uint32_t n, uint32_t *p)
484164426Ssam{
485164426Ssam	struct ixpqmgr_softc *sc = ixpqmgr_sc;
486164426Ssam	struct qmgrInfo *qi = &sc->qinfo[qId];
487164426Ssam	uint32_t entry;
488164426Ssam	bus_size_t off = qi->qAccRegAddr;
489164426Ssam
490164426Ssam	entry = aqm_reg_read(sc, off);
491164426Ssam	while (--n) {
492164426Ssam	    if (entry == 0) {
493164426Ssam		/* if we read a NULL entry, stop. We have underflowed */
494164426Ssam		break;
495164426Ssam	    }
496164426Ssam	    *p++ = entry;	/* store */
497164426Ssam	    entry = aqm_reg_read(sc, off);
498164426Ssam	}
499164426Ssam	*p = entry;
500164426Ssam
501164426Ssam	/*
502164426Ssam	 * Reset the current read count : next access to the read function
503164426Ssam	 * will force a underflow status check.
504164426Ssam	 */
505164426Ssam	qi->qReadCount = 0;
506164426Ssam
507164426Ssam	/* Check if underflow occurred on the read */
508164426Ssam	if (entry == 0 && qId < IX_QMGR_MIN_QUEUPP_QID) {
509164426Ssam	    /* get the queue status */
510164426Ssam	    uint32_t status = aqm_reg_read(sc, qi->qUOStatRegAddr);
511164426Ssam
512164426Ssam	    if (status & qi->qUflowStatBitMask) { /* clear underflow status */
513164426Ssam		aqm_reg_write(sc, qi->qUOStatRegAddr,
514164426Ssam		    status &~ qi->qUflowStatBitMask);
515164426Ssam		return ENOSPC;
516164426Ssam	    }
517164426Ssam	}
518164426Ssam	return 0;
519164426Ssam}
520164426Ssam
521164426Ssamuint32_t
522164426Ssamixpqmgr_getqstatus(int qId)
523164426Ssam{
524164426Ssam#define	QLOWSTATMASK \
525164426Ssam    ((1 << (32 / IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD)) - 1)
526164426Ssam	struct ixpqmgr_softc *sc = ixpqmgr_sc;
527164426Ssam	const struct qmgrInfo *qi = &sc->qinfo[qId];
528164426Ssam	uint32_t status;
529164426Ssam
530164426Ssam	if (qId < IX_QMGR_MIN_QUEUPP_QID) {
531164426Ssam	    /* read the status of a queue in the range 0-31 */
532164426Ssam	    status = aqm_reg_read(sc, qi->qStatRegAddr);
533164426Ssam
534164426Ssam	    /* mask out the status bits relevant only to this queue */
535164426Ssam	    status = (status >> qi->qStatBitsOffset) & QLOWSTATMASK;
536164426Ssam	} else { /* read status of a queue in the range 32-63 */
537164426Ssam	    status = 0;
538164426Ssam	    if (aqm_reg_read(sc, IX_QMGR_QUEUPPSTAT0_OFFSET)&qi->qStat0BitMask)
539164426Ssam		status |= IX_QMGR_Q_STATUS_NE_BIT_MASK;	/* nearly empty */
540164426Ssam	    if (aqm_reg_read(sc, IX_QMGR_QUEUPPSTAT1_OFFSET)&qi->qStat1BitMask)
541164426Ssam		status |= IX_QMGR_Q_STATUS_F_BIT_MASK;	/* full */
542164426Ssam	}
543164426Ssam	return status;
544164426Ssam#undef QLOWSTATMASK
545164426Ssam}
546164426Ssam
547164426Ssamuint32_t
548164426Ssamixpqmgr_getqconfig(int qId)
549164426Ssam{
550164426Ssam	struct ixpqmgr_softc *sc = ixpqmgr_sc;
551164426Ssam
552164426Ssam	return aqm_reg_read(sc, IX_QMGR_Q_CONFIG_ADDR_GET(qId));
553164426Ssam}
554164426Ssam
555164426Ssamvoid
556164426Ssamixpqmgr_dump(void)
557164426Ssam{
558164426Ssam	struct ixpqmgr_softc *sc = ixpqmgr_sc;
559164426Ssam	int i, a;
560164426Ssam
561164426Ssam	/* status registers */
562164426Ssam	printf("0x%04x: %08x %08x %08x %08x\n"
563164426Ssam		, 0x400
564164426Ssam		, aqm_reg_read(sc, 0x400)
565164426Ssam		, aqm_reg_read(sc, 0x400+4)
566164426Ssam		, aqm_reg_read(sc, 0x400+8)
567164426Ssam		, aqm_reg_read(sc, 0x400+12)
568164426Ssam	);
569164426Ssam	printf("0x%04x: %08x %08x %08x %08x\n"
570164426Ssam		, 0x410
571164426Ssam		, aqm_reg_read(sc, 0x410)
572164426Ssam		, aqm_reg_read(sc, 0x410+4)
573164426Ssam		, aqm_reg_read(sc, 0x410+8)
574164426Ssam		, aqm_reg_read(sc, 0x410+12)
575164426Ssam	);
576164426Ssam	printf("0x%04x: %08x %08x %08x %08x\n"
577164426Ssam		, 0x420
578164426Ssam		, aqm_reg_read(sc, 0x420)
579164426Ssam		, aqm_reg_read(sc, 0x420+4)
580164426Ssam		, aqm_reg_read(sc, 0x420+8)
581164426Ssam		, aqm_reg_read(sc, 0x420+12)
582164426Ssam	);
583164426Ssam	printf("0x%04x: %08x %08x %08x %08x\n"
584164426Ssam		, 0x430
585164426Ssam		, aqm_reg_read(sc, 0x430)
586164426Ssam		, aqm_reg_read(sc, 0x430+4)
587164426Ssam		, aqm_reg_read(sc, 0x430+8)
588164426Ssam		, aqm_reg_read(sc, 0x430+12)
589164426Ssam	);
590164426Ssam	/* q configuration registers */
591164426Ssam	for (a = 0x2000; a < 0x20ff; a += 32)
592164426Ssam		printf("0x%04x: %08x %08x %08x %08x %08x %08x %08x %08x\n"
593164426Ssam			, a
594164426Ssam			, aqm_reg_read(sc, a)
595164426Ssam			, aqm_reg_read(sc, a+4)
596164426Ssam			, aqm_reg_read(sc, a+8)
597164426Ssam			, aqm_reg_read(sc, a+12)
598164426Ssam			, aqm_reg_read(sc, a+16)
599164426Ssam			, aqm_reg_read(sc, a+20)
600164426Ssam			, aqm_reg_read(sc, a+24)
601164426Ssam			, aqm_reg_read(sc, a+28)
602164426Ssam		);
603164426Ssam	/* allocated SRAM */
604164426Ssam	for (i = 0x100; i < sc->aqmFreeSramAddress; i += 32) {
605164426Ssam		a = 0x2000 + i;
606164426Ssam		printf("0x%04x: %08x %08x %08x %08x %08x %08x %08x %08x\n"
607164426Ssam			, a
608164426Ssam			, aqm_reg_read(sc, a)
609164426Ssam			, aqm_reg_read(sc, a+4)
610164426Ssam			, aqm_reg_read(sc, a+8)
611164426Ssam			, aqm_reg_read(sc, a+12)
612164426Ssam			, aqm_reg_read(sc, a+16)
613164426Ssam			, aqm_reg_read(sc, a+20)
614164426Ssam			, aqm_reg_read(sc, a+24)
615164426Ssam			, aqm_reg_read(sc, a+28)
616164426Ssam		);
617164426Ssam	}
618164426Ssam	for (i = 0; i < 16; i++) {
619164426Ssam		printf("Q[%2d] config 0x%08x status 0x%02x  "
620164426Ssam		       "Q[%2d] config 0x%08x status 0x%02x\n"
621164426Ssam		    , i, ixpqmgr_getqconfig(i), ixpqmgr_getqstatus(i)
622164426Ssam		    , i+16, ixpqmgr_getqconfig(i+16), ixpqmgr_getqstatus(i+16)
623164426Ssam		);
624164426Ssam	}
625164426Ssam}
626164426Ssam
627164426Ssamvoid
628164426Ssamixpqmgr_notify_enable(int qId, int srcSel)
629164426Ssam{
630164426Ssam	struct ixpqmgr_softc *sc = ixpqmgr_sc;
631164426Ssam#if 0
632164426Ssam	/* Calculate the checkMask and checkValue for this q */
633164426Ssam	aqm_calc_statuscheck(sc, qId, srcSel);
634164426Ssam#endif
635172568Skevlo	/* Set the interrupt source if this queue is in the range 0-31 */
636164426Ssam	if (qId < IX_QMGR_MIN_QUEUPP_QID)
637164426Ssam	    aqm_srcsel_write(sc, qId, srcSel);
638164426Ssam
639164426Ssam	/* Enable the interrupt */
640164426Ssam	aqm_int_enable(sc, qId);
641164426Ssam}
642164426Ssam
643164426Ssamvoid
644164426Ssamixpqmgr_notify_disable(int qId)
645164426Ssam{
646164426Ssam	struct ixpqmgr_softc *sc = ixpqmgr_sc;
647164426Ssam
648164426Ssam	aqm_int_disable(sc, qId);
649164426Ssam}
650164426Ssam
651164426Ssam/*
652164426Ssam * Rebuild the priority table used by the dispatcher.
653164426Ssam */
654164426Ssamstatic void
655164426Ssamixpqmgr_rebuild(struct ixpqmgr_softc *sc)
656164426Ssam{
657164426Ssam	int q, pri;
658164426Ssam	int lowQuePriorityTableIndex, uppQuePriorityTableIndex;
659164426Ssam	struct qmgrInfo *qi;
660164426Ssam
661164426Ssam	sc->lowPriorityTableFirstHalfMask = 0;
662164426Ssam	sc->uppPriorityTableFirstHalfMask = 0;
663164426Ssam
664164426Ssam	lowQuePriorityTableIndex = 0;
665164426Ssam	uppQuePriorityTableIndex = 32;
666164426Ssam	for (pri = 0; pri < IX_QMGR_NUM_PRIORITY_LEVELS; pri++) {
667164426Ssam	    /* low priority q's */
668164426Ssam	    for (q = 0; q < IX_QMGR_MIN_QUEUPP_QID; q++) {
669164426Ssam		qi = &sc->qinfo[q];
670164426Ssam		if (qi->priority == pri) {
671164426Ssam		    /*
672164426Ssam		     * Build the priority table bitmask which match the
673164426Ssam		     * queues of the first half of the priority table.
674164426Ssam		     */
675164426Ssam		    if (lowQuePriorityTableIndex < 16) {
676164426Ssam			sc->lowPriorityTableFirstHalfMask |=
677164426Ssam			    qi->intRegCheckMask;
678164426Ssam		    }
679164426Ssam		    sc->priorityTable[lowQuePriorityTableIndex++] = q;
680164426Ssam		}
681164426Ssam	    }
682164426Ssam	    /* high priority q's */
683164426Ssam	    for (; q < IX_QMGR_MAX_NUM_QUEUES; q++) {
684164426Ssam		qi = &sc->qinfo[q];
685164426Ssam		if (qi->priority == pri) {
686164426Ssam		    /*
687164426Ssam		     * Build the priority table bitmask which match the
688164426Ssam		     * queues of the first half of the priority table .
689164426Ssam		     */
690164426Ssam		    if (uppQuePriorityTableIndex < 48) {
691164426Ssam			sc->uppPriorityTableFirstHalfMask |=
692164426Ssam			    qi->intRegCheckMask;
693164426Ssam		    }
694164426Ssam		    sc->priorityTable[uppQuePriorityTableIndex++] = q;
695164426Ssam		}
696164426Ssam	    }
697164426Ssam	}
698164426Ssam	sc->rebuildTable = FALSE;
699164426Ssam}
700164426Ssam
701164426Ssam/*
702164426Ssam * Count the number of leading zero bits in a word,
703164426Ssam * and return the same value than the CLZ instruction.
704164426Ssam * Note this is similar to the standard ffs function but
705164426Ssam * it counts zero's from the MSB instead of the LSB.
706164426Ssam *
707164426Ssam * word (in)    return value (out)
708164426Ssam * 0x80000000   0
709164426Ssam * 0x40000000   1
710164426Ssam * ,,,          ,,,
711164426Ssam * 0x00000002   30
712164426Ssam * 0x00000001   31
713164426Ssam * 0x00000000   32
714164426Ssam *
715164426Ssam * The C version of this function is used as a replacement
716164426Ssam * for system not providing the equivalent of the CLZ
717164426Ssam * assembly language instruction.
718164426Ssam *
719164426Ssam * Note that this version is big-endian
720164426Ssam */
721164426Ssamstatic unsigned int
722164426Ssam_lzcount(uint32_t word)
723164426Ssam{
724164426Ssam	unsigned int lzcount = 0;
725164426Ssam
726164426Ssam	if (word == 0)
727164426Ssam	    return 32;
728164426Ssam	while ((word & 0x80000000) == 0) {
729164426Ssam	    word <<= 1;
730164426Ssam	    lzcount++;
731164426Ssam	}
732164426Ssam	return lzcount;
733164426Ssam}
734164426Ssam
735164426Ssamstatic void
736164426Ssamixpqmgr_intr(void *arg)
737164426Ssam{
738164426Ssam	struct ixpqmgr_softc *sc = ixpqmgr_sc;
739164426Ssam	uint32_t intRegVal;                /* Interrupt reg val */
740164426Ssam	struct qmgrInfo *qi;
741164426Ssam	int priorityTableIndex;		/* Priority table index */
742164426Ssam	int qIndex;			/* Current queue being processed */
743164426Ssam
744164426Ssam	/* Read the interrupt register */
745164426Ssam	intRegVal = aqm_reg_read(sc, IX_QMGR_QINTREG0_OFFSET);
746164426Ssam	/* Write back to clear interrupt */
747164426Ssam	aqm_reg_write(sc, IX_QMGR_QINTREG0_OFFSET, intRegVal);
748164426Ssam
749164426Ssam	DPRINTFn(5, sc->sc_dev, "%s: ISR0 0x%x ISR1 0x%x\n",
750164426Ssam	    __func__, intRegVal, aqm_reg_read(sc, IX_QMGR_QINTREG1_OFFSET));
751164426Ssam
752164426Ssam	/* No queue has interrupt register set */
753164426Ssam	if (intRegVal != 0) {
754164426Ssam		/* get the first queue Id from the interrupt register value */
755164426Ssam		qIndex = (32 - 1) - _lzcount(intRegVal);
756164426Ssam
757164426Ssam		DPRINTFn(2, sc->sc_dev, "%s: ISR0 0x%x qIndex %u\n",
758164426Ssam		    __func__, intRegVal, qIndex);
759164426Ssam
760164426Ssam		/*
761164426Ssam		 * Optimize for single callback case.
762164426Ssam		 */
763164426Ssam		 qi = &sc->qinfo[qIndex];
764164426Ssam		 if (intRegVal == qi->intRegCheckMask) {
765164426Ssam		    /*
766164426Ssam		     * Only 1 queue event triggered a notification.
767164426Ssam		     * Call the callback function for this queue
768164426Ssam		     */
769164426Ssam		    qi->cb(qIndex, qi->cbarg);
770164426Ssam		 } else {
771164426Ssam		     /*
772164426Ssam		      * The event is triggered by more than 1 queue,
773164426Ssam		      * the queue search will start from the beginning
774164426Ssam		      * or the middle of the priority table.
775164426Ssam		      *
776164426Ssam		      * The search will end when all the bits of the interrupt
777164426Ssam		      * register are cleared. There is no need to maintain
778215034Sbrucec		      * a separate value and test it at each iteration.
779164426Ssam		      */
780164426Ssam		     if (intRegVal & sc->lowPriorityTableFirstHalfMask) {
781164426Ssam			 priorityTableIndex = 0;
782164426Ssam		     } else {
783164426Ssam			 priorityTableIndex = 16;
784164426Ssam		     }
785164426Ssam		     /*
786164426Ssam		      * Iterate over the priority table until all the bits
787164426Ssam		      * of the interrupt register are cleared.
788164426Ssam		      */
789164426Ssam		     do {
790164426Ssam			 qIndex = sc->priorityTable[priorityTableIndex++];
791164426Ssam			 qi = &sc->qinfo[qIndex];
792164426Ssam
793164426Ssam			 /* If this queue caused this interrupt to be raised */
794164426Ssam			 if (intRegVal & qi->intRegCheckMask) {
795164426Ssam			     /* Call the callback function for this queue */
796164426Ssam			     qi->cb(qIndex, qi->cbarg);
797164426Ssam			     /* Clear the interrupt register bit */
798164426Ssam			     intRegVal &= ~qi->intRegCheckMask;
799164426Ssam			 }
800164426Ssam		      } while (intRegVal);
801164426Ssam		 }
802164426Ssam	 }
803164426Ssam
804164426Ssam	/* Rebuild the priority table if needed */
805164426Ssam	if (sc->rebuildTable)
806164426Ssam	    ixpqmgr_rebuild(sc);
807164426Ssam}
808164426Ssam
809164426Ssam#if 0
810164426Ssam/*
811164426Ssam * Generate the parameters used to check if a Q's status matches
812164426Ssam * the specified source select.  We calculate which status word
813164426Ssam * to check (statusWordOffset), the value to check the status
814164426Ssam * against (statusCheckValue) and the mask (statusMask) to mask
815164426Ssam * out all but the bits to check in the status word.
816164426Ssam */
817164426Ssamstatic void
818164426Ssamaqm_calc_statuscheck(int qId, IxQMgrSourceId srcSel)
819164426Ssam{
820164426Ssam	struct qmgrInfo *qi = &qinfo[qId];
821164426Ssam	uint32_t shiftVal;
822164426Ssam
823164426Ssam	if (qId < IX_QMGR_MIN_QUEUPP_QID) {
824164426Ssam	    switch (srcSel) {
825164426Ssam	    case IX_QMGR_Q_SOURCE_ID_E:
826164426Ssam		qi->statusCheckValue = IX_QMGR_Q_STATUS_E_BIT_MASK;
827164426Ssam		qi->statusMask = IX_QMGR_Q_STATUS_E_BIT_MASK;
828164426Ssam		break;
829164426Ssam	    case IX_QMGR_Q_SOURCE_ID_NE:
830164426Ssam		qi->statusCheckValue = IX_QMGR_Q_STATUS_NE_BIT_MASK;
831164426Ssam		qi->statusMask = IX_QMGR_Q_STATUS_NE_BIT_MASK;
832164426Ssam		break;
833164426Ssam	    case IX_QMGR_Q_SOURCE_ID_NF:
834164426Ssam		qi->statusCheckValue = IX_QMGR_Q_STATUS_NF_BIT_MASK;
835164426Ssam		qi->statusMask = IX_QMGR_Q_STATUS_NF_BIT_MASK;
836164426Ssam		break;
837164426Ssam	    case IX_QMGR_Q_SOURCE_ID_F:
838164426Ssam		qi->statusCheckValue = IX_QMGR_Q_STATUS_F_BIT_MASK;
839164426Ssam		qi->statusMask = IX_QMGR_Q_STATUS_F_BIT_MASK;
840164426Ssam		break;
841164426Ssam	    case IX_QMGR_Q_SOURCE_ID_NOT_E:
842164426Ssam		qi->statusCheckValue = 0;
843164426Ssam		qi->statusMask = IX_QMGR_Q_STATUS_E_BIT_MASK;
844164426Ssam		break;
845164426Ssam	    case IX_QMGR_Q_SOURCE_ID_NOT_NE:
846164426Ssam		qi->statusCheckValue = 0;
847164426Ssam		qi->statusMask = IX_QMGR_Q_STATUS_NE_BIT_MASK;
848164426Ssam		break;
849164426Ssam	    case IX_QMGR_Q_SOURCE_ID_NOT_NF:
850164426Ssam		qi->statusCheckValue = 0;
851164426Ssam		qi->statusMask = IX_QMGR_Q_STATUS_NF_BIT_MASK;
852164426Ssam		break;
853164426Ssam	    case IX_QMGR_Q_SOURCE_ID_NOT_F:
854164426Ssam		qi->statusCheckValue = 0;
855164426Ssam		qi->statusMask = IX_QMGR_Q_STATUS_F_BIT_MASK;
856164426Ssam		break;
857164426Ssam	    default:
858164426Ssam		/* Should never hit */
859164426Ssam		IX_OSAL_ASSERT(0);
860164426Ssam		break;
861164426Ssam	    }
862164426Ssam
863164426Ssam	    /* One nibble of status per queue so need to shift the
864164426Ssam	     * check value and mask out to the correct position.
865164426Ssam	     */
866164426Ssam	    shiftVal = (qId % IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD) *
867164426Ssam		IX_QMGR_QUELOWSTAT_BITS_PER_Q;
868164426Ssam
869164426Ssam	    /* Calculate the which status word to check from the qId,
870164426Ssam	     * 8 Qs status per word
871164426Ssam	     */
872164426Ssam	    qi->statusWordOffset = qId / IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD;
873164426Ssam
874164426Ssam	    qi->statusCheckValue <<= shiftVal;
875164426Ssam	    qi->statusMask <<= shiftVal;
876164426Ssam	} else {
877164426Ssam	    /* One status word */
878164426Ssam	    qi->statusWordOffset = 0;
879164426Ssam	    /* Single bits per queue and int source bit hardwired  NE,
880164426Ssam	     * Qs start at 32.
881164426Ssam	     */
882164426Ssam	    qi->statusMask = 1 << (qId - IX_QMGR_MIN_QUEUPP_QID);
883164426Ssam	    qi->statusCheckValue = qi->statusMask;
884164426Ssam	}
885164426Ssam}
886164426Ssam#endif
887164426Ssam
888164426Ssamstatic void
889164426Ssamaqm_int_enable(struct ixpqmgr_softc *sc, int qId)
890164426Ssam{
891164426Ssam	bus_size_t reg;
892164426Ssam	uint32_t v;
893164426Ssam
894164426Ssam	if (qId < IX_QMGR_MIN_QUEUPP_QID)
895164426Ssam	    reg = IX_QMGR_QUEIEREG0_OFFSET;
896164426Ssam	else
897164426Ssam	    reg = IX_QMGR_QUEIEREG1_OFFSET;
898164426Ssam	v = aqm_reg_read(sc, reg);
899164426Ssam	aqm_reg_write(sc, reg, v | (1 << (qId % IX_QMGR_MIN_QUEUPP_QID)));
900164426Ssam
901164426Ssam	DPRINTF(sc->sc_dev, "%s(%u) 0x%lx: 0x%x => 0x%x\n",
902164426Ssam	    __func__, qId, reg, v, aqm_reg_read(sc, reg));
903164426Ssam}
904164426Ssam
905164426Ssamstatic void
906164426Ssamaqm_int_disable(struct ixpqmgr_softc *sc, int qId)
907164426Ssam{
908164426Ssam	bus_size_t reg;
909164426Ssam	uint32_t v;
910164426Ssam
911164426Ssam	if (qId < IX_QMGR_MIN_QUEUPP_QID)
912164426Ssam	    reg = IX_QMGR_QUEIEREG0_OFFSET;
913164426Ssam	else
914164426Ssam	    reg = IX_QMGR_QUEIEREG1_OFFSET;
915164426Ssam	v = aqm_reg_read(sc, reg);
916164426Ssam	aqm_reg_write(sc, reg, v &~ (1 << (qId % IX_QMGR_MIN_QUEUPP_QID)));
917164426Ssam
918164426Ssam	DPRINTF(sc->sc_dev, "%s(%u) 0x%lx: 0x%x => 0x%x\n",
919164426Ssam	    __func__, qId, reg, v, aqm_reg_read(sc, reg));
920164426Ssam}
921164426Ssam
922164426Ssamstatic unsigned
923164426Ssamlog2(unsigned n)
924164426Ssam{
925164426Ssam	unsigned count;
926164426Ssam	/*
927164426Ssam	 * N.B. this function will return 0 if supplied 0.
928164426Ssam	 */
929164426Ssam	for (count = 0; n/2; count++)
930164426Ssam	    n /= 2;
931164426Ssam	return count;
932164426Ssam}
933164426Ssam
934164426Ssamstatic __inline unsigned
935164426SsamtoAqmEntrySize(int entrySize)
936164426Ssam{
937164426Ssam	/* entrySize  1("00"),2("01"),4("10") */
938164426Ssam	return log2(entrySize);
939164426Ssam}
940164426Ssam
941164426Ssamstatic __inline unsigned
942164426SsamtoAqmBufferSize(unsigned bufferSizeInWords)
943164426Ssam{
944164426Ssam	/* bufferSize 16("00"),32("01),64("10"),128("11") */
945164426Ssam	return log2(bufferSizeInWords / IX_QMGR_MIN_BUFFER_SIZE);
946164426Ssam}
947164426Ssam
948164426Ssamstatic __inline unsigned
949164426SsamtoAqmWatermark(int watermark)
950164426Ssam{
951164426Ssam	/*
952164426Ssam	 * Watermarks 0("000"),1("001"),2("010"),4("011"),
953164426Ssam	 * 8("100"),16("101"),32("110"),64("111")
954164426Ssam	 */
955164426Ssam	return log2(2 * watermark);
956164426Ssam}
957164426Ssam
958164426Ssamstatic void
959164426Ssamaqm_qcfg(struct ixpqmgr_softc *sc, int qId, u_int ne, u_int nf)
960164426Ssam{
961164426Ssam	const struct qmgrInfo *qi = &sc->qinfo[qId];
962164426Ssam	uint32_t qCfg;
963164426Ssam	uint32_t baseAddress;
964164426Ssam
965164426Ssam	/* Build config register */
966164426Ssam	qCfg = ((toAqmEntrySize(1) & IX_QMGR_ENTRY_SIZE_MASK) <<
967164426Ssam		    IX_QMGR_Q_CONFIG_ESIZE_OFFSET)
968164426Ssam	     | ((toAqmBufferSize(qi->qSizeInWords) & IX_QMGR_SIZE_MASK) <<
969164426Ssam		    IX_QMGR_Q_CONFIG_BSIZE_OFFSET);
970164426Ssam
971164426Ssam	/* baseAddress, calculated relative to start address */
972164426Ssam	baseAddress = sc->aqmFreeSramAddress;
973164426Ssam
974164426Ssam	/* base address must be word-aligned */
975164426Ssam	KASSERT((baseAddress % IX_QMGR_BASE_ADDR_16_WORD_ALIGN) == 0,
976164426Ssam	    ("address not word-aligned"));
977164426Ssam
978164426Ssam	/* Now convert to a 16 word pointer as required by QUECONFIG register */
979164426Ssam	baseAddress >>= IX_QMGR_BASE_ADDR_16_WORD_SHIFT;
980164426Ssam	qCfg |= baseAddress << IX_QMGR_Q_CONFIG_BADDR_OFFSET;
981164426Ssam
982164426Ssam	/* set watermarks */
983164426Ssam	qCfg |= (toAqmWatermark(ne) << IX_QMGR_Q_CONFIG_NE_OFFSET)
984164426Ssam	     |  (toAqmWatermark(nf) << IX_QMGR_Q_CONFIG_NF_OFFSET);
985164426Ssam
986164426Ssam	DPRINTF(sc->sc_dev, "%s(%u, %u, %u) 0x%x => 0x%x @ 0x%x\n",
987164426Ssam	    __func__, qId, ne, nf,
988164426Ssam	    aqm_reg_read(sc, IX_QMGR_Q_CONFIG_ADDR_GET(qId)),
989164426Ssam	    qCfg, IX_QMGR_Q_CONFIG_ADDR_GET(qId));
990164426Ssam
991164426Ssam	aqm_reg_write(sc, IX_QMGR_Q_CONFIG_ADDR_GET(qId), qCfg);
992164426Ssam}
993164426Ssam
994164426Ssamstatic void
995164426Ssamaqm_srcsel_write(struct ixpqmgr_softc *sc, int qId, int sourceId)
996164426Ssam{
997164426Ssam	bus_size_t off;
998164426Ssam	uint32_t v;
999164426Ssam
1000164426Ssam	/*
1001164426Ssam	 * Calculate the register offset; multiple queues split across registers
1002164426Ssam	 */
1003164426Ssam	off = IX_QMGR_INT0SRCSELREG0_OFFSET +
1004164426Ssam	    ((qId / IX_QMGR_INTSRC_NUM_QUE_PER_WORD) * sizeof(uint32_t));
1005164426Ssam
1006164426Ssam	v = aqm_reg_read(sc, off);
1007164426Ssam	if (off == IX_QMGR_INT0SRCSELREG0_OFFSET && qId == 0) {
1008164426Ssam	    /* Queue 0 at INT0SRCSELREG should not corrupt the value bit-3  */
1009164426Ssam	    v |= 0x7;
1010164426Ssam	} else {
1011164426Ssam	  const uint32_t bpq = 32 / IX_QMGR_INTSRC_NUM_QUE_PER_WORD;
1012164426Ssam	  uint32_t mask;
1013164426Ssam	  int qshift;
1014164426Ssam
1015164426Ssam	  qshift = (qId & (IX_QMGR_INTSRC_NUM_QUE_PER_WORD-1)) * bpq;
1016164426Ssam	  mask = ((1 << bpq) - 1) << qshift;	/* q's status mask */
1017164426Ssam
1018164426Ssam	  /* merge sourceId */
1019164426Ssam	  v = (v &~ mask) | ((sourceId << qshift) & mask);
1020164426Ssam	}
1021164426Ssam
1022164426Ssam	DPRINTF(sc->sc_dev, "%s(%u, %u) 0x%x => 0x%x @ 0x%lx\n",
1023164426Ssam	    __func__, qId, sourceId, aqm_reg_read(sc, off), v, off);
1024164426Ssam	aqm_reg_write(sc, off, v);
1025164426Ssam}
1026164426Ssam
1027164426Ssam/*
1028164426Ssam * Reset AQM registers to default values.
1029164426Ssam */
1030164426Ssamstatic void
1031164426Ssamaqm_reset(struct ixpqmgr_softc *sc)
1032164426Ssam{
1033164426Ssam	int i;
1034164426Ssam
1035164426Ssam	/* Reset queues 0..31 status registers 0..3 */
1036164426Ssam	aqm_reg_write(sc, IX_QMGR_QUELOWSTAT0_OFFSET,
1037164426Ssam		IX_QMGR_QUELOWSTAT_RESET_VALUE);
1038164426Ssam	aqm_reg_write(sc, IX_QMGR_QUELOWSTAT1_OFFSET,
1039164426Ssam		IX_QMGR_QUELOWSTAT_RESET_VALUE);
1040164426Ssam	aqm_reg_write(sc, IX_QMGR_QUELOWSTAT2_OFFSET,
1041164426Ssam		IX_QMGR_QUELOWSTAT_RESET_VALUE);
1042164426Ssam	aqm_reg_write(sc, IX_QMGR_QUELOWSTAT3_OFFSET,
1043164426Ssam		IX_QMGR_QUELOWSTAT_RESET_VALUE);
1044164426Ssam
1045164426Ssam	/* Reset underflow/overflow status registers 0..1 */
1046164426Ssam	aqm_reg_write(sc, IX_QMGR_QUEUOSTAT0_OFFSET,
1047164426Ssam		IX_QMGR_QUEUOSTAT_RESET_VALUE);
1048164426Ssam	aqm_reg_write(sc, IX_QMGR_QUEUOSTAT1_OFFSET,
1049164426Ssam		IX_QMGR_QUEUOSTAT_RESET_VALUE);
1050164426Ssam
1051164426Ssam	/* Reset queues 32..63 nearly empty status registers */
1052164426Ssam	aqm_reg_write(sc, IX_QMGR_QUEUPPSTAT0_OFFSET,
1053164426Ssam		IX_QMGR_QUEUPPSTAT0_RESET_VALUE);
1054164426Ssam
1055164426Ssam	/* Reset queues 32..63 full status registers */
1056164426Ssam	aqm_reg_write(sc, IX_QMGR_QUEUPPSTAT1_OFFSET,
1057164426Ssam		IX_QMGR_QUEUPPSTAT1_RESET_VALUE);
1058164426Ssam
1059164426Ssam	/* Reset int0 status flag source select registers 0..3 */
1060164426Ssam	aqm_reg_write(sc, IX_QMGR_INT0SRCSELREG0_OFFSET,
1061164426Ssam			     IX_QMGR_INT0SRCSELREG_RESET_VALUE);
1062164426Ssam	aqm_reg_write(sc, IX_QMGR_INT0SRCSELREG1_OFFSET,
1063164426Ssam			     IX_QMGR_INT0SRCSELREG_RESET_VALUE);
1064164426Ssam	aqm_reg_write(sc, IX_QMGR_INT0SRCSELREG2_OFFSET,
1065164426Ssam			     IX_QMGR_INT0SRCSELREG_RESET_VALUE);
1066164426Ssam	aqm_reg_write(sc, IX_QMGR_INT0SRCSELREG3_OFFSET,
1067164426Ssam			     IX_QMGR_INT0SRCSELREG_RESET_VALUE);
1068164426Ssam
1069164426Ssam	/* Reset queue interrupt enable register 0..1 */
1070164426Ssam	aqm_reg_write(sc, IX_QMGR_QUEIEREG0_OFFSET,
1071164426Ssam		IX_QMGR_QUEIEREG_RESET_VALUE);
1072164426Ssam	aqm_reg_write(sc, IX_QMGR_QUEIEREG1_OFFSET,
1073164426Ssam		IX_QMGR_QUEIEREG_RESET_VALUE);
1074164426Ssam
1075164426Ssam	/* Reset queue interrupt register 0..1 */
1076164426Ssam	aqm_reg_write(sc, IX_QMGR_QINTREG0_OFFSET, IX_QMGR_QINTREG_RESET_VALUE);
1077164426Ssam	aqm_reg_write(sc, IX_QMGR_QINTREG1_OFFSET, IX_QMGR_QINTREG_RESET_VALUE);
1078164426Ssam
1079164426Ssam	/* Reset queue configuration words 0..63 */
1080164426Ssam	for (i = 0; i < IX_QMGR_MAX_NUM_QUEUES; i++)
1081164426Ssam	    aqm_reg_write(sc, sc->qinfo[i].qConfigRegAddr,
1082164426Ssam		IX_QMGR_QUECONFIG_RESET_VALUE);
1083164426Ssam
1084164426Ssam	/* XXX zero SRAM to simplify debugging */
1085164426Ssam	for (i = IX_QMGR_QUEBUFFER_SPACE_OFFSET;
1086164426Ssam	     i < IX_QMGR_AQM_SRAM_SIZE_IN_BYTES; i += sizeof(uint32_t))
1087164426Ssam	    aqm_reg_write(sc, i, 0);
1088164426Ssam}
1089164426Ssam
1090164426Ssamstatic device_method_t ixpqmgr_methods[] = {
1091164426Ssam	DEVMETHOD(device_probe,		ixpqmgr_probe),
1092164426Ssam	DEVMETHOD(device_attach,	ixpqmgr_attach),
1093164426Ssam	DEVMETHOD(device_detach,	ixpqmgr_detach),
1094164426Ssam
1095164426Ssam	{ 0, 0 }
1096164426Ssam};
1097164426Ssam
1098164426Ssamstatic driver_t ixpqmgr_driver = {
1099164426Ssam	"ixpqmgr",
1100164426Ssam	ixpqmgr_methods,
1101164426Ssam	sizeof(struct ixpqmgr_softc),
1102164426Ssam};
1103164426Ssamstatic devclass_t ixpqmgr_devclass;
1104164426Ssam
1105164426SsamDRIVER_MODULE(ixpqmgr, ixp, ixpqmgr_driver, ixpqmgr_devclass, 0, 0);
1106