#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ixp425_qmgr.c,v 1.12 2022/09/27 06:12:58 skrll Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/time.h>
#include <sys/kmem.h>
#include <sys/resource.h>
#include <sys/bus.h>
#include <machine/cpu.h>
#include <machine/intr.h>
#include <arm/xscale/ixp425reg.h>
#include <arm/xscale/ixp425var.h>
#include <arm/xscale/ixp425_qmgr.h>
struct qmgrInfo {
int qSizeInWords;
uint32_t qOflowStatBitMask;
int qWriteCount;
bus_size_t qAccRegAddr;
bus_size_t qUOStatRegAddr;
bus_size_t qConfigRegAddr;
int qSizeInEntries;
uint32_t qUflowStatBitMask;
int qReadCount;
uint32_t qStatRegAddr;
uint32_t qStatBitsOffset;
uint32_t qStat0BitMask;
uint32_t qStat1BitMask;
uint32_t intRegCheckMask;
void (*cb)(int, void *);
void *cbarg;
int priority;
#if 0
u_int statusWordOffset;
uint32_t statusMask;
uint32_t statusCheckValue;
#endif
};
struct ixpqmgr_softc {
#ifdef __FreeBSD__
device_t sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
struct resource *sc_irq;
int sc_rid;
void *sc_ih;
#else
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
void *sc_ih[2];
#endif
struct qmgrInfo qinfo[IX_QMGR_MAX_NUM_QUEUES];
int priorityTable[IX_QMGR_MAX_NUM_QUEUES];
uint32_t lowPriorityTableFirstHalfMask;
uint32_t uppPriorityTableFirstHalfMask;
int rebuildTable;
uint32_t aqmFreeSramAddress;
};
static int qmgr_debug = 0;
#define DPRINTF(dev, fmt, ...) do { \
if (qmgr_debug) printf(fmt, __VA_ARGS__); \
} while (0)
#define DPRINTFn(n, dev, fmt, ...) do { \
if (qmgr_debug >= n) printf(fmt, __VA_ARGS__); \
} while (0)
static struct ixpqmgr_softc *ixpqmgr_sc = NULL;
static void ixpqmgr_rebuild(struct ixpqmgr_softc *);
static int ixpqmgr_intr(void *);
static void aqm_int_enable(struct ixpqmgr_softc *sc, int qId);
static void aqm_int_disable(struct ixpqmgr_softc *sc, int qId);
static void aqm_qcfg(struct ixpqmgr_softc *sc, int qId, u_int ne, u_int nf);
static void aqm_srcsel_write(struct ixpqmgr_softc *sc, int qId, int sourceId);
static void aqm_reset(struct ixpqmgr_softc *sc);
static void
dummyCallback(int qId, void *arg)
{
}
static uint32_t
aqm_reg_read(struct ixpqmgr_softc *sc, bus_size_t off)
{
DPRINTFn(9, sc->sc_dev, "%s(0x%x)\n", __func__, (int)off);
return bus_space_read_4(sc->sc_iot, sc->sc_ioh, off);
}
static void
aqm_reg_write(struct ixpqmgr_softc *sc, bus_size_t off, uint32_t val)
{
DPRINTFn(9, sc->sc_dev, "%s(0x%x, 0x%x)\n", __func__, (int)off, val);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, val);
}
#ifdef __FreeBSD__
static int
ixpqmgr_probe(device_t dev)
{
device_set_desc(dev, "IXP425 Q-Manager");
return 0;
}
#endif
#ifdef __FreeBSD__
static void
ixpqmgr_attach(device_t dev)
#else
void *
ixpqmgr_init(bus_space_tag_t iot)
#endif
{
#ifdef __FreeBSD__
struct ixpqmgr_softc *sc = device_get_softc(dev);
struct ixp425_softc *sa = device_get_softc(device_get_parent(dev));
#else
struct ixpqmgr_softc *sc;
#endif
int i;
#ifdef __FreeBSD__
ixpqmgr_sc = sc;
sc->sc_dev = dev;
sc->sc_iot = sa->sc_iot;
#else
sc = kmem_zalloc(sizeof(*sc), KM_SLEEP);
sc->sc_iot = iot;
#endif
if (bus_space_map(sc->sc_iot, IXP425_QMGR_HWBASE, IXP425_QMGR_SIZE,
0, &sc->sc_ioh))
panic("%s: Cannot map registers", __func__);
#ifdef __FreeBSD__
sc->sc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->sc_rid,
IXP425_INT_QUE1_32, IXP425_INT_QUE33_64, 2, RF_ACTIVE);
if (!sc->sc_irq)
panic("Unable to allocate the qmgr irqs.\n");
bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE,
ixpqmgr_intr, NULL, &sc->sc_ih);
#else
ixpqmgr_sc = sc;
sc->sc_ih[0] = ixp425_intr_establish(IXP425_INT_QUE1_32, IPL_NET,
ixpqmgr_intr, sc);
if (sc->sc_ih[0] == NULL) {
ixpqmgr_sc = NULL;
kmem_free(sc, sizeof(*sc));
return (NULL);
}
sc->sc_ih[1] = ixp425_intr_establish(IXP425_INT_QUE33_64, IPL_NET,
ixpqmgr_intr, sc);
if (sc->sc_ih[1] == NULL) {
ixp425_intr_disestablish(sc->sc_ih[0]);
ixpqmgr_sc = NULL;
kmem_free(sc, sizeof(*sc));
return (NULL);
}
#endif
for (i = 0; i < IX_QMGR_MAX_NUM_QUEUES; i++) {
struct qmgrInfo *qi = &sc->qinfo[i];
qi->cb = dummyCallback;
qi->priority = IX_QMGR_Q_PRIORITY_0;
qi->intRegCheckMask = (1<<(i%(IX_QMGR_MIN_QUEUPP_QID)));
qi->qAccRegAddr = IX_QMGR_Q_ACCESS_ADDR_GET(i);
qi->qAccRegAddr = IX_QMGR_Q_ACCESS_ADDR_GET(i);
qi->qConfigRegAddr = IX_QMGR_Q_CONFIG_ADDR_GET(i);
if (i < IX_QMGR_MIN_QUEUPP_QID) {
qi->qUOStatRegAddr = IX_QMGR_QUEUOSTAT0_OFFSET +
((i / IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD) *
sizeof(uint32_t));
qi->qUflowStatBitMask =
(IX_QMGR_UNDERFLOW_BIT_OFFSET + 1) <<
((i & (IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD - 1)) *
(32 / IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD));
qi->qOflowStatBitMask =
(IX_QMGR_OVERFLOW_BIT_OFFSET + 1) <<
((i & (IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD - 1)) *
(32 / IX_QMGR_QUEUOSTAT_NUM_QUE_PER_WORD));
qi->qStatRegAddr = IX_QMGR_QUELOWSTAT0_OFFSET +
((i / IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD) *
sizeof(uint32_t));
qi->qStatBitsOffset =
(i & (IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD - 1)) *
(32 / IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD);
} else {
qi->qUOStatRegAddr = 0;
qi->qStat0BitMask = (1 << (i - IX_QMGR_MIN_QUEUPP_QID));
qi->qStat1BitMask = (1 << (i - IX_QMGR_MIN_QUEUPP_QID));
}
}
sc->aqmFreeSramAddress = 0x100;
ixpqmgr_rebuild(sc);
aqm_reset(sc);
return (sc);
}
#ifdef __FreeBSD__
static void
ixpqmgr_detach(device_t dev)
{
struct ixpqmgr_softc *sc = device_get_softc(dev);
aqm_reset(sc);
bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid, sc->sc_irq);
bus_space_unmap(sc->sc_iot, sc->sc_ioh, IXP425_QMGR_SIZE);
}
#endif
int
ixpqmgr_qconfig(int qId, int qEntries, int ne, int nf, int srcSel,
void (*cb)(int, void *), void *cbarg)
{
struct ixpqmgr_softc *sc = ixpqmgr_sc;
struct qmgrInfo *qi = &sc->qinfo[qId];
DPRINTF(sc->sc_dev, "%s(%u, %u, %u, %u, %u, %p, %p)\n",
__func__, qId, qEntries, ne, nf, srcSel, cb, cbarg);
qi->qSizeInWords = qEntries;
qi->qReadCount = 0;
qi->qWriteCount = 0;
qi->qSizeInEntries = qEntries;
if (cb == NULL) {
qi->cb = dummyCallback;
qi->cbarg = 0;
} else {
qi->cb = cb;
qi->cbarg = cbarg;
}
aqm_qcfg(sc, qId, ne, nf);
sc->aqmFreeSramAddress += (qi->qSizeInWords * sizeof(uint32_t));
if (qId < IX_QMGR_MIN_QUEUPP_QID)
aqm_srcsel_write(sc, qId, srcSel);
if (cb != NULL)
aqm_int_enable(sc, qId);
sc->rebuildTable = true;
return 0;
}
int
ixpqmgr_qwrite(int qId, uint32_t entry)
{
struct ixpqmgr_softc *sc = ixpqmgr_sc;
struct qmgrInfo *qi = &sc->qinfo[qId];
DPRINTFn(3, sc->sc_dev, "%s(%u, 0x%x) writeCount %u size %u\n",
__func__, qId, entry, qi->qWriteCount, qi->qSizeInEntries);
aqm_reg_write(sc, qi->qAccRegAddr, entry);
if (qId < IX_QMGR_MIN_QUEUPP_QID) {
int qSize = qi->qSizeInEntries;
if (qi->qWriteCount++ == qSize) {
uint32_t status = aqm_reg_read(sc, qi->qUOStatRegAddr);
int qPtrs;
if ((status & qi->qOflowStatBitMask) ||
((status = aqm_reg_read(sc, qi->qUOStatRegAddr)) & qi->qOflowStatBitMask)) {
aqm_reg_write(sc, qi->qUOStatRegAddr,
status & ~qi->qOflowStatBitMask);
qi->qWriteCount = qSize;
DPRINTFn(5, sc->sc_dev,
"%s(%u, 0x%x) Q full, overflow status cleared\n",
__func__, qId, entry);
return ENOSPC;
}
qPtrs = aqm_reg_read(sc, qi->qConfigRegAddr);
DPRINTFn(2, sc->sc_dev,
"%s(%u, 0x%x) Q full, no overflow status, qConfig 0x%x\n",
__func__, qId, entry, qPtrs);
qPtrs = (qPtrs - (qPtrs >> 7)) & 0x7f;
if (qPtrs == 0) {
qi->qWriteCount = qSize;
} else {
qi->qWriteCount = qPtrs & (qSize - 1);
}
}
}
return 0;
}
int
ixpqmgr_qread(int qId, uint32_t *entry)
{
struct ixpqmgr_softc *sc = ixpqmgr_sc;
struct qmgrInfo *qi = &sc->qinfo[qId];
bus_size_t off = qi->qAccRegAddr;
*entry = aqm_reg_read(sc, off);
qi->qReadCount = 0;
if (*entry == 0 && qId < IX_QMGR_MIN_QUEUPP_QID) {
uint32_t status = aqm_reg_read(sc, qi->qUOStatRegAddr);
if (status & qi->qUflowStatBitMask) {
aqm_reg_write(sc, qi->qUOStatRegAddr,
status &~ qi->qUflowStatBitMask);
return ENOSPC;
}
}
return 0;
}
int
ixpqmgr_qreadm(int qId, uint32_t n, uint32_t *p)
{
struct ixpqmgr_softc *sc = ixpqmgr_sc;
struct qmgrInfo *qi = &sc->qinfo[qId];
uint32_t entry;
bus_size_t off = qi->qAccRegAddr;
entry = aqm_reg_read(sc, off);
while (--n) {
if (entry == 0) {
break;
}
*p++ = entry;
entry = aqm_reg_read(sc, off);
}
*p = entry;
qi->qReadCount = 0;
if (entry == 0 && qId < IX_QMGR_MIN_QUEUPP_QID) {
uint32_t status = aqm_reg_read(sc, qi->qUOStatRegAddr);
if (status & qi->qUflowStatBitMask) {
aqm_reg_write(sc, qi->qUOStatRegAddr,
status &~ qi->qUflowStatBitMask);
return ENOSPC;
}
}
return 0;
}
uint32_t
ixpqmgr_getqstatus(int qId)
{
#define QLOWSTATMASK \
((1 << (32 / IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD)) - 1)
struct ixpqmgr_softc *sc = ixpqmgr_sc;
const struct qmgrInfo *qi = &sc->qinfo[qId];
uint32_t status;
if (qId < IX_QMGR_MIN_QUEUPP_QID) {
status = aqm_reg_read(sc, qi->qStatRegAddr);
status = (status >> qi->qStatBitsOffset) & QLOWSTATMASK;
} else {
status = 0;
if (aqm_reg_read(sc, IX_QMGR_QUEUPPSTAT0_OFFSET)&qi->qStat0BitMask)
status |= IX_QMGR_Q_STATUS_NE_BIT_MASK;
if (aqm_reg_read(sc, IX_QMGR_QUEUPPSTAT1_OFFSET)&qi->qStat1BitMask)
status |= IX_QMGR_Q_STATUS_F_BIT_MASK;
}
return status;
#undef QLOWSTATMASK
}
uint32_t
ixpqmgr_getqconfig(int qId)
{
struct ixpqmgr_softc *sc = ixpqmgr_sc;
return aqm_reg_read(sc, IX_QMGR_Q_CONFIG_ADDR_GET(qId));
}
void
ixpqmgr_dump(void)
{
struct ixpqmgr_softc *sc = ixpqmgr_sc;
int i, a;
printf("0x%04x: %08x %08x %08x %08x\n"
, 0x400
, aqm_reg_read(sc, 0x400)
, aqm_reg_read(sc, 0x400+4)
, aqm_reg_read(sc, 0x400+8)
, aqm_reg_read(sc, 0x400+12)
);
printf("0x%04x: %08x %08x %08x %08x\n"
, 0x410
, aqm_reg_read(sc, 0x410)
, aqm_reg_read(sc, 0x410+4)
, aqm_reg_read(sc, 0x410+8)
, aqm_reg_read(sc, 0x410+12)
);
printf("0x%04x: %08x %08x %08x %08x\n"
, 0x420
, aqm_reg_read(sc, 0x420)
, aqm_reg_read(sc, 0x420+4)
, aqm_reg_read(sc, 0x420+8)
, aqm_reg_read(sc, 0x420+12)
);
printf("0x%04x: %08x %08x %08x %08x\n"
, 0x430
, aqm_reg_read(sc, 0x430)
, aqm_reg_read(sc, 0x430+4)
, aqm_reg_read(sc, 0x430+8)
, aqm_reg_read(sc, 0x430+12)
);
for (a = 0x2000; a < 0x20ff; a += 32)
printf("0x%04x: %08x %08x %08x %08x %08x %08x %08x %08x\n"
, a
, aqm_reg_read(sc, a)
, aqm_reg_read(sc, a+4)
, aqm_reg_read(sc, a+8)
, aqm_reg_read(sc, a+12)
, aqm_reg_read(sc, a+16)
, aqm_reg_read(sc, a+20)
, aqm_reg_read(sc, a+24)
, aqm_reg_read(sc, a+28)
);
for (i = 0x100; i < sc->aqmFreeSramAddress; i += 32) {
a = 0x2000 + i;
printf("0x%04x: %08x %08x %08x %08x %08x %08x %08x %08x\n"
, a
, aqm_reg_read(sc, a)
, aqm_reg_read(sc, a+4)
, aqm_reg_read(sc, a+8)
, aqm_reg_read(sc, a+12)
, aqm_reg_read(sc, a+16)
, aqm_reg_read(sc, a+20)
, aqm_reg_read(sc, a+24)
, aqm_reg_read(sc, a+28)
);
}
for (i = 0; i < 16; i++) {
printf("Q[%2d] config 0x%08x status 0x%02x "
"Q[%2d] config 0x%08x status 0x%02x\n"
, i, ixpqmgr_getqconfig(i), ixpqmgr_getqstatus(i)
, i+16, ixpqmgr_getqconfig(i+16), ixpqmgr_getqstatus(i+16)
);
}
}
void
ixpqmgr_notify_enable(int qId, int srcSel)
{
struct ixpqmgr_softc *sc = ixpqmgr_sc;
#if 0
aqm_calc_statuscheck(sc, qId, srcSel);
#endif
if (qId < IX_QMGR_MIN_QUEUPP_QID)
aqm_srcsel_write(sc, qId, srcSel);
aqm_int_enable(sc, qId);
}
void
ixpqmgr_notify_disable(int qId)
{
struct ixpqmgr_softc *sc = ixpqmgr_sc;
aqm_int_disable(sc, qId);
}
static void
ixpqmgr_rebuild(struct ixpqmgr_softc *sc)
{
int q, pri;
int lowQuePriorityTableIndex, uppQuePriorityTableIndex;
struct qmgrInfo *qi;
sc->lowPriorityTableFirstHalfMask = 0;
sc->uppPriorityTableFirstHalfMask = 0;
lowQuePriorityTableIndex = 0;
uppQuePriorityTableIndex = 32;
for (pri = 0; pri < IX_QMGR_NUM_PRIORITY_LEVELS; pri++) {
for (q = 0; q < IX_QMGR_MIN_QUEUPP_QID; q++) {
qi = &sc->qinfo[q];
if (qi->priority == pri) {
if (lowQuePriorityTableIndex < 16) {
sc->lowPriorityTableFirstHalfMask |=
qi->intRegCheckMask;
}
sc->priorityTable[lowQuePriorityTableIndex++] = q;
}
}
for (; q < IX_QMGR_MAX_NUM_QUEUES; q++) {
qi = &sc->qinfo[q];
if (qi->priority == pri) {
if (uppQuePriorityTableIndex < 48) {
sc->uppPriorityTableFirstHalfMask |=
qi->intRegCheckMask;
}
sc->priorityTable[uppQuePriorityTableIndex++] = q;
}
}
}
sc->rebuildTable = false;
}
static unsigned int
_lzcount(uint32_t word)
{
unsigned int lzcount = 0;
if (word == 0)
return 32;
while ((word & 0x80000000) == 0) {
word <<= 1;
lzcount++;
}
return lzcount;
}
static int
ixpqmgr_intr(void *arg)
{
struct ixpqmgr_softc *sc = ixpqmgr_sc;
uint32_t intRegVal;
struct qmgrInfo *qi;
int priorityTableIndex;
int qIndex;
intRegVal = aqm_reg_read(sc, IX_QMGR_QINTREG0_OFFSET);
aqm_reg_write(sc, IX_QMGR_QINTREG0_OFFSET, intRegVal);
DPRINTFn(5, sc->sc_dev, "%s: ISR0 0x%x ISR1 0x%x\n",
__func__, intRegVal, aqm_reg_read(sc, IX_QMGR_QINTREG1_OFFSET));
if (intRegVal != 0) {
qIndex = (32 - 1) - _lzcount(intRegVal);
DPRINTFn(2, sc->sc_dev, "%s: ISR0 0x%x qIndex %u\n",
__func__, intRegVal, qIndex);
qi = &sc->qinfo[qIndex];
if (intRegVal == qi->intRegCheckMask) {
qi->cb(qIndex, qi->cbarg);
} else {
if (intRegVal & sc->lowPriorityTableFirstHalfMask) {
priorityTableIndex = 0;
} else {
priorityTableIndex = 16;
}
do {
qIndex = sc->priorityTable[priorityTableIndex++];
if (qIndex >= IX_QMGR_MAX_NUM_QUEUES)
break;
qi = &sc->qinfo[qIndex];
if (intRegVal & qi->intRegCheckMask) {
qi->cb(qIndex, qi->cbarg);
intRegVal &= ~qi->intRegCheckMask;
}
} while (intRegVal &&
priorityTableIndex < IX_QMGR_MAX_NUM_QUEUES);
}
}
if (sc->rebuildTable)
ixpqmgr_rebuild(sc);
return (1);
}
#if 0
static void
aqm_calc_statuscheck(int qId, IxQMgrSourceId srcSel)
{
struct qmgrInfo *qi = &qinfo[qId];
uint32_t shiftVal;
if (qId < IX_QMGR_MIN_QUEUPP_QID) {
switch (srcSel) {
case IX_QMGR_Q_SOURCE_ID_E:
qi->statusCheckValue = IX_QMGR_Q_STATUS_E_BIT_MASK;
qi->statusMask = IX_QMGR_Q_STATUS_E_BIT_MASK;
break;
case IX_QMGR_Q_SOURCE_ID_NE:
qi->statusCheckValue = IX_QMGR_Q_STATUS_NE_BIT_MASK;
qi->statusMask = IX_QMGR_Q_STATUS_NE_BIT_MASK;
break;
case IX_QMGR_Q_SOURCE_ID_NF:
qi->statusCheckValue = IX_QMGR_Q_STATUS_NF_BIT_MASK;
qi->statusMask = IX_QMGR_Q_STATUS_NF_BIT_MASK;
break;
case IX_QMGR_Q_SOURCE_ID_F:
qi->statusCheckValue = IX_QMGR_Q_STATUS_F_BIT_MASK;
qi->statusMask = IX_QMGR_Q_STATUS_F_BIT_MASK;
break;
case IX_QMGR_Q_SOURCE_ID_NOT_E:
qi->statusCheckValue = 0;
qi->statusMask = IX_QMGR_Q_STATUS_E_BIT_MASK;
break;
case IX_QMGR_Q_SOURCE_ID_NOT_NE:
qi->statusCheckValue = 0;
qi->statusMask = IX_QMGR_Q_STATUS_NE_BIT_MASK;
break;
case IX_QMGR_Q_SOURCE_ID_NOT_NF:
qi->statusCheckValue = 0;
qi->statusMask = IX_QMGR_Q_STATUS_NF_BIT_MASK;
break;
case IX_QMGR_Q_SOURCE_ID_NOT_F:
qi->statusCheckValue = 0;
qi->statusMask = IX_QMGR_Q_STATUS_F_BIT_MASK;
break;
default:
IX_OSAL_ASSERT(0);
break;
}
shiftVal = (qId % IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD) *
IX_QMGR_QUELOWSTAT_BITS_PER_Q;
qi->statusWordOffset = qId / IX_QMGR_QUELOWSTAT_NUM_QUE_PER_WORD;
qi->statusCheckValue <<= shiftVal;
qi->statusMask <<= shiftVal;
} else {
qi->statusWordOffset = 0;
qi->statusMask = 1 << (qId - IX_QMGR_MIN_QUEUPP_QID);
qi->statusCheckValue = qi->statusMask;
}
}
#endif
static void
aqm_int_enable(struct ixpqmgr_softc *sc, int qId)
{
bus_size_t reg;
uint32_t v;
if (qId < IX_QMGR_MIN_QUEUPP_QID)
reg = IX_QMGR_QUEIEREG0_OFFSET;
else
reg = IX_QMGR_QUEIEREG1_OFFSET;
v = aqm_reg_read(sc, reg);
aqm_reg_write(sc, reg, v | (1 << (qId % IX_QMGR_MIN_QUEUPP_QID)));
DPRINTF(sc->sc_dev, "%s(%u) 0x%lx: 0x%x => 0x%x\n",
__func__, qId, reg, v, aqm_reg_read(sc, reg));
}
static void
aqm_int_disable(struct ixpqmgr_softc *sc, int qId)
{
bus_size_t reg;
uint32_t v;
if (qId < IX_QMGR_MIN_QUEUPP_QID)
reg = IX_QMGR_QUEIEREG0_OFFSET;
else
reg = IX_QMGR_QUEIEREG1_OFFSET;
v = aqm_reg_read(sc, reg);
aqm_reg_write(sc, reg, v &~ (1 << (qId % IX_QMGR_MIN_QUEUPP_QID)));
DPRINTF(sc->sc_dev, "%s(%u) 0x%lx: 0x%x => 0x%x\n",
__func__, qId, reg, v, aqm_reg_read(sc, reg));
}
static unsigned
log2(unsigned n)
{
unsigned count;
for (count = 0; n/2; count++)
n /= 2;
return count;
}
static __inline unsigned
toAqmEntrySize(int entrySize)
{
return log2(entrySize);
}
static __inline unsigned
toAqmBufferSize(unsigned bufferSizeInWords)
{
return log2(bufferSizeInWords / IX_QMGR_MIN_BUFFER_SIZE);
}
static __inline unsigned
toAqmWatermark(int watermark)
{
return log2(2 * watermark);
}
static void
aqm_qcfg(struct ixpqmgr_softc *sc, int qId, u_int ne, u_int nf)
{
const struct qmgrInfo *qi = &sc->qinfo[qId];
uint32_t qCfg;
uint32_t baseAddress;
qCfg = ((toAqmEntrySize(1) & IX_QMGR_ENTRY_SIZE_MASK) <<
IX_QMGR_Q_CONFIG_ESIZE_OFFSET)
| ((toAqmBufferSize(qi->qSizeInWords) & IX_QMGR_SIZE_MASK) <<
IX_QMGR_Q_CONFIG_BSIZE_OFFSET);
baseAddress = sc->aqmFreeSramAddress;
KASSERT((baseAddress % IX_QMGR_BASE_ADDR_16_WORD_ALIGN) == 0);
baseAddress >>= IX_QMGR_BASE_ADDR_16_WORD_SHIFT;
qCfg |= baseAddress << IX_QMGR_Q_CONFIG_BADDR_OFFSET;
qCfg |= (toAqmWatermark(ne) << IX_QMGR_Q_CONFIG_NE_OFFSET)
| (toAqmWatermark(nf) << IX_QMGR_Q_CONFIG_NF_OFFSET);
DPRINTF(sc->sc_dev, "%s(%u, %u, %u) 0x%x => 0x%x @ 0x%x\n",
__func__, qId, ne, nf,
aqm_reg_read(sc, IX_QMGR_Q_CONFIG_ADDR_GET(qId)),
qCfg, (u_int)IX_QMGR_Q_CONFIG_ADDR_GET(qId));
aqm_reg_write(sc, IX_QMGR_Q_CONFIG_ADDR_GET(qId), qCfg);
}
static void
aqm_srcsel_write(struct ixpqmgr_softc *sc, int qId, int sourceId)
{
bus_size_t off;
uint32_t v;
off = IX_QMGR_INT0SRCSELREG0_OFFSET +
((qId / IX_QMGR_INTSRC_NUM_QUE_PER_WORD) * sizeof(uint32_t));
v = aqm_reg_read(sc, off);
if (off == IX_QMGR_INT0SRCSELREG0_OFFSET && qId == 0) {
v |= 0x7;
} else {
const uint32_t bpq = 32 / IX_QMGR_INTSRC_NUM_QUE_PER_WORD;
uint32_t mask;
int qshift;
qshift = (qId & (IX_QMGR_INTSRC_NUM_QUE_PER_WORD-1)) * bpq;
mask = ((1 << bpq) - 1) << qshift;
v = (v &~ mask) | ((sourceId << qshift) & mask);
}
DPRINTF(sc->sc_dev, "%s(%u, %u) 0x%x => 0x%x @ 0x%lx\n",
__func__, qId, sourceId, aqm_reg_read(sc, off), v, off);
aqm_reg_write(sc, off, v);
}
static void
aqm_reset(struct ixpqmgr_softc *sc)
{
int i;
aqm_reg_write(sc, IX_QMGR_QUELOWSTAT0_OFFSET,
IX_QMGR_QUELOWSTAT_RESET_VALUE);
aqm_reg_write(sc, IX_QMGR_QUELOWSTAT1_OFFSET,
IX_QMGR_QUELOWSTAT_RESET_VALUE);
aqm_reg_write(sc, IX_QMGR_QUELOWSTAT2_OFFSET,
IX_QMGR_QUELOWSTAT_RESET_VALUE);
aqm_reg_write(sc, IX_QMGR_QUELOWSTAT3_OFFSET,
IX_QMGR_QUELOWSTAT_RESET_VALUE);
aqm_reg_write(sc, IX_QMGR_QUEUOSTAT0_OFFSET,
IX_QMGR_QUEUOSTAT_RESET_VALUE);
aqm_reg_write(sc, IX_QMGR_QUEUOSTAT1_OFFSET,
IX_QMGR_QUEUOSTAT_RESET_VALUE);
aqm_reg_write(sc, IX_QMGR_QUEUPPSTAT0_OFFSET,
IX_QMGR_QUEUPPSTAT0_RESET_VALUE);
aqm_reg_write(sc, IX_QMGR_QUEUPPSTAT1_OFFSET,
IX_QMGR_QUEUPPSTAT1_RESET_VALUE);
aqm_reg_write(sc, IX_QMGR_INT0SRCSELREG0_OFFSET,
IX_QMGR_INT0SRCSELREG_RESET_VALUE);
aqm_reg_write(sc, IX_QMGR_INT0SRCSELREG1_OFFSET,
IX_QMGR_INT0SRCSELREG_RESET_VALUE);
aqm_reg_write(sc, IX_QMGR_INT0SRCSELREG2_OFFSET,
IX_QMGR_INT0SRCSELREG_RESET_VALUE);
aqm_reg_write(sc, IX_QMGR_INT0SRCSELREG3_OFFSET,
IX_QMGR_INT0SRCSELREG_RESET_VALUE);
aqm_reg_write(sc, IX_QMGR_QUEIEREG0_OFFSET,
IX_QMGR_QUEIEREG_RESET_VALUE);
aqm_reg_write(sc, IX_QMGR_QUEIEREG1_OFFSET,
IX_QMGR_QUEIEREG_RESET_VALUE);
aqm_reg_write(sc, IX_QMGR_QINTREG0_OFFSET, IX_QMGR_QINTREG_RESET_VALUE);
aqm_reg_write(sc, IX_QMGR_QINTREG1_OFFSET, IX_QMGR_QINTREG_RESET_VALUE);
for (i = 0; i < IX_QMGR_MAX_NUM_QUEUES; i++)
aqm_reg_write(sc, sc->qinfo[i].qConfigRegAddr,
IX_QMGR_QUECONFIG_RESET_VALUE);
for (i = IX_QMGR_QUEBUFFER_SPACE_OFFSET;
i < IX_QMGR_AQM_SRAM_SIZE_IN_BYTES; i += sizeof(uint32_t))
aqm_reg_write(sc, i, 0);
}
#ifdef __FreeBSD__
static device_method_t ixpqmgr_methods[] = {
DEVMETHOD(device_probe, ixpqmgr_probe),
DEVMETHOD(device_attach, ixpqmgr_attach),
DEVMETHOD(device_detach, ixpqmgr_detach),
{ 0, 0 }
};
static driver_t ixpqmgr_driver = {
"ixpqmgr",
ixpqmgr_methods,
sizeof(struct ixpqmgr_softc),
};
static devclass_t ixpqmgr_devclass;
DRIVER_MODULE(ixpqmgr, ixp, ixpqmgr_driver, ixpqmgr_devclass, 0, 0);
#endif