2 * Copyright (c) 2006 Sam Leffler, Errno Consulting
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer,
10 * without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 * redistribution must be conditioned upon including a substantially
14 * similar Disclaimer requirement for further binary redistribution.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
31 * Copyright (c) 2001-2005, Intel Corporation.
32 * All rights reserved.
34 * Redistribution and use in source and binary forms, with or without
35 * modification, are permitted provided that the following conditions
37 * 1. Redistributions of source code must retain the above copyright
38 * notice, this list of conditions and the following disclaimer.
39 * 2. Redistributions in binary form must reproduce the above copyright
40 * notice, this list of conditions and the following disclaimer in the
41 * documentation and/or other materials provided with the distribution.
42 * 3. Neither the name of the Intel Corporation nor the names of its contributors
43 * may be used to endorse or promote products derived from this software
44 * without specific prior written permission.
47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
48 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
51 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 #include <sys/cdefs.h>
60 __FBSDID("$FreeBSD$");
63 * Intel XScale Queue Manager support.
65 * Each IXP4XXX device has a hardware block that implements a priority
66 * queue manager that is shared between the XScale cpu and the backend
67 * devices (such as the NPE). Queues are accessed by reading/writing
68 * special memory locations. The queue contents are mapped into a shared
69 * SRAM region with entries managed in a circular buffer. The XScale
70 * processor can receive interrupts based on queue contents (a condition
71 * code determines when interrupts should be delivered).
73 * The code here basically replaces the qmgr class in the Intel Access
76 #include <sys/param.h>
77 #include <sys/systm.h>
78 #include <sys/kernel.h>
79 #include <sys/module.h>
82 #include <sys/resource.h>
84 #include <sys/sysctl.h>
86 #include <machine/bus.h>
87 #include <machine/cpu.h>
88 #include <machine/cpufunc.h>
89 #include <machine/resource.h>
90 #include <machine/intr.h>
91 #include <arm/xscale/ixp425/ixp425reg.h>
92 #include <arm/xscale/ixp425/ixp425var.h>
94 #include <arm/xscale/ixp425/ixp425_qmgr.h>
97 * State per AQM hw queue.
98 * This structure holds q configuration and dispatch state.
101 int qSizeInWords; /* queue size in words */
103 uint32_t qOflowStatBitMask; /* overflow status mask */
104 int qWriteCount; /* queue write count */
106 bus_size_t qAccRegAddr; /* access register */
107 bus_size_t qUOStatRegAddr; /* status register */
108 bus_size_t qConfigRegAddr; /* config register */
109 int qSizeInEntries; /* queue size in entries */
111 uint32_t qUflowStatBitMask; /* underflow status mask */
112 int qReadCount; /* queue read count */
115 uint32_t qStatRegAddr;
116 uint32_t qStatBitsOffset;
117 uint32_t qStat0BitMask;
118 uint32_t qStat1BitMask;
120 uint32_t intRegCheckMask; /* interrupt reg check mask */
121 void (*cb)(int, void *); /* callback function */
122 void *cbarg; /* callback argument */
123 int priority; /* dispatch priority */
125 /* NB: needed only for A0 parts */
126 u_int statusWordOffset; /* status word offset */
127 uint32_t statusMask; /* status mask */
128 uint32_t statusCheckValue; /* status check value */
132 struct ixpqmgr_softc {
134 bus_space_tag_t sc_iot;
135 bus_space_handle_t sc_ioh;
137 struct resource *sc_irq1; /* IRQ resource */
138 void *sc_ih1; /* interrupt handler */
139 int sc_rid1; /* resource id for irq */
141 struct resource *sc_irq2;
145 struct qmgrInfo qinfo[IX_QMGR_MAX_NUM_QUEUES];
147 * This array contains a list of queue identifiers ordered by
148 * priority. The table is split logically between queue
149 * identifiers 0-31 and 32-63. To optimize lookups bit masks
150 * are kept for the first-32 and last-32 q's. When the
151 * table needs to be rebuilt mark rebuildTable and it'll
152 * happen after the next interrupt.
154 int priorityTable[IX_QMGR_MAX_NUM_QUEUES];
155 uint32_t lowPriorityTableFirstHalfMask;
156 uint32_t uppPriorityTableFirstHalfMask;
157 int rebuildTable; /* rebuild priorityTable */
159 uint32_t aqmFreeSramAddress; /* SRAM free space */
162 static int qmgr_debug;
163 SYSCTL_INT(_debug, OID_AUTO, qmgr, CTLFLAG_RWTUN, &qmgr_debug,
164 0, "IXP4XX Q-Manager debug msgs");
165 #define DPRINTF(dev, fmt, ...) do { \
166 if (qmgr_debug) printf(fmt, __VA_ARGS__); \
168 #define DPRINTFn(n, dev, fmt, ...) do { \
169 if (qmgr_debug >= n) printf(fmt, __VA_ARGS__); \
172 static struct ixpqmgr_softc *ixpqmgr_sc = NULL;
174 static void ixpqmgr_rebuild(struct ixpqmgr_softc *);
175 static void ixpqmgr_intr(void *);
177 static void aqm_int_enable(struct ixpqmgr_softc *sc, int qId);
178 static void aqm_int_disable(struct ixpqmgr_softc *sc, int qId);
179 static void aqm_qcfg(struct ixpqmgr_softc *sc, int qId, u_int ne, u_int nf);
180 static void aqm_srcsel_write(struct ixpqmgr_softc *sc, int qId, int sourceId);
181 static void aqm_reset(struct ixpqmgr_softc *sc);
184 dummyCallback(int qId, void *arg)
190 aqm_reg_read(struct ixpqmgr_softc *sc, bus_size_t off)
192 DPRINTFn(9, sc->sc_dev, "%s(0x%x)\n", __func__, (int)off);
193 return bus_space_read_4(sc->sc_iot, sc->sc_ioh, off);
197 aqm_reg_write(struct ixpqmgr_softc *sc, bus_size_t off, uint32_t val)
199 DPRINTFn(9, sc->sc_dev, "%s(0x%x, 0x%x)\n", __func__, (int)off, val);
200 bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, val);
204 ixpqmgr_probe(device_t dev)
206 device_set_desc(dev, "IXP4XX Q-Manager");
211 ixpqmgr_attach(device_t dev)
213 struct ixpqmgr_softc *sc = device_get_softc(dev);
214 struct ixp425_softc *sa = device_get_softc(device_get_parent(dev));
220 sc->sc_iot = sa->sc_iot;
221 if (bus_space_map(sc->sc_iot, IXP425_QMGR_HWBASE, IXP425_QMGR_SIZE,
223 panic("%s: Cannot map registers", device_get_name(dev));
225 /* NB: we only use the lower 32 q's */
227 /* Set up QMGR interrupts */
229 sc->sc_irq1 = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->sc_rid1,
230 IXP425_INT_QUE1_32, IXP425_INT_QUE1_32, 1, RF_ACTIVE);
232 sc->sc_irq2 = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->sc_rid2,
233 IXP425_INT_QUE33_64, IXP425_INT_QUE33_64, 1, RF_ACTIVE);
235 if (sc->sc_irq1 == NULL || sc->sc_irq2 == NULL)
236 panic("Unable to allocate the qmgr irqs.\n");
238 err = bus_setup_intr(dev, sc->sc_irq1, INTR_TYPE_NET | INTR_MPSAFE,
239 NULL, ixpqmgr_intr, NULL, &sc->sc_ih1);
241 device_printf(dev, "failed to set up qmgr irq=%d\n",
245 err = bus_setup_intr(dev, sc->sc_irq2, INTR_TYPE_NET | INTR_MPSAFE,
246 NULL, ixpqmgr_intr, NULL, &sc->sc_ih2);
248 device_printf(dev, "failed to set up qmgr irq=%d\n",
249 IXP425_INT_QUE33_64);
253 /* NB: softc is pre-zero'd */
254 for (i = 0; i < IX_QMGR_MAX_NUM_QUEUES; i++) {
255 struct qmgrInfo *qi = &sc->qinfo[i];
257 qi->cb = dummyCallback;
258 qi->priority = IX_QMGR_Q_PRIORITY_0; /* default priority */
260 * There are two interrupt registers, 32 bits each. One
261 * for the lower queues(0-31) and one for the upper
262 * queues(32-63). Therefore need to mod by 32 i.e the
263 * min upper queue identifier.
265 qi->intRegCheckMask = (1<<(i%(IX_QMGR_MIN_QUEUPP_QID)));
268 * Register addresses and bit masks are calculated and
269 * stored here to optimize QRead, QWrite and QStatusGet
273 /* AQM Queue access reg addresses, per queue */
274 qi->qAccRegAddr = IX_QMGR_Q_ACCESS_ADDR_GET(i);
275 qi->qAccRegAddr = IX_QMGR_Q_ACCESS_ADDR_GET(i);
276 qi->qConfigRegAddr = IX_QMGR_Q_CONFIG_ADDR_GET(i);
278 /* AQM Queue lower-group (0-31), only */
279 if (i < IX_QMGR_MIN_QUEUPP_QID) {
280 /* AQM Q underflow/overflow status reg address, per queue */
281 qi->qUOStatRegAddr = IX_QMGR_QUEUOSTAT0_OFFSET +
282 ((i / IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD) *
285 /* AQM Q underflow status bit masks for status reg per queue */
286 qi->qUflowStatBitMask =
287 (IX_QMGR_UNDERFLOW_BIT_OFFSET + 1) <<
288 ((i & (IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD - 1)) *
289 (32 / IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD));
291 /* AQM Q overflow status bit masks for status reg, per queue */
292 qi->qOflowStatBitMask =
293 (IX_QMGR_OVERFLOW_BIT_OFFSET + 1) <<
294 ((i & (IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD - 1)) *
295 (32 / IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD));
297 /* AQM Q lower-group (0-31) status reg addresses, per queue */
298 qi->qStatRegAddr = IX_QMGR_QUELOWSTAT0_OFFSET +
299 ((i / IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD) *
302 /* AQM Q lower-group (0-31) status register bit offset */
303 qi->qStatBitsOffset =
304 (i & (IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD - 1)) *
305 (32 / IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD);
306 } else { /* AQM Q upper-group (32-63), only */
307 qi->qUOStatRegAddr = 0; /* XXX */
309 /* AQM Q upper-group (32-63) Nearly Empty status reg bitmasks */
310 qi->qStat0BitMask = (1 << (i - IX_QMGR_MIN_QUEUPP_QID));
312 /* AQM Q upper-group (32-63) Full status register bitmasks */
313 qi->qStat1BitMask = (1 << (i - IX_QMGR_MIN_QUEUPP_QID));
317 sc->aqmFreeSramAddress = 0x100; /* Q buffer space starts at 0x2100 */
319 ixpqmgr_rebuild(sc); /* build initial priority table */
320 aqm_reset(sc); /* reset h/w */
325 ixpqmgr_detach(device_t dev)
327 struct ixpqmgr_softc *sc = device_get_softc(dev);
329 aqm_reset(sc); /* disable interrupts */
330 bus_teardown_intr(dev, sc->sc_irq1, sc->sc_ih1);
331 bus_teardown_intr(dev, sc->sc_irq2, sc->sc_ih2);
332 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid1, sc->sc_irq1);
333 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid2, sc->sc_irq2);
334 bus_space_unmap(sc->sc_iot, sc->sc_ioh, IXP425_QMGR_SIZE);
339 ixpqmgr_qconfig(int qId, int qEntries, int ne, int nf, int srcSel,
340 qconfig_hand_t *cb, void *cbarg)
342 struct ixpqmgr_softc *sc = ixpqmgr_sc;
343 struct qmgrInfo *qi = &sc->qinfo[qId];
345 DPRINTF(sc->sc_dev, "%s(%u, %u, %u, %u, %u, %p, %p)\n",
346 __func__, qId, qEntries, ne, nf, srcSel, cb, cbarg);
348 /* NB: entry size is always 1 */
349 qi->qSizeInWords = qEntries;
353 qi->qSizeInEntries = qEntries; /* XXX kept for code clarity */
356 /* Reset to dummy callback */
357 qi->cb = dummyCallback;
364 /* Write the config register; NB must be AFTER qinfo setup */
365 aqm_qcfg(sc, qId, ne, nf);
367 * Account for space just allocated to queue.
369 sc->aqmFreeSramAddress += (qi->qSizeInWords * sizeof(uint32_t));
371 /* Set the interrupt source if this queue is in the range 0-31 */
372 if (qId < IX_QMGR_MIN_QUEUPP_QID)
373 aqm_srcsel_write(sc, qId, srcSel);
375 if (cb != NULL) /* Enable the interrupt */
376 aqm_int_enable(sc, qId);
378 sc->rebuildTable = TRUE;
384 ixpqmgr_qwrite(int qId, uint32_t entry)
386 struct ixpqmgr_softc *sc = ixpqmgr_sc;
387 struct qmgrInfo *qi = &sc->qinfo[qId];
389 DPRINTFn(3, sc->sc_dev, "%s(%u, 0x%x) writeCount %u size %u\n",
390 __func__, qId, entry, qi->qWriteCount, qi->qSizeInEntries);
392 /* write the entry */
393 aqm_reg_write(sc, qi->qAccRegAddr, entry);
395 /* NB: overflow is available for lower queues only */
396 if (qId < IX_QMGR_MIN_QUEUPP_QID) {
397 int qSize = qi->qSizeInEntries;
399 * Increment the current number of entries in the queue
400 * and check for overflow .
402 if (qi->qWriteCount++ == qSize) { /* check for overflow */
403 uint32_t status = aqm_reg_read(sc, qi->qUOStatRegAddr);
407 * Read the status twice because the status may
408 * not be immediately ready after the write operation
410 if ((status & qi->qOflowStatBitMask) ||
411 ((status = aqm_reg_read(sc, qi->qUOStatRegAddr)) & qi->qOflowStatBitMask)) {
413 * The queue is full, clear the overflow status bit if set.
415 aqm_reg_write(sc, qi->qUOStatRegAddr,
416 status & ~qi->qOflowStatBitMask);
417 qi->qWriteCount = qSize;
418 DPRINTFn(5, sc->sc_dev,
419 "%s(%u, 0x%x) Q full, overflow status cleared\n",
420 __func__, qId, entry);
424 * No overflow occured : someone is draining the queue
425 * and the current counter needs to be
426 * updated from the current number of entries in the queue
429 /* calculate number of words in q */
430 qPtrs = aqm_reg_read(sc, qi->qConfigRegAddr);
431 DPRINTFn(2, sc->sc_dev,
432 "%s(%u, 0x%x) Q full, no overflow status, qConfig 0x%x\n",
433 __func__, qId, entry, qPtrs);
434 qPtrs = (qPtrs - (qPtrs >> 7)) & 0x7f;
438 * The queue may be full at the time of the
439 * snapshot. Next access will check
440 * the overflow status again.
442 qi->qWriteCount = qSize;
444 /* convert the number of words to a number of entries */
445 qi->qWriteCount = qPtrs & (qSize - 1);
453 ixpqmgr_qread(int qId, uint32_t *entry)
455 struct ixpqmgr_softc *sc = ixpqmgr_sc;
456 struct qmgrInfo *qi = &sc->qinfo[qId];
457 bus_size_t off = qi->qAccRegAddr;
459 *entry = aqm_reg_read(sc, off);
462 * Reset the current read count : next access to the read function
463 * will force a underflow status check.
467 /* Check if underflow occurred on the read */
468 if (*entry == 0 && qId < IX_QMGR_MIN_QUEUPP_QID) {
469 /* get the queue status */
470 uint32_t status = aqm_reg_read(sc, qi->qUOStatRegAddr);
472 if (status & qi->qUflowStatBitMask) { /* clear underflow status */
473 aqm_reg_write(sc, qi->qUOStatRegAddr,
474 status &~ qi->qUflowStatBitMask);
482 ixpqmgr_qreadm(int qId, uint32_t n, uint32_t *p)
484 struct ixpqmgr_softc *sc = ixpqmgr_sc;
485 struct qmgrInfo *qi = &sc->qinfo[qId];
487 bus_size_t off = qi->qAccRegAddr;
489 entry = aqm_reg_read(sc, off);
492 /* if we read a NULL entry, stop. We have underflowed */
495 *p++ = entry; /* store */
496 entry = aqm_reg_read(sc, off);
501 * Reset the current read count : next access to the read function
502 * will force a underflow status check.
506 /* Check if underflow occurred on the read */
507 if (entry == 0 && qId < IX_QMGR_MIN_QUEUPP_QID) {
508 /* get the queue status */
509 uint32_t status = aqm_reg_read(sc, qi->qUOStatRegAddr);
511 if (status & qi->qUflowStatBitMask) { /* clear underflow status */
512 aqm_reg_write(sc, qi->qUOStatRegAddr,
513 status &~ qi->qUflowStatBitMask);
521 ixpqmgr_getqstatus(int qId)
523 #define QLOWSTATMASK \
524 ((1 << (32 / IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD)) - 1)
525 struct ixpqmgr_softc *sc = ixpqmgr_sc;
526 const struct qmgrInfo *qi = &sc->qinfo[qId];
529 if (qId < IX_QMGR_MIN_QUEUPP_QID) {
530 /* read the status of a queue in the range 0-31 */
531 status = aqm_reg_read(sc, qi->qStatRegAddr);
533 /* mask out the status bits relevant only to this queue */
534 status = (status >> qi->qStatBitsOffset) & QLOWSTATMASK;
535 } else { /* read status of a queue in the range 32-63 */
537 if (aqm_reg_read(sc, IX_QMGR_QUEUPPSTAT0_OFFSET)&qi->qStat0BitMask)
538 status |= IX_QMGR_Q_STATUS_NE_BIT_MASK; /* nearly empty */
539 if (aqm_reg_read(sc, IX_QMGR_QUEUPPSTAT1_OFFSET)&qi->qStat1BitMask)
540 status |= IX_QMGR_Q_STATUS_F_BIT_MASK; /* full */
547 ixpqmgr_getqconfig(int qId)
549 struct ixpqmgr_softc *sc = ixpqmgr_sc;
551 return aqm_reg_read(sc, IX_QMGR_Q_CONFIG_ADDR_GET(qId));
557 struct ixpqmgr_softc *sc = ixpqmgr_sc;
560 /* status registers */
561 printf("0x%04x: %08x %08x %08x %08x\n"
563 , aqm_reg_read(sc, 0x400)
564 , aqm_reg_read(sc, 0x400+4)
565 , aqm_reg_read(sc, 0x400+8)
566 , aqm_reg_read(sc, 0x400+12)
568 printf("0x%04x: %08x %08x %08x %08x\n"
570 , aqm_reg_read(sc, 0x410)
571 , aqm_reg_read(sc, 0x410+4)
572 , aqm_reg_read(sc, 0x410+8)
573 , aqm_reg_read(sc, 0x410+12)
575 printf("0x%04x: %08x %08x %08x %08x\n"
577 , aqm_reg_read(sc, 0x420)
578 , aqm_reg_read(sc, 0x420+4)
579 , aqm_reg_read(sc, 0x420+8)
580 , aqm_reg_read(sc, 0x420+12)
582 printf("0x%04x: %08x %08x %08x %08x\n"
584 , aqm_reg_read(sc, 0x430)
585 , aqm_reg_read(sc, 0x430+4)
586 , aqm_reg_read(sc, 0x430+8)
587 , aqm_reg_read(sc, 0x430+12)
589 /* q configuration registers */
590 for (a = 0x2000; a < 0x20ff; a += 32)
591 printf("0x%04x: %08x %08x %08x %08x %08x %08x %08x %08x\n"
593 , aqm_reg_read(sc, a)
594 , aqm_reg_read(sc, a+4)
595 , aqm_reg_read(sc, a+8)
596 , aqm_reg_read(sc, a+12)
597 , aqm_reg_read(sc, a+16)
598 , aqm_reg_read(sc, a+20)
599 , aqm_reg_read(sc, a+24)
600 , aqm_reg_read(sc, a+28)
603 for (i = 0x100; i < sc->aqmFreeSramAddress; i += 32) {
605 printf("0x%04x: %08x %08x %08x %08x %08x %08x %08x %08x\n"
607 , aqm_reg_read(sc, a)
608 , aqm_reg_read(sc, a+4)
609 , aqm_reg_read(sc, a+8)
610 , aqm_reg_read(sc, a+12)
611 , aqm_reg_read(sc, a+16)
612 , aqm_reg_read(sc, a+20)
613 , aqm_reg_read(sc, a+24)
614 , aqm_reg_read(sc, a+28)
617 for (i = 0; i < 16; i++) {
618 printf("Q[%2d] config 0x%08x status 0x%02x "
619 "Q[%2d] config 0x%08x status 0x%02x\n"
620 , i, ixpqmgr_getqconfig(i), ixpqmgr_getqstatus(i)
621 , i+16, ixpqmgr_getqconfig(i+16), ixpqmgr_getqstatus(i+16)
627 ixpqmgr_notify_enable(int qId, int srcSel)
629 struct ixpqmgr_softc *sc = ixpqmgr_sc;
631 /* Calculate the checkMask and checkValue for this q */
632 aqm_calc_statuscheck(sc, qId, srcSel);
634 /* Set the interrupt source if this queue is in the range 0-31 */
635 if (qId < IX_QMGR_MIN_QUEUPP_QID)
636 aqm_srcsel_write(sc, qId, srcSel);
638 /* Enable the interrupt */
639 aqm_int_enable(sc, qId);
643 ixpqmgr_notify_disable(int qId)
645 struct ixpqmgr_softc *sc = ixpqmgr_sc;
647 aqm_int_disable(sc, qId);
651 * Rebuild the priority table used by the dispatcher.
654 ixpqmgr_rebuild(struct ixpqmgr_softc *sc)
657 int lowQuePriorityTableIndex, uppQuePriorityTableIndex;
660 sc->lowPriorityTableFirstHalfMask = 0;
661 sc->uppPriorityTableFirstHalfMask = 0;
663 lowQuePriorityTableIndex = 0;
664 uppQuePriorityTableIndex = 32;
665 for (pri = 0; pri < IX_QMGR_NUM_PRIORITY_LEVELS; pri++) {
666 /* low priority q's */
667 for (q = 0; q < IX_QMGR_MIN_QUEUPP_QID; q++) {
669 if (qi->priority == pri) {
671 * Build the priority table bitmask which match the
672 * queues of the first half of the priority table.
674 if (lowQuePriorityTableIndex < 16) {
675 sc->lowPriorityTableFirstHalfMask |=
678 sc->priorityTable[lowQuePriorityTableIndex++] = q;
681 /* high priority q's */
682 for (; q < IX_QMGR_MAX_NUM_QUEUES; q++) {
684 if (qi->priority == pri) {
686 * Build the priority table bitmask which match the
687 * queues of the first half of the priority table .
689 if (uppQuePriorityTableIndex < 48) {
690 sc->uppPriorityTableFirstHalfMask |=
693 sc->priorityTable[uppQuePriorityTableIndex++] = q;
697 sc->rebuildTable = FALSE;
701 * Count the number of leading zero bits in a word,
702 * and return the same value than the CLZ instruction.
703 * Note this is similar to the standard ffs function but
704 * it counts zero's from the MSB instead of the LSB.
706 * word (in) return value (out)
714 * The C version of this function is used as a replacement
715 * for system not providing the equivalent of the CLZ
716 * assembly language instruction.
718 * Note that this version is big-endian
721 _lzcount(uint32_t word)
723 unsigned int lzcount = 0;
727 while ((word & 0x80000000) == 0) {
735 ixpqmgr_intr(void *arg)
737 struct ixpqmgr_softc *sc = ixpqmgr_sc;
738 uint32_t intRegVal; /* Interrupt reg val */
740 int priorityTableIndex; /* Priority table index */
741 int qIndex; /* Current queue being processed */
743 /* Read the interrupt register */
744 intRegVal = aqm_reg_read(sc, IX_QMGR_QINTREG0_OFFSET);
745 /* Write back to clear interrupt */
746 aqm_reg_write(sc, IX_QMGR_QINTREG0_OFFSET, intRegVal);
748 DPRINTFn(5, sc->sc_dev, "%s: ISR0 0x%x ISR1 0x%x\n",
749 __func__, intRegVal, aqm_reg_read(sc, IX_QMGR_QINTREG1_OFFSET));
751 /* No queue has interrupt register set */
752 if (intRegVal != 0) {
753 /* get the first queue Id from the interrupt register value */
754 qIndex = (32 - 1) - _lzcount(intRegVal);
756 DPRINTFn(2, sc->sc_dev, "%s: ISR0 0x%x qIndex %u\n",
757 __func__, intRegVal, qIndex);
760 * Optimize for single callback case.
762 qi = &sc->qinfo[qIndex];
763 if (intRegVal == qi->intRegCheckMask) {
765 * Only 1 queue event triggered a notification.
766 * Call the callback function for this queue
768 qi->cb(qIndex, qi->cbarg);
771 * The event is triggered by more than 1 queue,
772 * the queue search will start from the beginning
773 * or the middle of the priority table.
775 * The search will end when all the bits of the interrupt
776 * register are cleared. There is no need to maintain
777 * a separate value and test it at each iteration.
779 if (intRegVal & sc->lowPriorityTableFirstHalfMask) {
780 priorityTableIndex = 0;
782 priorityTableIndex = 16;
785 * Iterate over the priority table until all the bits
786 * of the interrupt register are cleared.
789 qIndex = sc->priorityTable[priorityTableIndex++];
790 qi = &sc->qinfo[qIndex];
792 /* If this queue caused this interrupt to be raised */
793 if (intRegVal & qi->intRegCheckMask) {
794 /* Call the callback function for this queue */
795 qi->cb(qIndex, qi->cbarg);
796 /* Clear the interrupt register bit */
797 intRegVal &= ~qi->intRegCheckMask;
803 /* Rebuild the priority table if needed */
804 if (sc->rebuildTable)
810 * Generate the parameters used to check if a Q's status matches
811 * the specified source select. We calculate which status word
812 * to check (statusWordOffset), the value to check the status
813 * against (statusCheckValue) and the mask (statusMask) to mask
814 * out all but the bits to check in the status word.
817 aqm_calc_statuscheck(int qId, IxQMgrSourceId srcSel)
819 struct qmgrInfo *qi = &qinfo[qId];
822 if (qId < IX_QMGR_MIN_QUEUPP_QID) {
824 case IX_QMGR_Q_SOURCE_ID_E:
825 qi->statusCheckValue = IX_QMGR_Q_STATUS_E_BIT_MASK;
826 qi->statusMask = IX_QMGR_Q_STATUS_E_BIT_MASK;
828 case IX_QMGR_Q_SOURCE_ID_NE:
829 qi->statusCheckValue = IX_QMGR_Q_STATUS_NE_BIT_MASK;
830 qi->statusMask = IX_QMGR_Q_STATUS_NE_BIT_MASK;
832 case IX_QMGR_Q_SOURCE_ID_NF:
833 qi->statusCheckValue = IX_QMGR_Q_STATUS_NF_BIT_MASK;
834 qi->statusMask = IX_QMGR_Q_STATUS_NF_BIT_MASK;
836 case IX_QMGR_Q_SOURCE_ID_F:
837 qi->statusCheckValue = IX_QMGR_Q_STATUS_F_BIT_MASK;
838 qi->statusMask = IX_QMGR_Q_STATUS_F_BIT_MASK;
840 case IX_QMGR_Q_SOURCE_ID_NOT_E:
841 qi->statusCheckValue = 0;
842 qi->statusMask = IX_QMGR_Q_STATUS_E_BIT_MASK;
844 case IX_QMGR_Q_SOURCE_ID_NOT_NE:
845 qi->statusCheckValue = 0;
846 qi->statusMask = IX_QMGR_Q_STATUS_NE_BIT_MASK;
848 case IX_QMGR_Q_SOURCE_ID_NOT_NF:
849 qi->statusCheckValue = 0;
850 qi->statusMask = IX_QMGR_Q_STATUS_NF_BIT_MASK;
852 case IX_QMGR_Q_SOURCE_ID_NOT_F:
853 qi->statusCheckValue = 0;
854 qi->statusMask = IX_QMGR_Q_STATUS_F_BIT_MASK;
857 /* Should never hit */
862 /* One nibble of status per queue so need to shift the
863 * check value and mask out to the correct position.
865 shiftVal = (qId % IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD) *
866 IX_QMGR_QUELOWSTAT_BITS_PER_Q;
868 /* Calculate the which status word to check from the qId,
869 * 8 Qs status per word
871 qi->statusWordOffset = qId / IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD;
873 qi->statusCheckValue <<= shiftVal;
874 qi->statusMask <<= shiftVal;
876 /* One status word */
877 qi->statusWordOffset = 0;
878 /* Single bits per queue and int source bit hardwired NE,
881 qi->statusMask = 1 << (qId - IX_QMGR_MIN_QUEUPP_QID);
882 qi->statusCheckValue = qi->statusMask;
888 aqm_int_enable(struct ixpqmgr_softc *sc, int qId)
893 if (qId < IX_QMGR_MIN_QUEUPP_QID)
894 reg = IX_QMGR_QUEIEREG0_OFFSET;
896 reg = IX_QMGR_QUEIEREG1_OFFSET;
897 v = aqm_reg_read(sc, reg);
898 aqm_reg_write(sc, reg, v | (1 << (qId % IX_QMGR_MIN_QUEUPP_QID)));
900 DPRINTF(sc->sc_dev, "%s(%u) 0x%lx: 0x%x => 0x%x\n",
901 __func__, qId, reg, v, aqm_reg_read(sc, reg));
905 aqm_int_disable(struct ixpqmgr_softc *sc, int qId)
910 if (qId < IX_QMGR_MIN_QUEUPP_QID)
911 reg = IX_QMGR_QUEIEREG0_OFFSET;
913 reg = IX_QMGR_QUEIEREG1_OFFSET;
914 v = aqm_reg_read(sc, reg);
915 aqm_reg_write(sc, reg, v &~ (1 << (qId % IX_QMGR_MIN_QUEUPP_QID)));
917 DPRINTF(sc->sc_dev, "%s(%u) 0x%lx: 0x%x => 0x%x\n",
918 __func__, qId, reg, v, aqm_reg_read(sc, reg));
926 * N.B. this function will return 0 if supplied 0.
928 for (count = 0; n/2; count++)
933 static __inline unsigned
934 toAqmEntrySize(int entrySize)
936 /* entrySize 1("00"),2("01"),4("10") */
937 return log2(entrySize);
940 static __inline unsigned
941 toAqmBufferSize(unsigned bufferSizeInWords)
943 /* bufferSize 16("00"),32("01),64("10"),128("11") */
944 return log2(bufferSizeInWords / IX_QMGR_MIN_BUFFER_SIZE);
947 static __inline unsigned
948 toAqmWatermark(int watermark)
951 * Watermarks 0("000"),1("001"),2("010"),4("011"),
952 * 8("100"),16("101"),32("110"),64("111")
954 return log2(2 * watermark);
958 aqm_qcfg(struct ixpqmgr_softc *sc, int qId, u_int ne, u_int nf)
960 const struct qmgrInfo *qi = &sc->qinfo[qId];
962 uint32_t baseAddress;
964 /* Build config register */
965 qCfg = ((toAqmEntrySize(1) & IX_QMGR_ENTRY_SIZE_MASK) <<
966 IX_QMGR_Q_CONFIG_ESIZE_OFFSET)
967 | ((toAqmBufferSize(qi->qSizeInWords) & IX_QMGR_SIZE_MASK) <<
968 IX_QMGR_Q_CONFIG_BSIZE_OFFSET);
970 /* baseAddress, calculated relative to start address */
971 baseAddress = sc->aqmFreeSramAddress;
973 /* base address must be word-aligned */
974 KASSERT((baseAddress % IX_QMGR_BASE_ADDR_16_WORD_ALIGN) == 0,
975 ("address not word-aligned"));
977 /* Now convert to a 16 word pointer as required by QUECONFIG register */
978 baseAddress >>= IX_QMGR_BASE_ADDR_16_WORD_SHIFT;
979 qCfg |= baseAddress << IX_QMGR_Q_CONFIG_BADDR_OFFSET;
982 qCfg |= (toAqmWatermark(ne) << IX_QMGR_Q_CONFIG_NE_OFFSET)
983 | (toAqmWatermark(nf) << IX_QMGR_Q_CONFIG_NF_OFFSET);
985 DPRINTF(sc->sc_dev, "%s(%u, %u, %u) 0x%x => 0x%x @ 0x%x\n",
986 __func__, qId, ne, nf,
987 aqm_reg_read(sc, IX_QMGR_Q_CONFIG_ADDR_GET(qId)),
988 qCfg, IX_QMGR_Q_CONFIG_ADDR_GET(qId));
990 aqm_reg_write(sc, IX_QMGR_Q_CONFIG_ADDR_GET(qId), qCfg);
994 aqm_srcsel_write(struct ixpqmgr_softc *sc, int qId, int sourceId)
1000 * Calculate the register offset; multiple queues split across registers
1002 off = IX_QMGR_INT0SRCSELREG0_OFFSET +
1003 ((qId / IX_QMGR_INTSRC_NUM_QUE_PER_WORD) * sizeof(uint32_t));
1005 v = aqm_reg_read(sc, off);
1006 if (off == IX_QMGR_INT0SRCSELREG0_OFFSET && qId == 0) {
1007 /* Queue 0 at INT0SRCSELREG should not corrupt the value bit-3 */
1010 const uint32_t bpq = 32 / IX_QMGR_INTSRC_NUM_QUE_PER_WORD;
1014 qshift = (qId & (IX_QMGR_INTSRC_NUM_QUE_PER_WORD-1)) * bpq;
1015 mask = ((1 << bpq) - 1) << qshift; /* q's status mask */
1017 /* merge sourceId */
1018 v = (v &~ mask) | ((sourceId << qshift) & mask);
1021 DPRINTF(sc->sc_dev, "%s(%u, %u) 0x%x => 0x%x @ 0x%lx\n",
1022 __func__, qId, sourceId, aqm_reg_read(sc, off), v, off);
1023 aqm_reg_write(sc, off, v);
1027 * Reset AQM registers to default values.
1030 aqm_reset(struct ixpqmgr_softc *sc)
1034 /* Reset queues 0..31 status registers 0..3 */
1035 aqm_reg_write(sc, IX_QMGR_QUELOWSTAT0_OFFSET,
1036 IX_QMGR_QUELOWSTAT_RESET_VALUE);
1037 aqm_reg_write(sc, IX_QMGR_QUELOWSTAT1_OFFSET,
1038 IX_QMGR_QUELOWSTAT_RESET_VALUE);
1039 aqm_reg_write(sc, IX_QMGR_QUELOWSTAT2_OFFSET,
1040 IX_QMGR_QUELOWSTAT_RESET_VALUE);
1041 aqm_reg_write(sc, IX_QMGR_QUELOWSTAT3_OFFSET,
1042 IX_QMGR_QUELOWSTAT_RESET_VALUE);
1044 /* Reset underflow/overflow status registers 0..1 */
1045 aqm_reg_write(sc, IX_QMGR_QUEUOSTAT0_OFFSET,
1046 IX_QMGR_QUEUOSTAT_RESET_VALUE);
1047 aqm_reg_write(sc, IX_QMGR_QUEUOSTAT1_OFFSET,
1048 IX_QMGR_QUEUOSTAT_RESET_VALUE);
1050 /* Reset queues 32..63 nearly empty status registers */
1051 aqm_reg_write(sc, IX_QMGR_QUEUPPSTAT0_OFFSET,
1052 IX_QMGR_QUEUPPSTAT0_RESET_VALUE);
1054 /* Reset queues 32..63 full status registers */
1055 aqm_reg_write(sc, IX_QMGR_QUEUPPSTAT1_OFFSET,
1056 IX_QMGR_QUEUPPSTAT1_RESET_VALUE);
1058 /* Reset int0 status flag source select registers 0..3 */
1059 aqm_reg_write(sc, IX_QMGR_INT0SRCSELREG0_OFFSET,
1060 IX_QMGR_INT0SRCSELREG_RESET_VALUE);
1061 aqm_reg_write(sc, IX_QMGR_INT0SRCSELREG1_OFFSET,
1062 IX_QMGR_INT0SRCSELREG_RESET_VALUE);
1063 aqm_reg_write(sc, IX_QMGR_INT0SRCSELREG2_OFFSET,
1064 IX_QMGR_INT0SRCSELREG_RESET_VALUE);
1065 aqm_reg_write(sc, IX_QMGR_INT0SRCSELREG3_OFFSET,
1066 IX_QMGR_INT0SRCSELREG_RESET_VALUE);
1068 /* Reset queue interrupt enable register 0..1 */
1069 aqm_reg_write(sc, IX_QMGR_QUEIEREG0_OFFSET,
1070 IX_QMGR_QUEIEREG_RESET_VALUE);
1071 aqm_reg_write(sc, IX_QMGR_QUEIEREG1_OFFSET,
1072 IX_QMGR_QUEIEREG_RESET_VALUE);
1074 /* Reset queue interrupt register 0..1 */
1075 aqm_reg_write(sc, IX_QMGR_QINTREG0_OFFSET, IX_QMGR_QINTREG_RESET_VALUE);
1076 aqm_reg_write(sc, IX_QMGR_QINTREG1_OFFSET, IX_QMGR_QINTREG_RESET_VALUE);
1078 /* Reset queue configuration words 0..63 */
1079 for (i = 0; i < IX_QMGR_MAX_NUM_QUEUES; i++)
1080 aqm_reg_write(sc, sc->qinfo[i].qConfigRegAddr,
1081 IX_QMGR_QUECONFIG_RESET_VALUE);
1083 /* XXX zero SRAM to simplify debugging */
1084 for (i = IX_QMGR_QUEBUFFER_SPACE_OFFSET;
1085 i < IX_QMGR_AQM_SRAM_SIZE_IN_BYTES; i += sizeof(uint32_t))
1086 aqm_reg_write(sc, i, 0);
1089 static device_method_t ixpqmgr_methods[] = {
1090 DEVMETHOD(device_probe, ixpqmgr_probe),
1091 DEVMETHOD(device_attach, ixpqmgr_attach),
1092 DEVMETHOD(device_detach, ixpqmgr_detach),
1097 static driver_t ixpqmgr_driver = {
1100 sizeof(struct ixpqmgr_softc),
1102 static devclass_t ixpqmgr_devclass;
1104 DRIVER_MODULE(ixpqmgr, ixp, ixpqmgr_driver, ixpqmgr_devclass, 0, 0);