1*ac7141acSskrll /* $NetBSD: ixp425_qmgr.c,v 1.12 2022/09/27 06:12:58 skrll Exp $ */
2bdea1361Sscw
3bdea1361Sscw /*-
4bdea1361Sscw * Copyright (c) 2006 Sam Leffler, Errno Consulting
5bdea1361Sscw * All rights reserved.
6bdea1361Sscw *
7bdea1361Sscw * Redistribution and use in source and binary forms, with or without
8bdea1361Sscw * modification, are permitted provided that the following conditions
9bdea1361Sscw * are met:
10bdea1361Sscw * 1. Redistributions of source code must retain the above copyright
11bdea1361Sscw * notice, this list of conditions and the following disclaimer,
12bdea1361Sscw * without modification.
13bdea1361Sscw * 2. Redistributions in binary form must reproduce at minimum a disclaimer
14bdea1361Sscw * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
15bdea1361Sscw * redistribution must be conditioned upon including a substantially
16bdea1361Sscw * similar Disclaimer requirement for further binary redistribution.
17bdea1361Sscw *
18bdea1361Sscw * NO WARRANTY
19bdea1361Sscw * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20bdea1361Sscw * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21bdea1361Sscw * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
22bdea1361Sscw * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23bdea1361Sscw * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
24bdea1361Sscw * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25bdea1361Sscw * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26bdea1361Sscw * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
27bdea1361Sscw * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28bdea1361Sscw * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29bdea1361Sscw * THE POSSIBILITY OF SUCH DAMAGES.
30bdea1361Sscw */
31bdea1361Sscw
32bdea1361Sscw /*-
33bdea1361Sscw * Copyright (c) 2001-2005, Intel Corporation.
34bdea1361Sscw * All rights reserved.
35bdea1361Sscw *
36bdea1361Sscw * Redistribution and use in source and binary forms, with or without
37bdea1361Sscw * modification, are permitted provided that the following conditions
38bdea1361Sscw * are met:
39bdea1361Sscw * 1. Redistributions of source code must retain the above copyright
40bdea1361Sscw * notice, this list of conditions and the following disclaimer.
41bdea1361Sscw * 2. Redistributions in binary form must reproduce the above copyright
42bdea1361Sscw * notice, this list of conditions and the following disclaimer in the
43bdea1361Sscw * documentation and/or other materials provided with the distribution.
44bdea1361Sscw * 3. Neither the name of the Intel Corporation nor the names of its contributors
45bdea1361Sscw * may be used to endorse or promote products derived from this software
46bdea1361Sscw * without specific prior written permission.
47bdea1361Sscw *
48bdea1361Sscw *
49bdea1361Sscw * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
50bdea1361Sscw * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51bdea1361Sscw * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52bdea1361Sscw * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
53bdea1361Sscw * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54bdea1361Sscw * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55bdea1361Sscw * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56bdea1361Sscw * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57bdea1361Sscw * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58bdea1361Sscw * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59bdea1361Sscw * SUCH DAMAGE.
60bdea1361Sscw */
61bdea1361Sscw #include <sys/cdefs.h>
62bdea1361Sscw /*__FBSDID("$FreeBSD: src/sys/arm/xscale/ixp425/ixp425_qmgr.c,v 1.1 2006/11/19 23:55:23 sam Exp $");*/
63*ac7141acSskrll __KERNEL_RCSID(0, "$NetBSD: ixp425_qmgr.c,v 1.12 2022/09/27 06:12:58 skrll Exp $");
64bdea1361Sscw
65bdea1361Sscw /*
66bdea1361Sscw * Intel XScale Queue Manager support.
67bdea1361Sscw *
68bdea1361Sscw * Each IXP4XXX device has a hardware block that implements a priority
69bdea1361Sscw * queue manager that is shared between the XScale cpu and the backend
70bdea1361Sscw * devices (such as the NPE). Queues are accessed by reading/writing
71bdea1361Sscw * special memory locations. The queue contents are mapped into a shared
72bdea1361Sscw * SRAM region with entries managed in a circular buffer. The XScale
73bdea1361Sscw * processor can receive interrupts based on queue contents (a condition
74bdea1361Sscw * code determines when interrupts should be delivered).
75bdea1361Sscw *
76bdea1361Sscw * The code here basically replaces the qmgr class in the Intel Access
77bdea1361Sscw * Library (IAL).
78bdea1361Sscw */
79bdea1361Sscw #include <sys/param.h>
80bdea1361Sscw #include <sys/systm.h>
81bdea1361Sscw #include <sys/kernel.h>
82bdea1361Sscw #include <sys/time.h>
83*ac7141acSskrll #include <sys/kmem.h>
84bdea1361Sscw #include <sys/resource.h>
85bdea1361Sscw
86ed9977b1Sdyoung #include <sys/bus.h>
87bdea1361Sscw #include <machine/cpu.h>
88bdea1361Sscw #include <machine/intr.h>
89bdea1361Sscw
90bdea1361Sscw #include <arm/xscale/ixp425reg.h>
91bdea1361Sscw #include <arm/xscale/ixp425var.h>
92bdea1361Sscw
93bdea1361Sscw #include <arm/xscale/ixp425_qmgr.h>
94bdea1361Sscw
95bdea1361Sscw /*
96bdea1361Sscw * State per AQM hw queue.
97bdea1361Sscw * This structure holds q configuration and dispatch state.
98bdea1361Sscw */
99bdea1361Sscw struct qmgrInfo {
100bdea1361Sscw int qSizeInWords; /* queue size in words */
101bdea1361Sscw
102bdea1361Sscw uint32_t qOflowStatBitMask; /* overflow status mask */
103bdea1361Sscw int qWriteCount; /* queue write count */
104bdea1361Sscw
105bdea1361Sscw bus_size_t qAccRegAddr; /* access register */
106bdea1361Sscw bus_size_t qUOStatRegAddr; /* status register */
107bdea1361Sscw bus_size_t qConfigRegAddr; /* config register */
108bdea1361Sscw int qSizeInEntries; /* queue size in entries */
109bdea1361Sscw
110bdea1361Sscw uint32_t qUflowStatBitMask; /* underflow status mask */
111bdea1361Sscw int qReadCount; /* queue read count */
112bdea1361Sscw
113bdea1361Sscw /* XXX union */
114bdea1361Sscw uint32_t qStatRegAddr;
115bdea1361Sscw uint32_t qStatBitsOffset;
116bdea1361Sscw uint32_t qStat0BitMask;
117bdea1361Sscw uint32_t qStat1BitMask;
118bdea1361Sscw
119bdea1361Sscw uint32_t intRegCheckMask; /* interrupt reg check mask */
120bdea1361Sscw void (*cb)(int, void *); /* callback function */
121bdea1361Sscw void *cbarg; /* callback argument */
122bdea1361Sscw int priority; /* dispatch priority */
123bdea1361Sscw #if 0
124bdea1361Sscw /* NB: needed only for A0 parts */
125bdea1361Sscw u_int statusWordOffset; /* status word offset */
126bdea1361Sscw uint32_t statusMask; /* status mask */
127bdea1361Sscw uint32_t statusCheckValue; /* status check value */
128bdea1361Sscw #endif
129bdea1361Sscw };
130bdea1361Sscw
131bdea1361Sscw struct ixpqmgr_softc {
132bdea1361Sscw #ifdef __FreeBSD__
133bdea1361Sscw device_t sc_dev;
134bdea1361Sscw bus_space_tag_t sc_iot;
135bdea1361Sscw bus_space_handle_t sc_ioh;
136bdea1361Sscw struct resource *sc_irq; /* IRQ resource */
137bdea1361Sscw int sc_rid; /* resource id for irq */
138bdea1361Sscw void *sc_ih; /* interrupt handler */
139bdea1361Sscw #else
140bdea1361Sscw bus_space_tag_t sc_iot;
141bdea1361Sscw bus_space_handle_t sc_ioh;
142bdea1361Sscw void *sc_ih[2]; /* interrupt handler */
143bdea1361Sscw #endif
144bdea1361Sscw
145bdea1361Sscw struct qmgrInfo qinfo[IX_QMGR_MAX_NUM_QUEUES];
146bdea1361Sscw /*
147bdea1361Sscw * This array contains a list of queue identifiers ordered by
148bdea1361Sscw * priority. The table is split logically between queue
149bdea1361Sscw * identifiers 0-31 and 32-63. To optimize lookups bit masks
150bdea1361Sscw * are kept for the first-32 and last-32 q's. When the
151bdea1361Sscw * table needs to be rebuilt mark rebuildTable and it'll
152bdea1361Sscw * happen after the next interrupt.
153bdea1361Sscw */
154bdea1361Sscw int priorityTable[IX_QMGR_MAX_NUM_QUEUES];
155bdea1361Sscw uint32_t lowPriorityTableFirstHalfMask;
156bdea1361Sscw uint32_t uppPriorityTableFirstHalfMask;
157bdea1361Sscw int rebuildTable; /* rebuild priorityTable */
158bdea1361Sscw
159bdea1361Sscw uint32_t aqmFreeSramAddress; /* SRAM free space */
160bdea1361Sscw };
161bdea1361Sscw
162bdea1361Sscw static int qmgr_debug = 0;
163bdea1361Sscw #define DPRINTF(dev, fmt, ...) do { \
164bdea1361Sscw if (qmgr_debug) printf(fmt, __VA_ARGS__); \
165bdea1361Sscw } while (0)
166bdea1361Sscw #define DPRINTFn(n, dev, fmt, ...) do { \
167bdea1361Sscw if (qmgr_debug >= n) printf(fmt, __VA_ARGS__); \
168bdea1361Sscw } while (0)
169bdea1361Sscw
170bdea1361Sscw static struct ixpqmgr_softc *ixpqmgr_sc = NULL;
171bdea1361Sscw
172bdea1361Sscw static void ixpqmgr_rebuild(struct ixpqmgr_softc *);
173bdea1361Sscw static int ixpqmgr_intr(void *);
174bdea1361Sscw
175bdea1361Sscw static void aqm_int_enable(struct ixpqmgr_softc *sc, int qId);
176bdea1361Sscw static void aqm_int_disable(struct ixpqmgr_softc *sc, int qId);
177bdea1361Sscw static void aqm_qcfg(struct ixpqmgr_softc *sc, int qId, u_int ne, u_int nf);
178bdea1361Sscw static void aqm_srcsel_write(struct ixpqmgr_softc *sc, int qId, int sourceId);
179bdea1361Sscw static void aqm_reset(struct ixpqmgr_softc *sc);
180bdea1361Sscw
181bdea1361Sscw static void
dummyCallback(int qId,void * arg)182bdea1361Sscw dummyCallback(int qId, void *arg)
183bdea1361Sscw {
184bdea1361Sscw /* XXX complain */
185bdea1361Sscw }
186bdea1361Sscw
187bdea1361Sscw static uint32_t
aqm_reg_read(struct ixpqmgr_softc * sc,bus_size_t off)188bdea1361Sscw aqm_reg_read(struct ixpqmgr_softc *sc, bus_size_t off)
189bdea1361Sscw {
190bdea1361Sscw DPRINTFn(9, sc->sc_dev, "%s(0x%x)\n", __func__, (int)off);
191bdea1361Sscw return bus_space_read_4(sc->sc_iot, sc->sc_ioh, off);
192bdea1361Sscw }
193bdea1361Sscw
194bdea1361Sscw static void
aqm_reg_write(struct ixpqmgr_softc * sc,bus_size_t off,uint32_t val)195bdea1361Sscw aqm_reg_write(struct ixpqmgr_softc *sc, bus_size_t off, uint32_t val)
196bdea1361Sscw {
197bdea1361Sscw DPRINTFn(9, sc->sc_dev, "%s(0x%x, 0x%x)\n", __func__, (int)off, val);
198bdea1361Sscw bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, val);
199bdea1361Sscw }
200bdea1361Sscw
201bdea1361Sscw #ifdef __FreeBSD__
202bdea1361Sscw static int
ixpqmgr_probe(device_t dev)203bdea1361Sscw ixpqmgr_probe(device_t dev)
204bdea1361Sscw {
205bdea1361Sscw device_set_desc(dev, "IXP425 Q-Manager");
206bdea1361Sscw return 0;
207bdea1361Sscw }
208bdea1361Sscw #endif
209bdea1361Sscw
210bdea1361Sscw #ifdef __FreeBSD__
211bdea1361Sscw static void
ixpqmgr_attach(device_t dev)212bdea1361Sscw ixpqmgr_attach(device_t dev)
213bdea1361Sscw #else
214bdea1361Sscw void *
215bdea1361Sscw ixpqmgr_init(bus_space_tag_t iot)
216bdea1361Sscw #endif
217bdea1361Sscw {
218bdea1361Sscw #ifdef __FreeBSD__
219bdea1361Sscw struct ixpqmgr_softc *sc = device_get_softc(dev);
220bdea1361Sscw struct ixp425_softc *sa = device_get_softc(device_get_parent(dev));
221bdea1361Sscw #else
222bdea1361Sscw struct ixpqmgr_softc *sc;
223bdea1361Sscw #endif
224bdea1361Sscw int i;
225bdea1361Sscw
226bdea1361Sscw #ifdef __FreeBSD__
227bdea1361Sscw ixpqmgr_sc = sc;
228bdea1361Sscw
229bdea1361Sscw sc->sc_dev = dev;
230bdea1361Sscw sc->sc_iot = sa->sc_iot;
231bdea1361Sscw #else
232*ac7141acSskrll sc = kmem_zalloc(sizeof(*sc), KM_SLEEP);
233bdea1361Sscw sc->sc_iot = iot;
234bdea1361Sscw #endif
235bdea1361Sscw
236bdea1361Sscw if (bus_space_map(sc->sc_iot, IXP425_QMGR_HWBASE, IXP425_QMGR_SIZE,
237bdea1361Sscw 0, &sc->sc_ioh))
238bdea1361Sscw panic("%s: Cannot map registers", __func__);
239bdea1361Sscw
240bdea1361Sscw #ifdef __FreeBSD__
241bdea1361Sscw /* NB: we only use the lower 32 q's */
242bdea1361Sscw sc->sc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->sc_rid,
243bdea1361Sscw IXP425_INT_QUE1_32, IXP425_INT_QUE33_64, 2, RF_ACTIVE);
244bdea1361Sscw if (!sc->sc_irq)
245bdea1361Sscw panic("Unable to allocate the qmgr irqs.\n");
246bdea1361Sscw /* XXX could be a source of entropy */
247bdea1361Sscw bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE,
248bdea1361Sscw ixpqmgr_intr, NULL, &sc->sc_ih);
249bdea1361Sscw #else
25026603f78Sscw ixpqmgr_sc = sc;
251bdea1361Sscw sc->sc_ih[0] = ixp425_intr_establish(IXP425_INT_QUE1_32, IPL_NET,
252bdea1361Sscw ixpqmgr_intr, sc);
253bdea1361Sscw if (sc->sc_ih[0] == NULL) {
25426603f78Sscw ixpqmgr_sc = NULL;
255*ac7141acSskrll kmem_free(sc, sizeof(*sc));
256bdea1361Sscw return (NULL);
257bdea1361Sscw }
258bdea1361Sscw sc->sc_ih[1] = ixp425_intr_establish(IXP425_INT_QUE33_64, IPL_NET,
259bdea1361Sscw ixpqmgr_intr, sc);
260bdea1361Sscw if (sc->sc_ih[1] == NULL) {
261bdea1361Sscw ixp425_intr_disestablish(sc->sc_ih[0]);
26226603f78Sscw ixpqmgr_sc = NULL;
263*ac7141acSskrll kmem_free(sc, sizeof(*sc));
264bdea1361Sscw return (NULL);
265bdea1361Sscw }
266bdea1361Sscw #endif
267bdea1361Sscw
268bdea1361Sscw /* NB: softc is pre-zero'd */
269bdea1361Sscw for (i = 0; i < IX_QMGR_MAX_NUM_QUEUES; i++) {
270bdea1361Sscw struct qmgrInfo *qi = &sc->qinfo[i];
271bdea1361Sscw
272bdea1361Sscw qi->cb = dummyCallback;
273bdea1361Sscw qi->priority = IX_QMGR_Q_PRIORITY_0; /* default priority */
274bdea1361Sscw /*
275bdea1361Sscw * There are two interrupt registers, 32 bits each. One
276bdea1361Sscw * for the lower queues(0-31) and one for the upper
277bdea1361Sscw * queues(32-63). Therefore need to mod by 32 i.e the
278bdea1361Sscw * min upper queue identifier.
279bdea1361Sscw */
280bdea1361Sscw qi->intRegCheckMask = (1<<(i%(IX_QMGR_MIN_QUEUPP_QID)));
281bdea1361Sscw
282bdea1361Sscw /*
283bdea1361Sscw * Register addresses and bit masks are calculated and
284bdea1361Sscw * stored here to optimize QRead, QWrite and QStatusGet
285bdea1361Sscw * functions.
286bdea1361Sscw */
287bdea1361Sscw
288bdea1361Sscw /* AQM Queue access reg addresses, per queue */
289bdea1361Sscw qi->qAccRegAddr = IX_QMGR_Q_ACCESS_ADDR_GET(i);
290bdea1361Sscw qi->qAccRegAddr = IX_QMGR_Q_ACCESS_ADDR_GET(i);
291bdea1361Sscw qi->qConfigRegAddr = IX_QMGR_Q_CONFIG_ADDR_GET(i);
292bdea1361Sscw
293bdea1361Sscw /* AQM Queue lower-group (0-31), only */
294bdea1361Sscw if (i < IX_QMGR_MIN_QUEUPP_QID) {
295bdea1361Sscw /* AQM Q underflow/overflow status reg address, per queue */
296bdea1361Sscw qi->qUOStatRegAddr = IX_QMGR_QUEUOSTAT0_OFFSET +
297bdea1361Sscw ((i / IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD) *
298bdea1361Sscw sizeof(uint32_t));
299bdea1361Sscw
300bdea1361Sscw /* AQM Q underflow status bit masks for status reg per queue */
301bdea1361Sscw qi->qUflowStatBitMask =
302bdea1361Sscw (IX_QMGR_UNDERFLOW_BIT_OFFSET + 1) <<
303bdea1361Sscw ((i & (IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD - 1)) *
304bdea1361Sscw (32 / IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD));
305bdea1361Sscw
306bdea1361Sscw /* AQM Q overflow status bit masks for status reg, per queue */
307bdea1361Sscw qi->qOflowStatBitMask =
308bdea1361Sscw (IX_QMGR_OVERFLOW_BIT_OFFSET + 1) <<
309bdea1361Sscw ((i & (IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD - 1)) *
310bdea1361Sscw (32 / IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD));
311bdea1361Sscw
312bdea1361Sscw /* AQM Q lower-group (0-31) status reg addresses, per queue */
313bdea1361Sscw qi->qStatRegAddr = IX_QMGR_QUELOWSTAT0_OFFSET +
314bdea1361Sscw ((i / IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD) *
315bdea1361Sscw sizeof(uint32_t));
316bdea1361Sscw
317bdea1361Sscw /* AQM Q lower-group (0-31) status register bit offset */
318bdea1361Sscw qi->qStatBitsOffset =
319bdea1361Sscw (i & (IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD - 1)) *
320bdea1361Sscw (32 / IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD);
321bdea1361Sscw } else { /* AQM Q upper-group (32-63), only */
322bdea1361Sscw qi->qUOStatRegAddr = 0; /* XXX */
323bdea1361Sscw
324bdea1361Sscw /* AQM Q upper-group (32-63) Nearly Empty status reg bitmasks */
325bdea1361Sscw qi->qStat0BitMask = (1 << (i - IX_QMGR_MIN_QUEUPP_QID));
326bdea1361Sscw
327bdea1361Sscw /* AQM Q upper-group (32-63) Full status register bitmasks */
328bdea1361Sscw qi->qStat1BitMask = (1 << (i - IX_QMGR_MIN_QUEUPP_QID));
329bdea1361Sscw }
330bdea1361Sscw }
331bdea1361Sscw
332bdea1361Sscw sc->aqmFreeSramAddress = 0x100; /* Q buffer space starts at 0x2100 */
333bdea1361Sscw
334a5effc3cSmsaitoh ixpqmgr_rebuild(sc); /* build initial priority table */
335bdea1361Sscw aqm_reset(sc); /* reset h/w */
336bdea1361Sscw
337bdea1361Sscw return (sc);
338bdea1361Sscw }
339bdea1361Sscw
340bdea1361Sscw #ifdef __FreeBSD__
341bdea1361Sscw static void
ixpqmgr_detach(device_t dev)342bdea1361Sscw ixpqmgr_detach(device_t dev)
343bdea1361Sscw {
344bdea1361Sscw struct ixpqmgr_softc *sc = device_get_softc(dev);
345bdea1361Sscw
346bdea1361Sscw aqm_reset(sc); /* disable interrupts */
347bdea1361Sscw bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
348bdea1361Sscw bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid, sc->sc_irq);
349bdea1361Sscw bus_space_unmap(sc->sc_iot, sc->sc_ioh, IXP425_QMGR_SIZE);
350bdea1361Sscw }
351bdea1361Sscw #endif
352bdea1361Sscw
353bdea1361Sscw int
ixpqmgr_qconfig(int qId,int qEntries,int ne,int nf,int srcSel,void (* cb)(int,void *),void * cbarg)354bdea1361Sscw ixpqmgr_qconfig(int qId, int qEntries, int ne, int nf, int srcSel,
355bdea1361Sscw void (*cb)(int, void *), void *cbarg)
356bdea1361Sscw {
357bdea1361Sscw struct ixpqmgr_softc *sc = ixpqmgr_sc;
358bdea1361Sscw struct qmgrInfo *qi = &sc->qinfo[qId];
359bdea1361Sscw
360bdea1361Sscw DPRINTF(sc->sc_dev, "%s(%u, %u, %u, %u, %u, %p, %p)\n",
361bdea1361Sscw __func__, qId, qEntries, ne, nf, srcSel, cb, cbarg);
362bdea1361Sscw
363bdea1361Sscw /* NB: entry size is always 1 */
364bdea1361Sscw qi->qSizeInWords = qEntries;
365bdea1361Sscw
366bdea1361Sscw qi->qReadCount = 0;
367bdea1361Sscw qi->qWriteCount = 0;
368bdea1361Sscw qi->qSizeInEntries = qEntries; /* XXX kept for code clarity */
369bdea1361Sscw
370bdea1361Sscw if (cb == NULL) {
371bdea1361Sscw /* Reset to dummy callback */
372bdea1361Sscw qi->cb = dummyCallback;
373bdea1361Sscw qi->cbarg = 0;
374bdea1361Sscw } else {
375bdea1361Sscw qi->cb = cb;
376bdea1361Sscw qi->cbarg = cbarg;
377bdea1361Sscw }
378bdea1361Sscw
379bdea1361Sscw /* Write the config register; NB must be AFTER qinfo setup */
380bdea1361Sscw aqm_qcfg(sc, qId, ne, nf);
381bdea1361Sscw /*
382bdea1361Sscw * Account for space just allocated to queue.
383bdea1361Sscw */
384bdea1361Sscw sc->aqmFreeSramAddress += (qi->qSizeInWords * sizeof(uint32_t));
385bdea1361Sscw
386f9ccf64bSmsaitoh /* Set the interrupt source if this queue is in the range 0-31 */
387bdea1361Sscw if (qId < IX_QMGR_MIN_QUEUPP_QID)
388bdea1361Sscw aqm_srcsel_write(sc, qId, srcSel);
389bdea1361Sscw
390bdea1361Sscw if (cb != NULL) /* Enable the interrupt */
391bdea1361Sscw aqm_int_enable(sc, qId);
392bdea1361Sscw
393ab87c666Sthorpej sc->rebuildTable = true;
394bdea1361Sscw
395bdea1361Sscw return 0; /* XXX */
396bdea1361Sscw }
397bdea1361Sscw
398bdea1361Sscw int
ixpqmgr_qwrite(int qId,uint32_t entry)399bdea1361Sscw ixpqmgr_qwrite(int qId, uint32_t entry)
400bdea1361Sscw {
401bdea1361Sscw struct ixpqmgr_softc *sc = ixpqmgr_sc;
402bdea1361Sscw struct qmgrInfo *qi = &sc->qinfo[qId];
403bdea1361Sscw
404bdea1361Sscw DPRINTFn(3, sc->sc_dev, "%s(%u, 0x%x) writeCount %u size %u\n",
405bdea1361Sscw __func__, qId, entry, qi->qWriteCount, qi->qSizeInEntries);
406bdea1361Sscw
407bdea1361Sscw /* write the entry */
408bdea1361Sscw aqm_reg_write(sc, qi->qAccRegAddr, entry);
409bdea1361Sscw
410bdea1361Sscw /* NB: overflow is available for lower queues only */
411bdea1361Sscw if (qId < IX_QMGR_MIN_QUEUPP_QID) {
412bdea1361Sscw int qSize = qi->qSizeInEntries;
413bdea1361Sscw /*
414bdea1361Sscw * Increment the current number of entries in the queue
415bdea1361Sscw * and check for overflow .
416bdea1361Sscw */
417bdea1361Sscw if (qi->qWriteCount++ == qSize) { /* check for overflow */
418bdea1361Sscw uint32_t status = aqm_reg_read(sc, qi->qUOStatRegAddr);
419bdea1361Sscw int qPtrs;
420bdea1361Sscw
421bdea1361Sscw /*
422bdea1361Sscw * Read the status twice because the status may
423bdea1361Sscw * not be immediately ready after the write operation
424bdea1361Sscw */
425bdea1361Sscw if ((status & qi->qOflowStatBitMask) ||
426bdea1361Sscw ((status = aqm_reg_read(sc, qi->qUOStatRegAddr)) & qi->qOflowStatBitMask)) {
427bdea1361Sscw /*
428bdea1361Sscw * The queue is full, clear the overflow status bit if set.
429bdea1361Sscw */
430bdea1361Sscw aqm_reg_write(sc, qi->qUOStatRegAddr,
431bdea1361Sscw status & ~qi->qOflowStatBitMask);
432bdea1361Sscw qi->qWriteCount = qSize;
433bdea1361Sscw DPRINTFn(5, sc->sc_dev,
434bdea1361Sscw "%s(%u, 0x%x) Q full, overflow status cleared\n",
435bdea1361Sscw __func__, qId, entry);
436bdea1361Sscw return ENOSPC;
437bdea1361Sscw }
438bdea1361Sscw /*
439cbf5c65aSandvar * No overflow occurred : someone is draining the queue
440bdea1361Sscw * and the current counter needs to be
441bdea1361Sscw * updated from the current number of entries in the queue
442bdea1361Sscw */
443bdea1361Sscw
444bdea1361Sscw /* calculate number of words in q */
445bdea1361Sscw qPtrs = aqm_reg_read(sc, qi->qConfigRegAddr);
446bdea1361Sscw DPRINTFn(2, sc->sc_dev,
447bdea1361Sscw "%s(%u, 0x%x) Q full, no overflow status, qConfig 0x%x\n",
448bdea1361Sscw __func__, qId, entry, qPtrs);
449bdea1361Sscw qPtrs = (qPtrs - (qPtrs >> 7)) & 0x7f;
450bdea1361Sscw
451bdea1361Sscw if (qPtrs == 0) {
452bdea1361Sscw /*
453bdea1361Sscw * The queue may be full at the time of the
454bdea1361Sscw * snapshot. Next access will check
455bdea1361Sscw * the overflow status again.
456bdea1361Sscw */
457bdea1361Sscw qi->qWriteCount = qSize;
458bdea1361Sscw } else {
459bdea1361Sscw /* convert the number of words to a number of entries */
460bdea1361Sscw qi->qWriteCount = qPtrs & (qSize - 1);
461bdea1361Sscw }
462bdea1361Sscw }
463bdea1361Sscw }
464bdea1361Sscw return 0;
465bdea1361Sscw }
466bdea1361Sscw
467bdea1361Sscw int
ixpqmgr_qread(int qId,uint32_t * entry)468bdea1361Sscw ixpqmgr_qread(int qId, uint32_t *entry)
469bdea1361Sscw {
470bdea1361Sscw struct ixpqmgr_softc *sc = ixpqmgr_sc;
471bdea1361Sscw struct qmgrInfo *qi = &sc->qinfo[qId];
472bdea1361Sscw bus_size_t off = qi->qAccRegAddr;
473bdea1361Sscw
474bdea1361Sscw *entry = aqm_reg_read(sc, off);
475bdea1361Sscw
476bdea1361Sscw /*
477bdea1361Sscw * Reset the current read count : next access to the read function
478bdea1361Sscw * will force a underflow status check.
479bdea1361Sscw */
480bdea1361Sscw qi->qReadCount = 0;
481bdea1361Sscw
482bdea1361Sscw /* Check if underflow occurred on the read */
483bdea1361Sscw if (*entry == 0 && qId < IX_QMGR_MIN_QUEUPP_QID) {
484bdea1361Sscw /* get the queue status */
485bdea1361Sscw uint32_t status = aqm_reg_read(sc, qi->qUOStatRegAddr);
486bdea1361Sscw
487bdea1361Sscw if (status & qi->qUflowStatBitMask) { /* clear underflow status */
488bdea1361Sscw aqm_reg_write(sc, qi->qUOStatRegAddr,
489bdea1361Sscw status &~ qi->qUflowStatBitMask);
490bdea1361Sscw return ENOSPC;
491bdea1361Sscw }
492bdea1361Sscw }
493bdea1361Sscw return 0;
494bdea1361Sscw }
495bdea1361Sscw
496bdea1361Sscw int
ixpqmgr_qreadm(int qId,uint32_t n,uint32_t * p)497bdea1361Sscw ixpqmgr_qreadm(int qId, uint32_t n, uint32_t *p)
498bdea1361Sscw {
499bdea1361Sscw struct ixpqmgr_softc *sc = ixpqmgr_sc;
500bdea1361Sscw struct qmgrInfo *qi = &sc->qinfo[qId];
501bdea1361Sscw uint32_t entry;
502bdea1361Sscw bus_size_t off = qi->qAccRegAddr;
503bdea1361Sscw
504bdea1361Sscw entry = aqm_reg_read(sc, off);
505bdea1361Sscw while (--n) {
506bdea1361Sscw if (entry == 0) {
507bdea1361Sscw /* if we read a NULL entry, stop. We have underflowed */
508bdea1361Sscw break;
509bdea1361Sscw }
510bdea1361Sscw *p++ = entry; /* store */
511bdea1361Sscw entry = aqm_reg_read(sc, off);
512bdea1361Sscw }
513bdea1361Sscw *p = entry;
514bdea1361Sscw
515bdea1361Sscw /*
516bdea1361Sscw * Reset the current read count : next access to the read function
517bdea1361Sscw * will force a underflow status check.
518bdea1361Sscw */
519bdea1361Sscw qi->qReadCount = 0;
520bdea1361Sscw
521bdea1361Sscw /* Check if underflow occurred on the read */
522bdea1361Sscw if (entry == 0 && qId < IX_QMGR_MIN_QUEUPP_QID) {
523bdea1361Sscw /* get the queue status */
524bdea1361Sscw uint32_t status = aqm_reg_read(sc, qi->qUOStatRegAddr);
525bdea1361Sscw
526bdea1361Sscw if (status & qi->qUflowStatBitMask) { /* clear underflow status */
527bdea1361Sscw aqm_reg_write(sc, qi->qUOStatRegAddr,
528bdea1361Sscw status &~ qi->qUflowStatBitMask);
529bdea1361Sscw return ENOSPC;
530bdea1361Sscw }
531bdea1361Sscw }
532bdea1361Sscw return 0;
533bdea1361Sscw }
534bdea1361Sscw
535bdea1361Sscw uint32_t
ixpqmgr_getqstatus(int qId)536bdea1361Sscw ixpqmgr_getqstatus(int qId)
537bdea1361Sscw {
538bdea1361Sscw #define QLOWSTATMASK \
539bdea1361Sscw ((1 << (32 / IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD)) - 1)
540bdea1361Sscw struct ixpqmgr_softc *sc = ixpqmgr_sc;
541bdea1361Sscw const struct qmgrInfo *qi = &sc->qinfo[qId];
542bdea1361Sscw uint32_t status;
543bdea1361Sscw
544bdea1361Sscw if (qId < IX_QMGR_MIN_QUEUPP_QID) {
545bdea1361Sscw /* read the status of a queue in the range 0-31 */
546bdea1361Sscw status = aqm_reg_read(sc, qi->qStatRegAddr);
547bdea1361Sscw
548bdea1361Sscw /* mask out the status bits relevant only to this queue */
549bdea1361Sscw status = (status >> qi->qStatBitsOffset) & QLOWSTATMASK;
550bdea1361Sscw } else { /* read status of a queue in the range 32-63 */
551bdea1361Sscw status = 0;
552bdea1361Sscw if (aqm_reg_read(sc, IX_QMGR_QUEUPPSTAT0_OFFSET)&qi->qStat0BitMask)
553bdea1361Sscw status |= IX_QMGR_Q_STATUS_NE_BIT_MASK; /* nearly empty */
554bdea1361Sscw if (aqm_reg_read(sc, IX_QMGR_QUEUPPSTAT1_OFFSET)&qi->qStat1BitMask)
555bdea1361Sscw status |= IX_QMGR_Q_STATUS_F_BIT_MASK; /* full */
556bdea1361Sscw }
557bdea1361Sscw return status;
558bdea1361Sscw #undef QLOWSTATMASK
559bdea1361Sscw }
560bdea1361Sscw
561bdea1361Sscw uint32_t
ixpqmgr_getqconfig(int qId)562bdea1361Sscw ixpqmgr_getqconfig(int qId)
563bdea1361Sscw {
564bdea1361Sscw struct ixpqmgr_softc *sc = ixpqmgr_sc;
565bdea1361Sscw
566bdea1361Sscw return aqm_reg_read(sc, IX_QMGR_Q_CONFIG_ADDR_GET(qId));
567bdea1361Sscw }
568bdea1361Sscw
569bdea1361Sscw void
ixpqmgr_dump(void)570bdea1361Sscw ixpqmgr_dump(void)
571bdea1361Sscw {
572bdea1361Sscw struct ixpqmgr_softc *sc = ixpqmgr_sc;
573bdea1361Sscw int i, a;
574bdea1361Sscw
575bdea1361Sscw /* status registers */
576bdea1361Sscw printf("0x%04x: %08x %08x %08x %08x\n"
577bdea1361Sscw , 0x400
578bdea1361Sscw , aqm_reg_read(sc, 0x400)
579bdea1361Sscw , aqm_reg_read(sc, 0x400+4)
580bdea1361Sscw , aqm_reg_read(sc, 0x400+8)
581bdea1361Sscw , aqm_reg_read(sc, 0x400+12)
582bdea1361Sscw );
583bdea1361Sscw printf("0x%04x: %08x %08x %08x %08x\n"
584bdea1361Sscw , 0x410
585bdea1361Sscw , aqm_reg_read(sc, 0x410)
586bdea1361Sscw , aqm_reg_read(sc, 0x410+4)
587bdea1361Sscw , aqm_reg_read(sc, 0x410+8)
588bdea1361Sscw , aqm_reg_read(sc, 0x410+12)
589bdea1361Sscw );
590bdea1361Sscw printf("0x%04x: %08x %08x %08x %08x\n"
591bdea1361Sscw , 0x420
592bdea1361Sscw , aqm_reg_read(sc, 0x420)
593bdea1361Sscw , aqm_reg_read(sc, 0x420+4)
594bdea1361Sscw , aqm_reg_read(sc, 0x420+8)
595bdea1361Sscw , aqm_reg_read(sc, 0x420+12)
596bdea1361Sscw );
597bdea1361Sscw printf("0x%04x: %08x %08x %08x %08x\n"
598bdea1361Sscw , 0x430
599bdea1361Sscw , aqm_reg_read(sc, 0x430)
600bdea1361Sscw , aqm_reg_read(sc, 0x430+4)
601bdea1361Sscw , aqm_reg_read(sc, 0x430+8)
602bdea1361Sscw , aqm_reg_read(sc, 0x430+12)
603bdea1361Sscw );
604bdea1361Sscw /* q configuration registers */
605bdea1361Sscw for (a = 0x2000; a < 0x20ff; a += 32)
606bdea1361Sscw printf("0x%04x: %08x %08x %08x %08x %08x %08x %08x %08x\n"
607bdea1361Sscw , a
608bdea1361Sscw , aqm_reg_read(sc, a)
609bdea1361Sscw , aqm_reg_read(sc, a+4)
610bdea1361Sscw , aqm_reg_read(sc, a+8)
611bdea1361Sscw , aqm_reg_read(sc, a+12)
612bdea1361Sscw , aqm_reg_read(sc, a+16)
613bdea1361Sscw , aqm_reg_read(sc, a+20)
614bdea1361Sscw , aqm_reg_read(sc, a+24)
615bdea1361Sscw , aqm_reg_read(sc, a+28)
616bdea1361Sscw );
617bdea1361Sscw /* allocated SRAM */
618bdea1361Sscw for (i = 0x100; i < sc->aqmFreeSramAddress; i += 32) {
619bdea1361Sscw a = 0x2000 + i;
620bdea1361Sscw printf("0x%04x: %08x %08x %08x %08x %08x %08x %08x %08x\n"
621bdea1361Sscw , a
622bdea1361Sscw , aqm_reg_read(sc, a)
623bdea1361Sscw , aqm_reg_read(sc, a+4)
624bdea1361Sscw , aqm_reg_read(sc, a+8)
625bdea1361Sscw , aqm_reg_read(sc, a+12)
626bdea1361Sscw , aqm_reg_read(sc, a+16)
627bdea1361Sscw , aqm_reg_read(sc, a+20)
628bdea1361Sscw , aqm_reg_read(sc, a+24)
629bdea1361Sscw , aqm_reg_read(sc, a+28)
630bdea1361Sscw );
631bdea1361Sscw }
632bdea1361Sscw for (i = 0; i < 16; i++) {
633bdea1361Sscw printf("Q[%2d] config 0x%08x status 0x%02x "
634bdea1361Sscw "Q[%2d] config 0x%08x status 0x%02x\n"
635bdea1361Sscw , i, ixpqmgr_getqconfig(i), ixpqmgr_getqstatus(i)
636bdea1361Sscw , i+16, ixpqmgr_getqconfig(i+16), ixpqmgr_getqstatus(i+16)
637bdea1361Sscw );
638bdea1361Sscw }
639bdea1361Sscw }
640bdea1361Sscw
641bdea1361Sscw void
ixpqmgr_notify_enable(int qId,int srcSel)642bdea1361Sscw ixpqmgr_notify_enable(int qId, int srcSel)
643bdea1361Sscw {
644bdea1361Sscw struct ixpqmgr_softc *sc = ixpqmgr_sc;
645bdea1361Sscw #if 0
646bdea1361Sscw /* Calculate the checkMask and checkValue for this q */
647bdea1361Sscw aqm_calc_statuscheck(sc, qId, srcSel);
648bdea1361Sscw #endif
6494f0b35e9Smsaitoh /* Set the interrupt source if this queue is in the range 0-31 */
650bdea1361Sscw if (qId < IX_QMGR_MIN_QUEUPP_QID)
651bdea1361Sscw aqm_srcsel_write(sc, qId, srcSel);
652bdea1361Sscw
653bdea1361Sscw /* Enable the interrupt */
654bdea1361Sscw aqm_int_enable(sc, qId);
655bdea1361Sscw }
656bdea1361Sscw
657bdea1361Sscw void
ixpqmgr_notify_disable(int qId)658bdea1361Sscw ixpqmgr_notify_disable(int qId)
659bdea1361Sscw {
660bdea1361Sscw struct ixpqmgr_softc *sc = ixpqmgr_sc;
661bdea1361Sscw
662bdea1361Sscw aqm_int_disable(sc, qId);
663bdea1361Sscw }
664bdea1361Sscw
665bdea1361Sscw /*
666bdea1361Sscw * Rebuild the priority table used by the dispatcher.
667bdea1361Sscw */
668bdea1361Sscw static void
ixpqmgr_rebuild(struct ixpqmgr_softc * sc)669bdea1361Sscw ixpqmgr_rebuild(struct ixpqmgr_softc *sc)
670bdea1361Sscw {
671bdea1361Sscw int q, pri;
672bdea1361Sscw int lowQuePriorityTableIndex, uppQuePriorityTableIndex;
673bdea1361Sscw struct qmgrInfo *qi;
674bdea1361Sscw
675bdea1361Sscw sc->lowPriorityTableFirstHalfMask = 0;
676bdea1361Sscw sc->uppPriorityTableFirstHalfMask = 0;
677bdea1361Sscw
678bdea1361Sscw lowQuePriorityTableIndex = 0;
679bdea1361Sscw uppQuePriorityTableIndex = 32;
680bdea1361Sscw for (pri = 0; pri < IX_QMGR_NUM_PRIORITY_LEVELS; pri++) {
681bdea1361Sscw /* low priority q's */
682bdea1361Sscw for (q = 0; q < IX_QMGR_MIN_QUEUPP_QID; q++) {
683bdea1361Sscw qi = &sc->qinfo[q];
684bdea1361Sscw if (qi->priority == pri) {
685bdea1361Sscw /*
686bdea1361Sscw * Build the priority table bitmask which match the
687bdea1361Sscw * queues of the first half of the priority table.
688bdea1361Sscw */
689bdea1361Sscw if (lowQuePriorityTableIndex < 16) {
690bdea1361Sscw sc->lowPriorityTableFirstHalfMask |=
691bdea1361Sscw qi->intRegCheckMask;
692bdea1361Sscw }
693bdea1361Sscw sc->priorityTable[lowQuePriorityTableIndex++] = q;
694bdea1361Sscw }
695bdea1361Sscw }
696bdea1361Sscw /* high priority q's */
697bdea1361Sscw for (; q < IX_QMGR_MAX_NUM_QUEUES; q++) {
698bdea1361Sscw qi = &sc->qinfo[q];
699bdea1361Sscw if (qi->priority == pri) {
700bdea1361Sscw /*
701bdea1361Sscw * Build the priority table bitmask which match the
702bdea1361Sscw * queues of the first half of the priority table .
703bdea1361Sscw */
704bdea1361Sscw if (uppQuePriorityTableIndex < 48) {
705bdea1361Sscw sc->uppPriorityTableFirstHalfMask |=
706bdea1361Sscw qi->intRegCheckMask;
707bdea1361Sscw }
708bdea1361Sscw sc->priorityTable[uppQuePriorityTableIndex++] = q;
709bdea1361Sscw }
710bdea1361Sscw }
711bdea1361Sscw }
712ab87c666Sthorpej sc->rebuildTable = false;
713bdea1361Sscw }
714bdea1361Sscw
715bdea1361Sscw /*
716bdea1361Sscw * Count the number of leading zero bits in a word,
717bdea1361Sscw * and return the same value than the CLZ instruction.
718bdea1361Sscw * Note this is similar to the standard ffs function but
719bdea1361Sscw * it counts zero's from the MSB instead of the LSB.
720bdea1361Sscw *
721bdea1361Sscw * word (in) return value (out)
722bdea1361Sscw * 0x80000000 0
723bdea1361Sscw * 0x40000000 1
724bdea1361Sscw * ,,, ,,,
725bdea1361Sscw * 0x00000002 30
726bdea1361Sscw * 0x00000001 31
727bdea1361Sscw * 0x00000000 32
728bdea1361Sscw *
729bdea1361Sscw * The C version of this function is used as a replacement
730bdea1361Sscw * for system not providing the equivalent of the CLZ
731bdea1361Sscw * assembly language instruction.
732bdea1361Sscw *
733bdea1361Sscw * Note that this version is big-endian
734bdea1361Sscw */
735bdea1361Sscw static unsigned int
_lzcount(uint32_t word)736bdea1361Sscw _lzcount(uint32_t word)
737bdea1361Sscw {
738bdea1361Sscw unsigned int lzcount = 0;
739bdea1361Sscw
740bdea1361Sscw if (word == 0)
741bdea1361Sscw return 32;
742bdea1361Sscw while ((word & 0x80000000) == 0) {
743bdea1361Sscw word <<= 1;
744bdea1361Sscw lzcount++;
745bdea1361Sscw }
746bdea1361Sscw return lzcount;
747bdea1361Sscw }
748bdea1361Sscw
749bdea1361Sscw static int
ixpqmgr_intr(void * arg)750bdea1361Sscw ixpqmgr_intr(void *arg)
751bdea1361Sscw {
752bdea1361Sscw struct ixpqmgr_softc *sc = ixpqmgr_sc;
753bdea1361Sscw uint32_t intRegVal; /* Interrupt reg val */
754bdea1361Sscw struct qmgrInfo *qi;
755bdea1361Sscw int priorityTableIndex; /* Priority table index */
756bdea1361Sscw int qIndex; /* Current queue being processed */
757bdea1361Sscw
758bdea1361Sscw /* Read the interrupt register */
759bdea1361Sscw intRegVal = aqm_reg_read(sc, IX_QMGR_QINTREG0_OFFSET);
760bdea1361Sscw /* Write back to clear interrupt */
761bdea1361Sscw aqm_reg_write(sc, IX_QMGR_QINTREG0_OFFSET, intRegVal);
762bdea1361Sscw
763bdea1361Sscw DPRINTFn(5, sc->sc_dev, "%s: ISR0 0x%x ISR1 0x%x\n",
764bdea1361Sscw __func__, intRegVal, aqm_reg_read(sc, IX_QMGR_QINTREG1_OFFSET));
765bdea1361Sscw
766bdea1361Sscw /* No queue has interrupt register set */
767bdea1361Sscw if (intRegVal != 0) {
768bdea1361Sscw /* get the first queue Id from the interrupt register value */
769bdea1361Sscw qIndex = (32 - 1) - _lzcount(intRegVal);
770bdea1361Sscw
771bdea1361Sscw DPRINTFn(2, sc->sc_dev, "%s: ISR0 0x%x qIndex %u\n",
772bdea1361Sscw __func__, intRegVal, qIndex);
773bdea1361Sscw
774bdea1361Sscw /*
775bdea1361Sscw * Optimize for single callback case.
776bdea1361Sscw */
777bdea1361Sscw qi = &sc->qinfo[qIndex];
778bdea1361Sscw if (intRegVal == qi->intRegCheckMask) {
779bdea1361Sscw /*
780bdea1361Sscw * Only 1 queue event triggered a notification.
781bdea1361Sscw * Call the callback function for this queue
782bdea1361Sscw */
783bdea1361Sscw qi->cb(qIndex, qi->cbarg);
784bdea1361Sscw } else {
785bdea1361Sscw /*
786bdea1361Sscw * The event is triggered by more than 1 queue,
787bdea1361Sscw * the queue search will start from the beginning
788bdea1361Sscw * or the middle of the priority table.
789bdea1361Sscw *
790bdea1361Sscw * The search will end when all the bits of the interrupt
791bdea1361Sscw * register are cleared. There is no need to maintain
7929ba4df15Smsaitoh * a separate value and test it at each iteration.
793bdea1361Sscw */
794bdea1361Sscw if (intRegVal & sc->lowPriorityTableFirstHalfMask) {
795bdea1361Sscw priorityTableIndex = 0;
796bdea1361Sscw } else {
797bdea1361Sscw priorityTableIndex = 16;
798bdea1361Sscw }
799bdea1361Sscw /*
800bdea1361Sscw * Iterate over the priority table until all the bits
801bdea1361Sscw * of the interrupt register are cleared.
802bdea1361Sscw */
803bdea1361Sscw do {
804bdea1361Sscw qIndex = sc->priorityTable[priorityTableIndex++];
80526603f78Sscw if (qIndex >= IX_QMGR_MAX_NUM_QUEUES)
80626603f78Sscw break;
807bdea1361Sscw qi = &sc->qinfo[qIndex];
808bdea1361Sscw
809bdea1361Sscw /* If this queue caused this interrupt to be raised */
810bdea1361Sscw if (intRegVal & qi->intRegCheckMask) {
811bdea1361Sscw /* Call the callback function for this queue */
812bdea1361Sscw qi->cb(qIndex, qi->cbarg);
813bdea1361Sscw /* Clear the interrupt register bit */
814bdea1361Sscw intRegVal &= ~qi->intRegCheckMask;
815bdea1361Sscw }
81626603f78Sscw } while (intRegVal &&
81726603f78Sscw priorityTableIndex < IX_QMGR_MAX_NUM_QUEUES);
818bdea1361Sscw }
819bdea1361Sscw }
820bdea1361Sscw
821bdea1361Sscw /* Rebuild the priority table if needed */
822bdea1361Sscw if (sc->rebuildTable)
823bdea1361Sscw ixpqmgr_rebuild(sc);
824bdea1361Sscw
825bdea1361Sscw return (1);
826bdea1361Sscw }
827bdea1361Sscw
828bdea1361Sscw #if 0
829bdea1361Sscw /*
830bdea1361Sscw * Generate the parameters used to check if a Q's status matches
831bdea1361Sscw * the specified source select. We calculate which status word
832bdea1361Sscw * to check (statusWordOffset), the value to check the status
833bdea1361Sscw * against (statusCheckValue) and the mask (statusMask) to mask
834bdea1361Sscw * out all but the bits to check in the status word.
835bdea1361Sscw */
836bdea1361Sscw static void
837bdea1361Sscw aqm_calc_statuscheck(int qId, IxQMgrSourceId srcSel)
838bdea1361Sscw {
839bdea1361Sscw struct qmgrInfo *qi = &qinfo[qId];
840bdea1361Sscw uint32_t shiftVal;
841bdea1361Sscw
842bdea1361Sscw if (qId < IX_QMGR_MIN_QUEUPP_QID) {
843bdea1361Sscw switch (srcSel) {
844bdea1361Sscw case IX_QMGR_Q_SOURCE_ID_E:
845bdea1361Sscw qi->statusCheckValue = IX_QMGR_Q_STATUS_E_BIT_MASK;
846bdea1361Sscw qi->statusMask = IX_QMGR_Q_STATUS_E_BIT_MASK;
847bdea1361Sscw break;
848bdea1361Sscw case IX_QMGR_Q_SOURCE_ID_NE:
849bdea1361Sscw qi->statusCheckValue = IX_QMGR_Q_STATUS_NE_BIT_MASK;
850bdea1361Sscw qi->statusMask = IX_QMGR_Q_STATUS_NE_BIT_MASK;
851bdea1361Sscw break;
852bdea1361Sscw case IX_QMGR_Q_SOURCE_ID_NF:
853bdea1361Sscw qi->statusCheckValue = IX_QMGR_Q_STATUS_NF_BIT_MASK;
854bdea1361Sscw qi->statusMask = IX_QMGR_Q_STATUS_NF_BIT_MASK;
855bdea1361Sscw break;
856bdea1361Sscw case IX_QMGR_Q_SOURCE_ID_F:
857bdea1361Sscw qi->statusCheckValue = IX_QMGR_Q_STATUS_F_BIT_MASK;
858bdea1361Sscw qi->statusMask = IX_QMGR_Q_STATUS_F_BIT_MASK;
859bdea1361Sscw break;
860bdea1361Sscw case IX_QMGR_Q_SOURCE_ID_NOT_E:
861bdea1361Sscw qi->statusCheckValue = 0;
862bdea1361Sscw qi->statusMask = IX_QMGR_Q_STATUS_E_BIT_MASK;
863bdea1361Sscw break;
864bdea1361Sscw case IX_QMGR_Q_SOURCE_ID_NOT_NE:
865bdea1361Sscw qi->statusCheckValue = 0;
866bdea1361Sscw qi->statusMask = IX_QMGR_Q_STATUS_NE_BIT_MASK;
867bdea1361Sscw break;
868bdea1361Sscw case IX_QMGR_Q_SOURCE_ID_NOT_NF:
869bdea1361Sscw qi->statusCheckValue = 0;
870bdea1361Sscw qi->statusMask = IX_QMGR_Q_STATUS_NF_BIT_MASK;
871bdea1361Sscw break;
872bdea1361Sscw case IX_QMGR_Q_SOURCE_ID_NOT_F:
873bdea1361Sscw qi->statusCheckValue = 0;
874bdea1361Sscw qi->statusMask = IX_QMGR_Q_STATUS_F_BIT_MASK;
875bdea1361Sscw break;
876bdea1361Sscw default:
877bdea1361Sscw /* Should never hit */
878bdea1361Sscw IX_OSAL_ASSERT(0);
879bdea1361Sscw break;
880bdea1361Sscw }
881bdea1361Sscw
882bdea1361Sscw /* One nibble of status per queue so need to shift the
883bdea1361Sscw * check value and mask out to the correct position.
884bdea1361Sscw */
885bdea1361Sscw shiftVal = (qId % IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD) *
886bdea1361Sscw IX_QMGR_QUELOWSTAT_BITS_PER_Q;
887bdea1361Sscw
888bdea1361Sscw /* Calculate the which status word to check from the qId,
889bdea1361Sscw * 8 Qs status per word
890bdea1361Sscw */
891bdea1361Sscw qi->statusWordOffset = qId / IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD;
892bdea1361Sscw
893bdea1361Sscw qi->statusCheckValue <<= shiftVal;
894bdea1361Sscw qi->statusMask <<= shiftVal;
895bdea1361Sscw } else {
896bdea1361Sscw /* One status word */
897bdea1361Sscw qi->statusWordOffset = 0;
898bdea1361Sscw /* Single bits per queue and int source bit hardwired NE,
899bdea1361Sscw * Qs start at 32.
900bdea1361Sscw */
901bdea1361Sscw qi->statusMask = 1 << (qId - IX_QMGR_MIN_QUEUPP_QID);
902bdea1361Sscw qi->statusCheckValue = qi->statusMask;
903bdea1361Sscw }
904bdea1361Sscw }
905bdea1361Sscw #endif
906bdea1361Sscw
907bdea1361Sscw static void
aqm_int_enable(struct ixpqmgr_softc * sc,int qId)908bdea1361Sscw aqm_int_enable(struct ixpqmgr_softc *sc, int qId)
909bdea1361Sscw {
910bdea1361Sscw bus_size_t reg;
911bdea1361Sscw uint32_t v;
912bdea1361Sscw
913bdea1361Sscw if (qId < IX_QMGR_MIN_QUEUPP_QID)
914bdea1361Sscw reg = IX_QMGR_QUEIEREG0_OFFSET;
915bdea1361Sscw else
916bdea1361Sscw reg = IX_QMGR_QUEIEREG1_OFFSET;
917bdea1361Sscw v = aqm_reg_read(sc, reg);
918bdea1361Sscw aqm_reg_write(sc, reg, v | (1 << (qId % IX_QMGR_MIN_QUEUPP_QID)));
919bdea1361Sscw
920bdea1361Sscw DPRINTF(sc->sc_dev, "%s(%u) 0x%lx: 0x%x => 0x%x\n",
921bdea1361Sscw __func__, qId, reg, v, aqm_reg_read(sc, reg));
922bdea1361Sscw }
923bdea1361Sscw
924bdea1361Sscw static void
aqm_int_disable(struct ixpqmgr_softc * sc,int qId)925bdea1361Sscw aqm_int_disable(struct ixpqmgr_softc *sc, int qId)
926bdea1361Sscw {
927bdea1361Sscw bus_size_t reg;
928bdea1361Sscw uint32_t v;
929bdea1361Sscw
930bdea1361Sscw if (qId < IX_QMGR_MIN_QUEUPP_QID)
931bdea1361Sscw reg = IX_QMGR_QUEIEREG0_OFFSET;
932bdea1361Sscw else
933bdea1361Sscw reg = IX_QMGR_QUEIEREG1_OFFSET;
934bdea1361Sscw v = aqm_reg_read(sc, reg);
935bdea1361Sscw aqm_reg_write(sc, reg, v &~ (1 << (qId % IX_QMGR_MIN_QUEUPP_QID)));
936bdea1361Sscw
937bdea1361Sscw DPRINTF(sc->sc_dev, "%s(%u) 0x%lx: 0x%x => 0x%x\n",
938bdea1361Sscw __func__, qId, reg, v, aqm_reg_read(sc, reg));
939bdea1361Sscw }
940bdea1361Sscw
941bdea1361Sscw static unsigned
log2(unsigned n)942bdea1361Sscw log2(unsigned n)
943bdea1361Sscw {
944bdea1361Sscw unsigned count;
945bdea1361Sscw /*
946bdea1361Sscw * N.B. this function will return 0 if supplied 0.
947bdea1361Sscw */
948bdea1361Sscw for (count = 0; n/2; count++)
949bdea1361Sscw n /= 2;
950bdea1361Sscw return count;
951bdea1361Sscw }
952bdea1361Sscw
953bdea1361Sscw static __inline unsigned
toAqmEntrySize(int entrySize)954bdea1361Sscw toAqmEntrySize(int entrySize)
955bdea1361Sscw {
956bdea1361Sscw /* entrySize 1("00"),2("01"),4("10") */
957bdea1361Sscw return log2(entrySize);
958bdea1361Sscw }
959bdea1361Sscw
960bdea1361Sscw static __inline unsigned
toAqmBufferSize(unsigned bufferSizeInWords)961bdea1361Sscw toAqmBufferSize(unsigned bufferSizeInWords)
962bdea1361Sscw {
963bdea1361Sscw /* bufferSize 16("00"),32("01),64("10"),128("11") */
964bdea1361Sscw return log2(bufferSizeInWords / IX_QMGR_MIN_BUFFER_SIZE);
965bdea1361Sscw }
966bdea1361Sscw
967bdea1361Sscw static __inline unsigned
toAqmWatermark(int watermark)968bdea1361Sscw toAqmWatermark(int watermark)
969bdea1361Sscw {
970bdea1361Sscw /*
971bdea1361Sscw * Watermarks 0("000"),1("001"),2("010"),4("011"),
972bdea1361Sscw * 8("100"),16("101"),32("110"),64("111")
973bdea1361Sscw */
974bdea1361Sscw return log2(2 * watermark);
975bdea1361Sscw }
976bdea1361Sscw
977bdea1361Sscw static void
aqm_qcfg(struct ixpqmgr_softc * sc,int qId,u_int ne,u_int nf)978bdea1361Sscw aqm_qcfg(struct ixpqmgr_softc *sc, int qId, u_int ne, u_int nf)
979bdea1361Sscw {
980bdea1361Sscw const struct qmgrInfo *qi = &sc->qinfo[qId];
981bdea1361Sscw uint32_t qCfg;
982bdea1361Sscw uint32_t baseAddress;
983bdea1361Sscw
984bdea1361Sscw /* Build config register */
985bdea1361Sscw qCfg = ((toAqmEntrySize(1) & IX_QMGR_ENTRY_SIZE_MASK) <<
986bdea1361Sscw IX_QMGR_Q_CONFIG_ESIZE_OFFSET)
987bdea1361Sscw | ((toAqmBufferSize(qi->qSizeInWords) & IX_QMGR_SIZE_MASK) <<
988bdea1361Sscw IX_QMGR_Q_CONFIG_BSIZE_OFFSET);
989bdea1361Sscw
990bdea1361Sscw /* baseAddress, calculated relative to start address */
991bdea1361Sscw baseAddress = sc->aqmFreeSramAddress;
992bdea1361Sscw
993bdea1361Sscw /* base address must be word-aligned */
994bdea1361Sscw KASSERT((baseAddress % IX_QMGR_BASE_ADDR_16_WORD_ALIGN) == 0);
995bdea1361Sscw
996bdea1361Sscw /* Now convert to a 16 word pointer as required by QUECONFIG register */
997bdea1361Sscw baseAddress >>= IX_QMGR_BASE_ADDR_16_WORD_SHIFT;
998bdea1361Sscw qCfg |= baseAddress << IX_QMGR_Q_CONFIG_BADDR_OFFSET;
999bdea1361Sscw
1000bdea1361Sscw /* set watermarks */
1001bdea1361Sscw qCfg |= (toAqmWatermark(ne) << IX_QMGR_Q_CONFIG_NE_OFFSET)
1002bdea1361Sscw | (toAqmWatermark(nf) << IX_QMGR_Q_CONFIG_NF_OFFSET);
1003bdea1361Sscw
1004bdea1361Sscw DPRINTF(sc->sc_dev, "%s(%u, %u, %u) 0x%x => 0x%x @ 0x%x\n",
1005bdea1361Sscw __func__, qId, ne, nf,
1006bdea1361Sscw aqm_reg_read(sc, IX_QMGR_Q_CONFIG_ADDR_GET(qId)),
1007bdea1361Sscw qCfg, (u_int)IX_QMGR_Q_CONFIG_ADDR_GET(qId));
1008bdea1361Sscw
1009bdea1361Sscw aqm_reg_write(sc, IX_QMGR_Q_CONFIG_ADDR_GET(qId), qCfg);
1010bdea1361Sscw }
1011bdea1361Sscw
1012bdea1361Sscw static void
aqm_srcsel_write(struct ixpqmgr_softc * sc,int qId,int sourceId)1013bdea1361Sscw aqm_srcsel_write(struct ixpqmgr_softc *sc, int qId, int sourceId)
1014bdea1361Sscw {
1015bdea1361Sscw bus_size_t off;
1016bdea1361Sscw uint32_t v;
1017bdea1361Sscw
1018bdea1361Sscw /*
1019bdea1361Sscw * Calculate the register offset; multiple queues split across registers
1020bdea1361Sscw */
1021bdea1361Sscw off = IX_QMGR_INT0SRCSELREG0_OFFSET +
1022bdea1361Sscw ((qId / IX_QMGR_INTSRC_NUM_QUE_PER_WORD) * sizeof(uint32_t));
1023bdea1361Sscw
1024bdea1361Sscw v = aqm_reg_read(sc, off);
1025bdea1361Sscw if (off == IX_QMGR_INT0SRCSELREG0_OFFSET && qId == 0) {
1026bdea1361Sscw /* Queue 0 at INT0SRCSELREG should not corrupt the value bit-3 */
1027bdea1361Sscw v |= 0x7;
1028bdea1361Sscw } else {
1029bdea1361Sscw const uint32_t bpq = 32 / IX_QMGR_INTSRC_NUM_QUE_PER_WORD;
1030bdea1361Sscw uint32_t mask;
1031bdea1361Sscw int qshift;
1032bdea1361Sscw
1033bdea1361Sscw qshift = (qId & (IX_QMGR_INTSRC_NUM_QUE_PER_WORD-1)) * bpq;
1034bdea1361Sscw mask = ((1 << bpq) - 1) << qshift; /* q's status mask */
1035bdea1361Sscw
1036bdea1361Sscw /* merge sourceId */
1037bdea1361Sscw v = (v &~ mask) | ((sourceId << qshift) & mask);
1038bdea1361Sscw }
1039bdea1361Sscw
1040bdea1361Sscw DPRINTF(sc->sc_dev, "%s(%u, %u) 0x%x => 0x%x @ 0x%lx\n",
1041bdea1361Sscw __func__, qId, sourceId, aqm_reg_read(sc, off), v, off);
1042bdea1361Sscw aqm_reg_write(sc, off, v);
1043bdea1361Sscw }
1044bdea1361Sscw
1045bdea1361Sscw /*
1046bdea1361Sscw * Reset AQM registers to default values.
1047bdea1361Sscw */
1048bdea1361Sscw static void
aqm_reset(struct ixpqmgr_softc * sc)1049bdea1361Sscw aqm_reset(struct ixpqmgr_softc *sc)
1050bdea1361Sscw {
1051bdea1361Sscw int i;
1052bdea1361Sscw
1053bdea1361Sscw /* Reset queues 0..31 status registers 0..3 */
1054bdea1361Sscw aqm_reg_write(sc, IX_QMGR_QUELOWSTAT0_OFFSET,
1055bdea1361Sscw IX_QMGR_QUELOWSTAT_RESET_VALUE);
1056bdea1361Sscw aqm_reg_write(sc, IX_QMGR_QUELOWSTAT1_OFFSET,
1057bdea1361Sscw IX_QMGR_QUELOWSTAT_RESET_VALUE);
1058bdea1361Sscw aqm_reg_write(sc, IX_QMGR_QUELOWSTAT2_OFFSET,
1059bdea1361Sscw IX_QMGR_QUELOWSTAT_RESET_VALUE);
1060bdea1361Sscw aqm_reg_write(sc, IX_QMGR_QUELOWSTAT3_OFFSET,
1061bdea1361Sscw IX_QMGR_QUELOWSTAT_RESET_VALUE);
1062bdea1361Sscw
1063bdea1361Sscw /* Reset underflow/overflow status registers 0..1 */
1064bdea1361Sscw aqm_reg_write(sc, IX_QMGR_QUEUOSTAT0_OFFSET,
1065bdea1361Sscw IX_QMGR_QUEUOSTAT_RESET_VALUE);
1066bdea1361Sscw aqm_reg_write(sc, IX_QMGR_QUEUOSTAT1_OFFSET,
1067bdea1361Sscw IX_QMGR_QUEUOSTAT_RESET_VALUE);
1068bdea1361Sscw
1069bdea1361Sscw /* Reset queues 32..63 nearly empty status registers */
1070bdea1361Sscw aqm_reg_write(sc, IX_QMGR_QUEUPPSTAT0_OFFSET,
1071bdea1361Sscw IX_QMGR_QUEUPPSTAT0_RESET_VALUE);
1072bdea1361Sscw
1073bdea1361Sscw /* Reset queues 32..63 full status registers */
1074bdea1361Sscw aqm_reg_write(sc, IX_QMGR_QUEUPPSTAT1_OFFSET,
1075bdea1361Sscw IX_QMGR_QUEUPPSTAT1_RESET_VALUE);
1076bdea1361Sscw
1077bdea1361Sscw /* Reset int0 status flag source select registers 0..3 */
1078bdea1361Sscw aqm_reg_write(sc, IX_QMGR_INT0SRCSELREG0_OFFSET,
1079bdea1361Sscw IX_QMGR_INT0SRCSELREG_RESET_VALUE);
1080bdea1361Sscw aqm_reg_write(sc, IX_QMGR_INT0SRCSELREG1_OFFSET,
1081bdea1361Sscw IX_QMGR_INT0SRCSELREG_RESET_VALUE);
1082bdea1361Sscw aqm_reg_write(sc, IX_QMGR_INT0SRCSELREG2_OFFSET,
1083bdea1361Sscw IX_QMGR_INT0SRCSELREG_RESET_VALUE);
1084bdea1361Sscw aqm_reg_write(sc, IX_QMGR_INT0SRCSELREG3_OFFSET,
1085bdea1361Sscw IX_QMGR_INT0SRCSELREG_RESET_VALUE);
1086bdea1361Sscw
1087bdea1361Sscw /* Reset queue interrupt enable register 0..1 */
1088bdea1361Sscw aqm_reg_write(sc, IX_QMGR_QUEIEREG0_OFFSET,
1089bdea1361Sscw IX_QMGR_QUEIEREG_RESET_VALUE);
1090bdea1361Sscw aqm_reg_write(sc, IX_QMGR_QUEIEREG1_OFFSET,
1091bdea1361Sscw IX_QMGR_QUEIEREG_RESET_VALUE);
1092bdea1361Sscw
1093bdea1361Sscw /* Reset queue interrupt register 0..1 */
1094bdea1361Sscw aqm_reg_write(sc, IX_QMGR_QINTREG0_OFFSET, IX_QMGR_QINTREG_RESET_VALUE);
1095bdea1361Sscw aqm_reg_write(sc, IX_QMGR_QINTREG1_OFFSET, IX_QMGR_QINTREG_RESET_VALUE);
1096bdea1361Sscw
1097bdea1361Sscw /* Reset queue configuration words 0..63 */
1098bdea1361Sscw for (i = 0; i < IX_QMGR_MAX_NUM_QUEUES; i++)
1099bdea1361Sscw aqm_reg_write(sc, sc->qinfo[i].qConfigRegAddr,
1100bdea1361Sscw IX_QMGR_QUECONFIG_RESET_VALUE);
1101bdea1361Sscw
1102bdea1361Sscw /* XXX zero SRAM to simplify debugging */
1103bdea1361Sscw for (i = IX_QMGR_QUEBUFFER_SPACE_OFFSET;
1104bdea1361Sscw i < IX_QMGR_AQM_SRAM_SIZE_IN_BYTES; i += sizeof(uint32_t))
1105bdea1361Sscw aqm_reg_write(sc, i, 0);
1106bdea1361Sscw }
1107bdea1361Sscw
1108bdea1361Sscw #ifdef __FreeBSD__
1109bdea1361Sscw static device_method_t ixpqmgr_methods[] = {
1110bdea1361Sscw DEVMETHOD(device_probe, ixpqmgr_probe),
1111bdea1361Sscw DEVMETHOD(device_attach, ixpqmgr_attach),
1112bdea1361Sscw DEVMETHOD(device_detach, ixpqmgr_detach),
1113bdea1361Sscw
1114bdea1361Sscw { 0, 0 }
1115bdea1361Sscw };
1116bdea1361Sscw
1117bdea1361Sscw static driver_t ixpqmgr_driver = {
1118bdea1361Sscw "ixpqmgr",
1119bdea1361Sscw ixpqmgr_methods,
1120bdea1361Sscw sizeof(struct ixpqmgr_softc),
1121bdea1361Sscw };
1122bdea1361Sscw static devclass_t ixpqmgr_devclass;
1123bdea1361Sscw
1124bdea1361Sscw DRIVER_MODULE(ixpqmgr, ixp, ixpqmgr_driver, ixpqmgr_devclass, 0, 0);
1125bdea1361Sscw #endif
1126