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