root/usr/src/uts/common/io/chxge/sge.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * This file is part of the Chelsio T1 Ethernet driver.
 *
 * Copyright (C) 2003-2005 Chelsio Communications.  All rights reserved.
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/cmn_err.h>
#include <sys/sunddi.h>
#include <sys/kmem.h>
#include <sys/cmn_err.h>
#include <sys/byteorder.h>
#include <sys/atomic.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/strsubr.h>
#include <sys/dlpi.h>
#include <sys/kstat.h>
#include <sys/ethernet.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <inet/common.h>
#include <inet/nd.h>
#include <inet/ip.h>
#include <inet/tcp.h>
#include <netinet/udp.h>
#include <sys/gld.h>
#include "ostypes.h"
#include "common.h"
#ifdef CONFIG_CHELSIO_T1_1G
#include "fpga_defs.h"
#endif
#include "regs.h"
#include "suni1x10gexp_regs.h"
#include "sge.h"
#include "espi.h"

#include "ch.h"

extern uint32_t buffers_in_use[];

uint32_t sge_cmdq0_cnt = SGE_CMDQ0_E_N;
uint32_t sge_cmdq1_cnt = SGE_CMDQ1_E_N;
uint32_t sge_flq0_cnt = SGE_FREELQ0_E_N;
uint32_t sge_flq1_cnt = SGE_FREELQ1_E_N;
uint32_t sge_respq_cnt = SGE_RESPQ_E_N;

uint32_t sge_cmdq0_cnt_orig = SGE_CMDQ0_E_N;
uint32_t sge_cmdq1_cnt_orig = SGE_CMDQ1_E_N;
uint32_t sge_flq0_cnt_orig = SGE_FREELQ0_E_N;
uint32_t sge_flq1_cnt_orig = SGE_FREELQ1_E_N;
uint32_t sge_respq_cnt_orig = SGE_RESPQ_E_N;

#ifdef HOST_PAUSE
uint32_t do_host_pause = 1;
uint32_t flq_pause_window = 64;
#endif

static uint64_t os_freelist_buffer_alloc(ch_t *sa, int sz, mblk_t **mb,
    ulong_t *dh);
void pe_os_free_contig(ch_t *, size_t, void *, uint64_t, ulong_t, ulong_t);

static inline uint32_t t1_sge_rx(pesge *sge, freelQ_t *Q,
    unsigned int len, unsigned int offload);
#ifdef HOST_PAUSE
static void t1_sge_check_pause(pesge *sge, struct freelQ *Q);
#endif
static void alloc_freelQ_buffers(pesge *sge, struct freelQ *Q);
static void freelQs_empty(pesge *sge);
static void free_cmdQ_buffers(pesge *sge, cmdQ_t *Q, uint32_t credits_pend);
static int alloc_rx_resources(pesge *sge, struct sge_params *p);
static int alloc_tx_resources(pesge *sge, struct sge_params *p);
static inline void setup_ring_params(ch_t *adapter, u64 addr, u32 size,
    int base_reg_lo, int base_reg_hi, int size_reg);
static void configure_sge(pesge *sge, struct sge_params *p);
static void free_freelQ_buffers(pesge *sge, struct freelQ *Q);
static void free_rx_resources(pesge *sge);
static void free_tx_resources(pesge *sge);
static inline unsigned int jumbo_payload_capacity(pesge *sge);
#ifdef SUN_KSTATS
static int sge_kstat_setup(pesge *);
static void sge_kstat_remove(pesge *);
static int sge_kstat_update(p_kstat_t, int);
#endif
static uint16_t calc_ocsum(mblk_t *, int);

/*
 * Local routines.
 */
static inline void sge_ring_doorbell(pesge *sge, u32 control_reg);

static inline void
sge_ring_doorbell(pesge *sge, u32 control_reg)
{
        membar_producer();
        t1_write_reg_4(sge->obj, A_SG_DOORBELL, control_reg);
}

/*
 * DESC:
 *
 * NOTES:   Must have at least 1 command queue and 1 freelist queue.
 *
 */
pesge *
t1_sge_create(ch_t *sa, struct sge_params *p)
{
        pesge *sge;

        sge = t1_os_malloc_wait_zero(sizeof (pesge));

        if (sge == NULL)
                goto error_no_mem;

        memset(sge, 0, sizeof (*sge));

        /*
         * PR2928 & PR3309
         * set default timeout value - 20 msec
         * we set the initial value to 2 which gurantees at least one tick.
         */
        if (is_T2(sa))
                sge->ptimeout = 1;

        sge->obj = sa;
#ifdef SUN_KSTATS
        if (sge_kstat_setup(sge) != 0)
                goto t1_sge_create_fail1;
#endif
        p->cmdQ_size[0] = sge_cmdq0_cnt;
        p->cmdQ_size[1] = sge_cmdq1_cnt;

        /* note that jumbo frame index is inverted for T2 */
        if (is_T2(sa)) {
                p->freelQ_size[1] = sge_flq0_cnt;
                p->freelQ_size[0] = sge_flq1_cnt;
        } else {
                p->freelQ_size[0] = sge_flq0_cnt;
                p->freelQ_size[1] = sge_flq1_cnt;
        }

#if CH_DEBUG
        /* DEBUG only */
        cmn_err(CE_NOTE, "sge: %p\n", sge);
        cmn_err(CE_NOTE, "&sge->cmdQ[0]: %p\n", &sge->cmdQ[0]);
        cmn_err(CE_NOTE, "&sge->freelQ[0]: %p\n", &sge->freelQ[0]);
        cmn_err(CE_NOTE, "&sge->freelQ[1]: %p\n", &sge->freelQ[1]);
        cmn_err(CE_NOTE, "&sge->respQ: %p\n", &sge->respQ);
        cmn_err(CE_NOTE, "&sge->intr_cnt: %p\n", &sge->intr_cnt);
#endif
#ifdef SUN_KSTATS
        goto error_no_mem;

t1_sge_create_fail1:
        t1_os_free(sge, sizeof (pesge));
        sge = NULL;
#endif
error_no_mem:
        return (sge);
}

int
t1_sge_destroy(pesge* sge)
{
        if (sge != NULL) {
                free_tx_resources(sge);
                free_rx_resources(sge);

                /* PR2928 & PR3309 */
                if ((is_T2(sge->obj)) && (sge->pskb))
                        pe_free_fake_arp(sge->pskb);
#ifdef SUN_KSTATS
                sge_kstat_remove(sge);
#endif
                t1_os_free(sge, sizeof (pesge));
        }
        return (0);
}

/*
 * PR2928 & PR3309
 * call out event from timeout
 *
 * there is a potential race between the timeout and the close.
 * unless we protect the timeout, the close could occur at the
 * same time. Then if the timeout service routine was slow or
 * interrupted, the sge_stop() could complete with a timeoutID
 * that has expired, thus letting another timeout occur. If the
 * service routine was delayed still further, a detach could occur.
 * the second time could then end up accessing memory that has been
 * released back to the system. Bad things could then occur. We
 * set a flag in sge_stop() to tell the service routine not to
 * issue further timeouts. sge_stop() will block until a timeout
 * has occured. If the command Q is full then we shouldn't put out
 * an arp.
 */

void
t1_espi_workaround(ch_t *adapter)
{
        pesge *sge = adapter->sge;
        ch_t *chp = (ch_t *)sge->obj;
        int rv = 1;

        if ((chp->ch_state == PERUNNING) &&
            atomic_read(sge->cmdQ[0].cq_asleep)) {
                u32 seop;
                seop = t1_espi_get_mon(adapter, 0x930, 0);
                if ((seop & 0xfff0fff) == 0xfff) {
                        /* after first arp */
                        if (sge->pskb) {
                                rv = pe_start(adapter, (mblk_t *)sge->pskb,
                                    CH_ARP);
                                if (!rv)
                                        sge->intr_cnt.arp_sent++;
                        }
                }
        }
#ifdef HOST_PAUSE
        /*
         * If we are already in sge_data_in, then we can skip calling
         * t1_sge_check_pause() this clock cycle. lockstat showed that
         * we were blocking on the mutex ~ 2% of the time.
         */
        if (mutex_tryenter(&adapter->ch_intr)) {
                t1_sge_check_pause(sge, &sge->freelQ[0]);
                t1_sge_check_pause(sge, &sge->freelQ[1]);
                mutex_exit(&adapter->ch_intr);
        }
#endif
}

int
sge_start(pesge *sge)
{
        t1_write_reg_4(sge->obj, A_SG_CONTROL, sge->sge_control);
        /* PR2928 & PR3309, also need to avoid Pause deadlock */
        ch_init_cyclic(sge->obj, &sge->espi_wa_cyclic,
            (void (*)(void *))t1_espi_workaround, sge->obj);
        ch_start_cyclic(&sge->espi_wa_cyclic, sge->ptimeout);
        return (0);
}

/*
 * Disables SGE queues.
 */
int
sge_stop(pesge *sge)
{
        uint32_t status;
        int loops;

        DBGASSERT(sge);

        /* PR2928 & PR3309, also need to avoid Pause deadlock */
        t1_write_reg_4(sge->obj, A_SG_CONTROL, 0x0);

        /* wait until there's no more outstanding interrupts pending */
        loops = 0;
        do {
                status = t1_read_reg_4(sge->obj, A_SG_INT_CAUSE);
                t1_write_reg_4(sge->obj, A_SG_INT_CAUSE, status);
                drv_usecwait(125);
                loops++;
        } while (status && (loops < 1000));

        ch_stop_cyclic(&sge->espi_wa_cyclic);

        return (0);
}

uint32_t sge_cmdq_send_fail;

int
sge_data_out(pesge* sge, int qid, mblk_t *m0,
    cmdQ_ce_t *cmp, int count, uint32_t flg)
{
        struct cmdQ *Q = &sge->cmdQ[qid];
        ddi_dma_handle_t dh = (ddi_dma_handle_t)sge->cmdQ[qid].cq_dh;
        spinlock_t *qlock = &Q->cq_qlock;
        cmdQ_e *e;
        cmdQ_e *q = Q->cq_entries;
        uint32_t credits;
        uint32_t pidx;
        uint32_t genbit;
        uint32_t entries_n = Q->cq_entries_n;
        cmdQ_ce_t *ce;
        cmdQ_ce_t *cq = Q->cq_centries;
        dma_addr_t mapping;
        uint32_t j = 0;
        uint32_t offset;
#if defined(TX_CKSUM_FIX)
        uint16_t csum;
        uint16_t *csum_loc;
#endif
#ifdef TX_THREAD_RECLAIM
        uint32_t reclaim_cnt;
#endif

        /*
         * We must exit if we don't have enough free command queue entries
         * available.
         */

        spin_lock(qlock);

#if defined(TX_CKSUM_FIX)
        /*
         * This checksum fix will address a fragmented datagram
         * checksum error. Which will lead to the next packet after
         * the last packet with the More fragment bit set having its
         * checksum corrupted. When the packet reaches this point
         * the 'flg' variable indicates whether a checksum is needed
         * or not. The algorithm is as follows, if the current packet
         * is a More fragment set the count of packets to be checksummed
         * after it to 3. If it't not and the count of is more than 0
         * then calculate the checksum in software, if a hardware checksum
         * was requested. Then decrment the count. Same algorithm applies
         * to TCP.
         */
        if (flg & CH_UDP_MF) {
                sge->do_udp_csum = 3;
        } else if ((flg & CH_UDP) && (sge->do_udp_csum != 0)) {
                if ((flg & CH_NO_HWCKSUM) == 0) {
                        /*
                         *  Calc Checksum here.
                         */
                        csum = calc_ocsum(m0,
                            sizeof (struct ether_header) + CPL_FORMAT_0_SIZE);
                        csum_loc = (uint16_t *)(m0->b_rptr +
                            sizeof (struct ether_header) + CPL_FORMAT_0_SIZE);
                        csum_loc += (((*(char *)csum_loc) & 0x0f) << 1);

                        sge->intr_cnt.tx_soft_cksums++;
                        ((struct udphdr *)(csum_loc))->uh_sum = csum;
                        ((struct cpl_tx_pkt *)m0->b_rptr)->l4_csum_dis = 1;
                }
                sge->do_udp_csum--;
        } else if (flg & CH_TCP_MF) {
                sge->do_tcp_csum = 3;
        } else if (sge->do_tcp_csum != 0) {
                if ((flg & CH_NO_HWCKSUM) == 0) {
                        sge->intr_cnt.tx_soft_cksums++;
                        /*
                         *  Calc Checksum here.
                         */
                }
                sge->do_tcp_csum--;
        }
#endif  /* TX_CKSUM_FIX */
#ifdef TX_THREAD_RECLAIM
        reclaim_cnt = Q->cq_complete;
        if (reclaim_cnt > SGE_BATCH_THRESH) {
                sge->intr_cnt.tx_reclaims[qid]++;
                free_cmdQ_buffers(sge, Q, reclaim_cnt);
                Q->cq_complete = 0;
        }
#endif
        genbit = Q->cq_genbit;
        pidx = Q->cq_pidx;
        credits = Q->cq_credits;

        if ((credits - 1) < count) {
                spin_unlock(qlock);
                sge->intr_cnt.cmdQ_full[qid]++;
                return (1);
        }

        atomic_sub(count, &Q->cq_credits);
        Q->cq_pidx += count;
        if (Q->cq_pidx >= entries_n) {
                Q->cq_pidx -= entries_n;
                Q->cq_genbit ^= 1;
        }

        spin_unlock(qlock);

#ifdef SUN_KSTATS
        if (count > MBLK_MAX)
                sge->intr_cnt.tx_descs[MBLK_MAX - 1]++;
        else
                sge->intr_cnt.tx_descs[count]++;
#endif

        ce = &cq[pidx];
        *ce = *cmp;
        mapping = cmp->ce_pa;
        j++;

        e = &q[pidx];

        offset = (caddr_t)e - (caddr_t)q;

        e->Sop =  1;
        e->DataValid = 1;
        e->BufferLength = cmp->ce_len;
        e->AddrHigh = ((u64)mapping >> 32);
        e->AddrLow = ((u64)mapping & 0xffffffff);

        --count;
        if (count > 0) {
                unsigned int i;

                e->Eop = 0;
                wmb();
                e->GenerationBit = e->GenerationBit2 = genbit;

                for (i = 0; i < count; i++) {

                        ce++;
                        e++;
                        cmp++;
                        if (++pidx == entries_n) {
                                pidx = 0;
                                genbit ^= 1;
                                /* sync from offset to end of cmdQ */
                                (void) ddi_dma_sync(dh, (off_t)(offset),
                                    j*sizeof (*e), DDI_DMA_SYNC_FORDEV);
                                offset = j = 0;
                                ce = cq;
                                e = q;
                        }

                        *ce = *cmp;
                        mapping = cmp->ce_pa;
                        j++;
                        e->Sop = 0;
                        e->DataValid = 1;
                        e->BufferLength = cmp->ce_len;
                        e->AddrHigh = ((u64)mapping >> 32);
                        e->AddrLow = ((u64)mapping & 0xffffffff);

                        if (i < (count - 1)) {
                                e->Eop = 0;
                                wmb();
                                e->GenerationBit = e->GenerationBit2 = genbit;
                        }
                }
        }

        ce->ce_mp = m0;

        e->Eop = 1;
        wmb();
        e->GenerationBit = e->GenerationBit2 = genbit;

        (void) ddi_dma_sync(dh, (off_t)(offset), j*sizeof (*e),
            DDI_DMA_SYNC_FORDEV);

        /*
         * We always ring the doorbell for cmdQ1.  For cmdQ0, we only ring
         * the doorbell if the Q is asleep. There is a natural race, where
         * the hardware is going to sleep just after we checked, however,
         * then the interrupt handler will detect the outstanding TX packet
         * and ring the doorbell for us.
         */
        if (qid) {
                doorbell_pio(sge, F_CMDQ1_ENABLE);
        } else {
                if (atomic_read(Q->cq_asleep)) {
                        atomic_set(&Q->cq_asleep, 0);
/* NOT YET              doorbell_pio(sge, F_CMDQ0_ENABLE); */
                        atomic_set(&Q->cq_pio_pidx, Q->cq_pidx);
                }
        }
        doorbell_pio(sge, F_CMDQ0_ENABLE);

        return (0);
}

#define SGE_PL_INTR_MASK (F_PL_INTR_SGE_ERR | F_PL_INTR_SGE_DATA)

/*
 * Disable SGE error interrupts.
 */
int
t1_sge_intr_disable(pesge* sge)
{
        u32 val = t1_read_reg_4(sge->obj, A_PL_ENABLE);

        t1_write_reg_4(sge->obj, A_PL_ENABLE, val & ~SGE_PL_INTR_MASK);
        t1_write_reg_4(sge->obj, A_SG_INT_ENABLE, 0);
        return (0);
}

#define SGE_INT_ENABLE (F_RESPQ_EXHAUSTED | F_RESPQ_OVERFLOW | \
        F_FL_EXHAUSTED | F_PACKET_TOO_BIG | F_PACKET_MISMATCH)

/*
 * Enable SGE error interrupts.
 */
int
t1_sge_intr_enable(pesge* sge)
{
        u32 en = SGE_INT_ENABLE;
        u32 val = t1_read_reg_4(sge->obj, A_PL_ENABLE);

        t1_write_reg_4(sge->obj, A_PL_ENABLE, val | SGE_PL_INTR_MASK);

        if (sge->obj->ch_flags & TSO_CAPABLE)
                en &= ~F_PACKET_TOO_BIG;
        t1_write_reg_4(sge->obj, A_SG_INT_ENABLE, en);
        return (0);
}

/*
 * Clear SGE error interrupts.
 */
int
t1_sge_intr_clear(pesge* sge)
{
        t1_write_reg_4(sge->obj, A_PL_CAUSE, SGE_PL_INTR_MASK);
        t1_write_reg_4(sge->obj, A_SG_INT_CAUSE, 0xffffffff);
        return (0);
}

#define SGE_INT_FATAL (F_RESPQ_OVERFLOW | F_PACKET_TOO_BIG | F_PACKET_MISMATCH)

int
t1_sge_intr_error_handler(pesge *sge)
{
        peobj *obj = sge->obj;
        u32 cause = t1_read_reg_4(obj, A_SG_INT_CAUSE);

        if (cause & F_RESPQ_EXHAUSTED)
                sge->intr_cnt.respQ_empty++;
        if (cause & F_RESPQ_OVERFLOW) {
                sge->intr_cnt.respQ_overflow++;
                cmn_err(CE_WARN, "%s: SGE response queue overflow\n",
                    obj->ch_name);
        }
        if (cause & F_FL_EXHAUSTED) {
                sge->intr_cnt.freelistQ_empty++;
                freelQs_empty(sge);
        }
        if (cause & F_PACKET_TOO_BIG) {
                sge->intr_cnt.pkt_too_big++;
                cmn_err(CE_WARN, "%s: SGE max packet size exceeded\n",
                    obj->ch_name);
        }
        if (cause & F_PACKET_MISMATCH) {
                sge->intr_cnt.pkt_mismatch++;
                cmn_err(CE_WARN, "%s: SGE packet mismatch\n",
                    obj->ch_name);
        }
        if (cause & SGE_INT_FATAL)
                t1_fatal_err(obj);

        t1_write_reg_4(obj, A_SG_INT_CAUSE, cause);
        return (0);
}

/*
 *
 * PARAM:   sge     - SGE instance pointer.
 */
int
sge_data_in(pesge *sge)
{
        peobj *adapter = sge->obj;
        struct respQ *Q = &sge->respQ;
        respQ_e *e;                             /* response queue entry */
        respQ_e *q = Q->rq_entries;             /* base response queue */
        uint32_t cidx = Q->rq_cidx;
        uint32_t genbit = Q->rq_genbit;
        uint32_t entries_n = Q->rq_entries_n;
        uint32_t credits = Q->rq_credits;
        uint32_t credits_thresh = Q->rq_credits_thresh;
        uint32_t ret = 0;
#ifndef TX_THREAD_RECLAIM
        uint32_t credits_pend[2] = {0, 0};
#endif
        uint32_t flags = 0;
        uint32_t flagt;
        ddi_dma_handle_t dh = (ddi_dma_handle_t)Q->rq_dh;

        t1_write_reg_4(adapter, A_PL_CAUSE, F_PL_INTR_SGE_DATA);

        /*
         * Catch the case where an interrupt arrives
         * early.
         */
        if ((q == NULL) || (dh == NULL)) {
                goto check_slow_ints;
        }

        /* initial response queue entry */
        e = &q[cidx];

        /* pull physical memory of response queue entry into cache */
        (void) ddi_dma_sync(dh, (off_t)((caddr_t)e - (caddr_t)q),
            sizeof (*e), DDI_DMA_SYNC_FORKERNEL);

        while (e->GenerationBit == genbit) {
                if (--credits < credits_thresh) {
                        uint32_t n = entries_n - credits - 1;
                        t1_write_reg_4(adapter, A_SG_RSPQUEUECREDIT, n);
                        credits += n;
                }
                if (likely(e->DataValid)) {
                        (void) t1_sge_rx(sge, &sge->freelQ[e->FreelistQid],
                            e->BufferLength, e->Offload);
                        if ((e->Sop != 1) || (e->Eop != 1)) {
                                sge->intr_cnt.rx_badEopSop++;
                                cmn_err(CE_WARN, "bad Sop %d or Eop %d: %d",
                                    e->Sop, e->Eop, e->BufferLength);
                        }
                }
                flagt = e->Qsleeping;
                flags |= flagt;
                if (flagt & F_CMDQ0_ENABLE)
                        sge->intr_cnt.rx_cmdq0++;
                if (flagt & F_CMDQ1_ENABLE)
                        sge->intr_cnt.rx_cmdq1++;
                if (flagt & F_FL0_ENABLE)
                        sge->intr_cnt.rx_flq0++;
                if (flagt & F_FL1_ENABLE)
                        sge->intr_cnt.rx_flq1++;
#ifdef TX_THREAD_RECLAIM
                spin_lock(&sge->cmdQ[0].cq_qlock);
                sge->cmdQ[0].cq_complete += e->Cmdq0CreditReturn;
                spin_unlock(&sge->cmdQ[0].cq_qlock);
                spin_lock(&sge->cmdQ[1].cq_qlock);
                sge->cmdQ[1].cq_complete += e->Cmdq1CreditReturn;
                if ((adapter->ch_blked) &&
                    (sge->cmdQ[0].cq_complete +
                    sge->cmdQ[1].cq_complete) > 16) {
                        adapter->ch_blked = 0;
                        ch_gld_ok(adapter);
                }
                spin_unlock(&sge->cmdQ[1].cq_qlock);
#else
                credits_pend[0] += e->Cmdq0CreditReturn;
                credits_pend[1] += e->Cmdq1CreditReturn;
#ifdef CONFIG_SMP
                if (unlikely(credits_pend[0] > SGE_BATCH_THRESH)) {
                        free_cmdQ_buffers(sge, &sge->cmdQ[0], credits_pend[0]);
                        credits_pend[0] = 0;
                }
                if (unlikely(credits_pend[1] > SGE_BATCH_THRESH)) {
                        free_cmdQ_buffers(sge, &sge->cmdQ[1], credits_pend[1]);
                        credits_pend[1] = 0;
                }
#endif
#endif
#ifdef HOST_PAUSE
                t1_sge_check_pause(sge, &sge->freelQ[e->FreelistQid]);
#endif
                e++;
                if (unlikely(++cidx == entries_n)) {
                        cidx = 0;
                        genbit ^= 1;
                        e = q;
                }

                /* pull physical memory of response queue entry into cache */
                (void) ddi_dma_sync(dh, (off_t)((caddr_t)e - (caddr_t)q),
                    sizeof (*e), DDI_DMA_SYNC_FORKERNEL);

                ret = 1;
        }

#ifndef TX_THREAD_RECLAIM
        if (credits_pend[0])
                free_cmdQ_buffers(sge, &sge->cmdQ[0], credits_pend[0]);
        if (credits_pend[1])
                free_cmdQ_buffers(sge, &sge->cmdQ[1], credits_pend[1]);
#endif
        if (flags & F_CMDQ0_ENABLE) {
                struct cmdQ *cmdQ = &sge->cmdQ[0];
                atomic_set(&cmdQ->cq_asleep, 1);
                if (atomic_read(cmdQ->cq_pio_pidx) != cmdQ->cq_pidx) {
                        doorbell_pio(sge, F_CMDQ0_ENABLE);
                        atomic_set(&cmdQ->cq_pio_pidx, cmdQ->cq_pidx);
                }
        }

        /* the SGE told us one of the free lists is empty */
        if (unlikely(flags & (F_FL0_ENABLE | F_FL1_ENABLE)))
                freelQs_empty(sge);

#ifdef CONFIG_CHELSIO_T1_OFFLOAD
        if (adapter->ch_tx_overflow_mutex)
                mutex_enter(adapter->ch_tx_overflow_mutex);
        if (adapter->ch_blked &&
            (sge->cmdQ[0].cq_credits > (sge->cmdQ[0].cq_entries_n>>2)) &&
            (sge->cmdQ[1].cq_credits > (sge->cmdQ[1].cq_entries_n>>2))) {
                adapter->ch_blked = 0;
                if (adapter->ch_tx_overflow_cv)
                        cv_broadcast(adapter->ch_tx_overflow_cv);
                ch_gld_ok(adapter);
        }
        if (adapter->ch_tx_overflow_mutex)
                mutex_exit(adapter->ch_tx_overflow_mutex);
#else
#ifndef TX_THREAD_RECLAIM
        if (adapter->ch_blked &&
            (sge->cmdQ[0].cq_credits > (sge->cmdQ[0].cq_entries_n>>1)) &&
            (sge->cmdQ[1].cq_credits > (sge->cmdQ[1].cq_entries_n>>1))) {
                adapter->ch_blked = 0;
                ch_gld_ok(adapter);
        }
#endif
#endif  /* CONFIG_CHELSIO_T1_OFFLOAD */

        Q->rq_genbit = genbit;
        Q->rq_cidx = cidx;
        Q->rq_credits = credits;

        t1_write_reg_4(adapter, A_SG_SLEEPING, cidx);

check_slow_ints:
        /* handle non-data interrupts */
        if (unlikely(!ret))
                ret = t1_slow_intr_handler(adapter);

        return (ret);
}

/*
 * allocate a mblk with DMA mapped mblk.
 * When checksum offload is enabled, we start the DMA at a 2 byte offset so
 * the IP header will be aligned. We do this for sparc only.
 */
static uint64_t
os_freelist_buffer_alloc(ch_t *sa, int sz, mblk_t **mb, ulong_t *dh)
{
        ch_esb_t *ch_get_small_rbuf(ch_t *sa);
        ch_esb_t *ch_get_big_rbuf(ch_t *sa);
        ch_esb_t *rbp;
        uint32_t rxoff = sa->sge->rx_offset;

        if (sz == SGE_SM_BUF_SZ(sa)) {
                /* get pre-mapped buffer */
                if ((rbp = ch_get_small_rbuf(sa)) == NULL) {
                        sa->norcvbuf++;
                        return ((uint64_t)0);
                }

                *mb = desballoc((unsigned char *)rbp->cs_buf + rxoff,
                    SGE_SM_BUF_SZ(sa)-rxoff, BPRI_MED, &rbp->cs_frtn);
                if (*mb == NULL) {
                        mutex_enter(&sa->ch_small_esbl);
                        rbp->cs_next = sa->ch_small_esb_free;
                        sa->ch_small_esb_free = rbp;
                        mutex_exit(&sa->ch_small_esbl);
                        return ((uint64_t)0);
                }
                *dh = rbp->cs_dh;

                return (rbp->cs_pa + rxoff);
        } else {
                /* get pre-mapped buffer */
                if ((rbp = ch_get_big_rbuf(sa)) == NULL) {
                        sa->norcvbuf++;
                        return ((uint64_t)0);
                }

                *mb = desballoc((unsigned char *)rbp->cs_buf + rxoff,
                    SGE_BG_BUF_SZ(sa)-rxoff, BPRI_MED, &rbp->cs_frtn);
                if (*mb == NULL) {
                        mutex_enter(&sa->ch_big_esbl);
                        rbp->cs_next = sa->ch_big_esb_free;
                        sa->ch_big_esb_free = rbp;
                        mutex_exit(&sa->ch_big_esbl);
                        return ((uint64_t)0);
                }
                *dh = rbp->cs_dh;

                return (rbp->cs_pa + rxoff);
        }
}

static inline unsigned int
t1_sge_rx(pesge *sge, struct freelQ *Q, unsigned int len, unsigned int offload)
{
        mblk_t *skb;
        peobj *adapter = sge->obj;
        struct freelQ_ce *cq = Q->fq_centries;
        struct freelQ_ce *ce = &cq[Q->fq_cidx];
        ddi_dma_handle_t dh = (ddi_dma_handle_t)ce->fe_dh;
        uint32_t cidx = Q->fq_cidx;
        uint32_t entries_n = Q->fq_entries_n;
        uint32_t sz = Q->fq_rx_buffer_size;
        uint32_t useit = 1;
        uint32_t rxoff = sge->rx_offset;
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
        uint32_t rv;
#endif

        if (Q->fq_id)
                sge->intr_cnt.rx_flq1_cnt++;
        else
                sge->intr_cnt.rx_flq0_cnt++;
        /*
         * If pkt size falls below threshold, then we'll copy data to
         * an blk and reuse mblk.
         *
         * NOTE that rxoff is 2 for T1 adapters. We align the the start
         * of the DMA buffer begin at rxoff offset for T1 cards instead of
         * at the beginning of the buffer, thus the length of the received
         * data does not include this offset. We therefore always add
         * SGE_RX_OFFSET to the allocb size so we have space to provide the
         * offset for the copied data.
         */
#ifdef HOST_PAUSE
        /*
         * If we have Host pause compiled in, then we look at the
         * free list, if the pause is on and we're not in offload
         * mode then we drop packets, this is designed to avoid
         * overwhelming the machine. If the machine is powerfull enough
         * this will not happen. The 'rx_pkt_drops' will show when
         * packets are being dropped and how much.
         */
        if ((offload == 0) && adapter->pause_on) {
                freelQ_e *e;
                /* Ditch the packet and reuse original buffer */
                e = &Q->fq_entries[cidx];
                e->GenerationBit  ^= 1;
                e->GenerationBit2 ^= 1;
                sge->intr_cnt.rx_pkt_drops++;
                goto rx_entry_consumed;
        } else if (((adapter->pause_on ||
            (len <= SGE_RX_COPY_THRESHOLD)) &&
            (skb = allocb(len + SGE_RX_OFFSET, BPRI_HI))))
#else
        if ((len <= SGE_RX_COPY_THRESHOLD) &&
            (skb = allocb(len + SGE_RX_OFFSET, BPRI_HI)))
#endif
        {
                freelQ_e *e;
                char *src = (char *)((mblk_t *)ce->fe_mp)->b_rptr;

                /*
                 * pull physical memory of pkt data into cache
                 * Note that len does not include offset for T1.
                 */
                (void) ddi_dma_sync(dh, (off_t)(rxoff), len,
                    DDI_DMA_SYNC_FORKERNEL);

                if (offload == 0) {
                        /*
                         * create 2 byte offset so IP header aligned on
                         * 4 byte boundry
                         */
                        skb_reserve(skb, SGE_RX_OFFSET);
                        /*
                         * if hardware inserted 2 byte offset then need to
                         * start copying with extra offset
                         */
                        src += sge->rx_pkt_pad;
                }
                memcpy(skb->b_rptr, src, len);
                useit = 0;      /* mblk copy, don't inc esballoc in use cnt */

                /* so we can reuse original buffer */
                e = &Q->fq_entries[cidx];
                e->GenerationBit  ^= 1;
                e->GenerationBit2 ^= 1;
                sge->intr_cnt.rx_pkt_copied++;
        } else {
                /* consume buffer off the ring */
                skb = ce->fe_mp;
                ce->fe_mp = NULL;

                /*
                 * if not offload (tunneled pkt), & hardward padded, then
                 * adjust start of pkt to point to start of data i.e.
                 * skip pad (2 bytes).
                 */
                if (!offload && sge->rx_pkt_pad)
                        __skb_pull(skb, SGE_RX_OFFSET);

                /*
                 * pull physical memory of pkt data into cache
                 * Note that len does not include offset for T1.
                 */
                (void) ddi_dma_sync(dh, (off_t)(rxoff), len,
                    DDI_DMA_SYNC_FORKERNEL);
        }

        /* set length of data in skb */
        skb_put(skb, len);

#ifdef CONFIG_CHELSIO_T1_OFFLOAD
        if (likely(offload)) {
                if (likely(toe_running(adapter))) {
                        /* sends pkt upstream to toe layer */
                        if (useit) {
                                uint_t index;
                                if (sz == SGE_SM_BUF_SZ(adapter))
                                        index = adapter->ch_sm_index;
                                else
                                        index = adapter->ch_big_index;
                                atomic_add(1, &buffers_in_use[index]);
                        }
                        if (adapter->toe_rcv)
                                adapter->toe_rcv(adapter->ch_toeinst, skb);
                        else
                                freemsg(skb);
                } else {
                        cmn_err(CE_WARN,
                            "%s: unexpected offloaded packet, cmd %u\n",
                            adapter->ch_name, *skb->b_rptr);

                        /* discard packet */
                        freemsg(skb);
                }
        }
#else
        if (unlikely(offload)) {
                cmn_err(CE_WARN,
                    "%s: unexpected offloaded packet, cmd %u\n",
                    adapter->ch_name, *skb->b_rptr);

                /* discard paket */
                freemsg(skb);
        }
#endif
        else {
                struct cpl_rx_pkt *p = (struct cpl_rx_pkt *)skb->b_rptr;
                int flg = 0;
                uint32_t cksum;

                /* adjust beginning of data to skip CPL header */
                skb_pull(skb, SZ_CPL_RX_PKT);

                /* extract checksum from CPL header here */

                /*
                 * bump count of mlbks in used by protocol stack(s)
                 */
                if (useit) {
                        if (sz == SGE_SM_BUF_SZ(adapter)) {
                                atomic_add(1,
                                    &buffers_in_use[adapter->ch_sm_index]);
                        } else {
                                atomic_add(1,
                                    &buffers_in_use[adapter->ch_big_index]);
                        }
                }

#ifdef CONFIG_CHELSIO_T1_OFFLOAD
                /*
                 * let the TOE layer have a crack at the packet first.
                 */
                if (adapter->toe_tunnel) {
                        rv = adapter->toe_tunnel(adapter->ch_toeinst, skb);
                        /*
                         * The TOE may have consumed the packet.
                         */
                        if (rv)
                                goto rx_entry_consumed;
                }
#endif  /* CONFIG_CHELSIO_T1_OFFLOAD */

                cksum = p->csum;

                /*
                 * NOTE: 14+9 = size of MAC + offset to IP protocol field
                 */
                if (adapter->ch_config.cksum_enabled &&
                    (ntohs(((struct ether_header *)skb->b_rptr)->ether_type) ==
                    ETHERTYPE_IP) &&
                    ((skb->b_rptr[14+9] == IPPROTO_TCP) ||
                    (skb->b_rptr[14+9] == IPPROTO_UDP))) {
                        flg = 1;
                }

                ch_send_up(adapter, skb, cksum, flg);
        }

rx_entry_consumed:

        if (++cidx == entries_n)
                cidx = 0;

        Q->fq_cidx = cidx;

        if (unlikely(--Q->fq_credits < (entries_n>>2)))
                /* allocate new buffers on the free list */
                alloc_freelQ_buffers(sge, Q);
        return (1);
}

#ifdef HOST_PAUSE
static void
t1_sge_check_pause(pesge *sge, struct freelQ *Q)
{
        peobj *adapter = sge->obj;

        /*
         * If the number of available credits shrinks below
         * the Pause on threshold then enable the pause and
         * try and allocate more buffers.
         * On the next pass, if there's more credits returned
         * then check that you've went above the pause
         * threshold and then disable the pause.
         */
        if (Q->fq_credits < Q->fq_pause_on_thresh) {
                if (do_host_pause) {
                        sge->intr_cnt.rx_pause_on++;
                        adapter->txxg_cfg1 |=
                            SUNI1x10GEXP_BITMSK_TXXG_HOSTPAUSE;
                        (void) t1_tpi_write(adapter,
                            SUNI1x10GEXP_REG_TXXG_CONFIG_1 << 2,
                            adapter->txxg_cfg1);
                        adapter->pause_on = 1;
                        adapter->pause_time = gethrtime();
                }
                alloc_freelQ_buffers(sge, Q);
        } else if ((adapter->pause_on) &&
            (Q->fq_credits > Q->fq_pause_off_thresh)) {
                hrtime_t time;
                sge->intr_cnt.rx_pause_off++;
                adapter->txxg_cfg1 &= ~SUNI1x10GEXP_BITMSK_TXXG_HOSTPAUSE;
                (void) t1_tpi_write(adapter,
                    SUNI1x10GEXP_REG_TXXG_CONFIG_1 << 2,
                    adapter->txxg_cfg1);
                adapter->pause_on = 0;
                time = (gethrtime() - adapter->pause_time)/1000;
                sge->intr_cnt.rx_pause_ms += time;
                if (time > sge->intr_cnt.rx_pause_spike)
                        sge->intr_cnt.rx_pause_spike = (uint32_t)time;
        }
        sge->intr_cnt.rx_fl_credits = Q->fq_credits;
}
#endif  /* HOST_PAUSE */

static void
alloc_freelQ_buffers(pesge *sge, struct freelQ *Q)
{
        uint32_t pidx = Q->fq_pidx;
        struct freelQ_ce *ce = &Q->fq_centries[pidx];
        freelQ_e *fq = Q->fq_entries;           /* base of freelist Q */
        freelQ_e *e = &Q->fq_entries[pidx];
        uint32_t sz = Q->fq_rx_buffer_size;
        uint32_t rxoff = sge->rx_offset;
        uint32_t credits = Q->fq_credits;
        uint32_t entries_n = Q->fq_entries_n;
        uint32_t genbit = Q->fq_genbit;
        ddi_dma_handle_t th = (ddi_dma_handle_t)Q->fq_dh;
        ulong_t dh;
        uint64_t mapping;
        off_t offset = (off_t)((caddr_t)e - (caddr_t)fq);
        size_t len = 0;

        while (credits < entries_n) {
                if (e->GenerationBit != genbit) {
                        mblk_t *skb;

                        mapping = os_freelist_buffer_alloc(sge->obj, sz,
                            &skb, &dh);
                        if (mapping == 0) {
                                sge->intr_cnt.rx_flbuf_fails++;
                                break;
                        }
                        sge->intr_cnt.rx_flbuf_allocs++;

                        ce->fe_mp = skb;
                        ce->fe_dh = dh;

                        /*
                         * Note that for T1, we've started the beginning of
                         * of the buffer by an offset of 2 bytes. We thus
                         * decrement the length to account for this.
                         */
                        e->AddrLow = (u32)mapping;
                        e->AddrHigh = (u64)mapping >> 32;
                        e->BufferLength = sz - rxoff;
                        wmb();
                        e->GenerationBit = e->GenerationBit2 = genbit;
                }

                len += sizeof (*e);

                ce++;
                e++;
                credits++;
                if (++pidx == entries_n) {
                        /*
                         * sync freelist entries to physical memory up to
                         * end of the table.
                         */
                        (void) ddi_dma_sync(th, offset, len,
                            DDI_DMA_SYNC_FORDEV);
                        offset = 0;
                        len = 0;

                        pidx = 0;
                        genbit ^= 1;
                        ce = Q->fq_centries;
                        e = Q->fq_entries;
                }
        }

        /* sync freelist entries that have been modified. */
        if (len)
                (void) ddi_dma_sync(th, offset, len, DDI_DMA_SYNC_FORDEV);

        Q->fq_genbit = genbit;
        Q->fq_pidx = pidx;
        Q->fq_credits = credits;
}

static void
freelQs_empty(pesge *sge)
{
        u32 irq_reg = t1_read_reg_4(sge->obj, A_SG_INT_ENABLE);
        u32 irqholdoff_reg;

        alloc_freelQ_buffers(sge, &sge->freelQ[0]);
        alloc_freelQ_buffers(sge, &sge->freelQ[1]);

        if ((sge->freelQ[0].fq_credits > sge->freelQ[0].fq_entries_n >> 2) &&
            (sge->freelQ[1].fq_credits > sge->freelQ[1].fq_entries_n >> 2)) {
                irq_reg |= F_FL_EXHAUSTED;
                irqholdoff_reg = sge->intrtimer[sge->currIndex];
        } else {
                /* Clear the F_FL_EXHAUSTED interrupts for now */
                irq_reg &= ~F_FL_EXHAUSTED;
                irqholdoff_reg = sge->intrtimer_nres;
        }
        t1_write_reg_4(sge->obj, A_SG_INTRTIMER, irqholdoff_reg);
        t1_write_reg_4(sge->obj, A_SG_INT_ENABLE, irq_reg);

        /* We reenable the Qs to force an Freelist GTS interrupt later */
        doorbell_pio(sge, F_FL0_ENABLE | F_FL1_ENABLE);
}

/*
 * Frees 'credits_pend' TX buffers and returns the credits to Q->credits.
 * Free xmit buffers
 */
static void
free_cmdQ_buffers(pesge *sge, struct cmdQ *Q, unsigned int credits_pend)
{
        mblk_t *skb;
        struct cmdQ_ce *ce;
        struct cmdQ_ce *cq = Q->cq_centries;
        uint32_t entries_n = Q->cq_entries_n;
        uint32_t cidx = Q->cq_cidx;
        uint32_t i = credits_pend;
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
        ch_t *chp = sge->obj;
#endif
        ce = &cq[cidx];

        while (i--) {
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
                /* if flag set, then toe buffer */
                switch (ce->ce_flg & 0x7) {
                case DH_DMA:
                        if (ce->ce_dh) {
                                ch_unbind_dma_handle(sge->obj, ce->ce_dh);
                                ce->ce_dh = NULL;       /* may not be needed */
                        }
                        skb = ce->ce_mp;
                        if (skb && ((ce->ce_flg & CH_ARP) == 0)) {
                                freemsg(skb);
                        }
                        ce->ce_mp = NULL;
                        break;

#if defined(__sparc)
                case DH_DVMA:
                        if (ce->ce_dh) {
                                ch_unbind_dvma_handle(sge->obj, ce->ce_dh);
                                ce->ce_dh = NULL;       /* may not be needed */
                        }
                        skb = ce->ce_mp;
                        if (skb && ((ce->ce_flg & CH_ARP) == 0)) {
                                freemsg(skb);
                        }
                        ce->ce_mp = NULL;
                        break;
#endif  /* __sparc */

                case DH_TOE:
                        chp->toe_free(chp->ch_toeinst, (tbuf_t *)(ce->ce_mp));
                        ce->ce_mp = NULL;
                        break;
                }
#else   /* CONFIG_CHELSIO_T1_OFFLOAD */
                if (ce->ce_dh) {
                        if ((ce->ce_flg & 7) == DH_DMA) {
                                ch_unbind_dma_handle(sge->obj, ce->ce_dh);
                        }
#if defined(__sparc)
                        else {
                                ch_unbind_dvma_handle(sge->obj, ce->ce_dh);
                        }
#endif  /* __sparc */
                        ce->ce_dh = NULL; /* may not be needed */
                }

                skb = ce->ce_mp;
                if (skb && ((ce->ce_flg & CH_ARP) == 0)) {
                        freemsg(skb);
                }
                ce->ce_mp = NULL;
#endif  /* !CONFIG_CHELSIO_T1_OFFLOAD */

                ce++;
                if (++cidx == entries_n) {
                        cidx = 0;
                        ce = cq;
                }
        }

        Q->cq_cidx = cidx;
        atomic_add(credits_pend, &Q->cq_credits);
}

struct sge_intr_counts *
sge_get_stat(pesge *sge)
{
        return (&sge->intr_cnt);
}

/*
 * Allocates both RX and TX resources and configures the SGE. However,
 * the hardware is not enabled yet.
 *
 * rx_pkt_pad is set, if the hardware supports aligning non-offload traffic.
 * jumbo_fl is set to the index of the freelist containing the jumbo buffers.
 */
int
t1_sge_configure(pesge *sge, struct sge_params *p)
{
        sge->rx_pkt_pad = t1_is_T1B(sge->obj) ? 0 : SGE_RX_OFFSET;
        sge->jumbo_fl = t1_is_T1B(sge->obj) ? 1 : 0;
        /* if we're a T2 card, then we have hardware offset support */
        sge->rx_offset = t1_is_T1B(sge->obj) ? SGE_RX_OFFSET: 0;

        if (alloc_rx_resources(sge, p))
                return (-ENOMEM);
        if (alloc_tx_resources(sge, p)) {
                free_rx_resources(sge);
                return (-ENOMEM);
        }
        configure_sge(sge, p);

        /*
         * Now that we have sized the free lists calculate the payload
         * capacity of the large buffers.  Other parts of the driver use
         * this to set the max offload coalescing size so that RX packets
         * do not overflow our large buffers.
         */
        p->large_buf_capacity = jumbo_payload_capacity(sge);
        return (0);
}

/*
 * Allocates basic RX resources, consisting of memory mapped freelist Qs and a
 * response Q.
 */
static int
alloc_rx_resources(pesge *sge, struct sge_params *p)
{
        unsigned int size, i;

        for (i = 0; i < SGE_FREELQ_N; i++) {
                struct freelQ *Q = &sge->freelQ[i];

                Q->fq_id = i;
                Q->fq_genbit = 1;
                Q->fq_entries_n = p->freelQ_size[i];
#ifdef HOST_PAUSE
                Q->fq_pause_on_thresh = flq_pause_window;
                Q->fq_pause_off_thresh = Q->fq_entries_n >> 1;
#endif
                size = sizeof (freelQ_e) * Q->fq_entries_n;

                Q->fq_entries = pe_os_malloc_contig_wait_zero(sge->obj,
                    size, &Q->fq_pa, &Q->fq_dh, &Q->fq_ah, DMA_OUT);


                if (!Q->fq_entries)
                        goto err_no_mem;
                memset(Q->fq_entries, 0, size);
                size = sizeof (struct freelQ_ce) * Q->fq_entries_n;
                Q->fq_centries = t1_os_malloc_wait_zero(size);
                if (!Q->fq_centries)
                        goto err_no_mem;
                memset(Q->fq_centries, 0, size);
        }

        /*
         * Calculate the buffer sizes for the two free lists.  FL0 accommodates
         * regular sized Ethernet frames, FL1 is sized not to exceed 16K,
         * including all the sk_buff overhead.
         * For T1C FL0 and FL1 are reversed.
         */
#ifdef NOTYET
        sge->freelQ[1 ^ sge->jumbo_fl].fq_rx_buffer_size = SGE_RX_SM_BUF_SIZE +
            sizeof (struct cpl_rx_data) +
            SGE_RX_OFFSET - sge->rx_pkt_pad;
#else
        sge->freelQ[1 ^ sge->jumbo_fl].fq_rx_buffer_size =
            sge->obj->ch_sm_buf_sz;
        if (is_T2(sge->obj))
                sge->intr_cnt.rx_flq1_sz = sge->obj->ch_sm_buf_sz;
        else
                sge->intr_cnt.rx_flq0_sz = sge->obj->ch_sm_buf_sz;
#endif
#ifdef NOTYET
        sge->freelQ[sge->jumbo_fl].fq_rx_buffer_size = (16 * 1024) -
            SKB_DATA_ALIGN(sizeof (struct skb_shared_info));
#else
        sge->freelQ[sge->jumbo_fl].fq_rx_buffer_size = sge->obj->ch_bg_buf_sz;
        if (is_T2(sge->obj))
                sge->intr_cnt.rx_flq0_sz = sge->obj->ch_bg_buf_sz;
        else
                sge->intr_cnt.rx_flq1_sz = sge->obj->ch_bg_buf_sz;
#endif

        sge->respQ.rq_genbit = 1;
        sge->respQ.rq_entries_n = sge_respq_cnt;
        sge->respQ.rq_credits = sge_respq_cnt;
        sge->respQ.rq_credits_thresh = sge_respq_cnt - (sge_respq_cnt >> 2);
        size = sizeof (respQ_e) * sge->respQ.rq_entries_n;

        sge->respQ.rq_entries = pe_os_malloc_contig_wait_zero(sge->obj,
            size, &(sge->respQ.rq_pa), &(sge->respQ.rq_dh),
            &(sge->respQ.rq_ah), 0);

        if (!sge->respQ.rq_entries)
                goto err_no_mem;
        memset(sge->respQ.rq_entries, 0, size);
        return (0);

err_no_mem:
        free_rx_resources(sge);
        return (1);
}

/*
 * Allocates basic TX resources, consisting of memory mapped command Qs.
 */
static int
alloc_tx_resources(pesge *sge, struct sge_params *p)
{
        unsigned int size, i;

        for (i = 0; i < SGE_CMDQ_N; i++) {
                struct cmdQ *Q = &sge->cmdQ[i];

                Q->cq_genbit = 1;
                Q->cq_entries_n = p->cmdQ_size[i];
                atomic_set(&Q->cq_credits, Q->cq_entries_n);
                atomic_set(&Q->cq_asleep, 1);

                mutex_init(&Q->cq_qlock, NULL, MUTEX_DRIVER,
                    sge->obj->ch_icookp);

                size = sizeof (cmdQ_e) * Q->cq_entries_n;
                Q->cq_entries = pe_os_malloc_contig_wait_zero(sge->obj,
                    size, &Q->cq_pa, &Q->cq_dh, &Q->cq_ah, DMA_OUT);

                if (!Q->cq_entries)
                        goto err_no_mem;
                memset(Q->cq_entries, 0, size);
                size = sizeof (struct cmdQ_ce) * Q->cq_entries_n;
                Q->cq_centries = t1_os_malloc_wait_zero(size);
                if (!Q->cq_centries)
                        goto err_no_mem;
                memset(Q->cq_centries, 0, size);

                /* allocate pre-mapped dma headers */
                pe_dma_handle_init(sge->obj, Q->cq_entries_n);
        }

        return (0);

err_no_mem:
        free_tx_resources(sge);
        return (1);
}

/*
 * Sets the interrupt latency timer when the adaptive Rx coalescing
 * is turned off. Do nothing when it is turned on again.
 *
 * This routine relies on the fact that the caller has already set
 * the adaptive policy in adapter->sge_params before calling it.
 */
int
t1_sge_set_coalesce_params(pesge *sge, struct sge_params *p)
{
        if (!p->coalesce_enable) {
                u32 newTimer = p->rx_coalesce_usecs *
                    (board_info(sge->obj)->clock_core / 1000000);

                t1_write_reg_4(sge->obj, A_SG_INTRTIMER, newTimer);
        }
        return (0);
}

/*
 * Programs the various SGE registers. However, the engine is not yet enabled,
 * but sge->sge_control is setup and ready to go.
 */
static void
configure_sge(pesge *sge, struct sge_params *p)
{
        ch_t *ap = sge->obj;
        int i;

        t1_write_reg_4(ap, A_SG_CONTROL, 0);

        setup_ring_params(ap, sge->cmdQ[0].cq_pa, sge->cmdQ[0].cq_entries_n,
            A_SG_CMD0BASELWR, A_SG_CMD0BASEUPR, A_SG_CMD0SIZE);
        setup_ring_params(ap, sge->cmdQ[1].cq_pa, sge->cmdQ[1].cq_entries_n,
            A_SG_CMD1BASELWR, A_SG_CMD1BASEUPR, A_SG_CMD1SIZE);
        setup_ring_params(ap, sge->freelQ[0].fq_pa,
            sge->freelQ[0].fq_entries_n, A_SG_FL0BASELWR,
            A_SG_FL0BASEUPR, A_SG_FL0SIZE);
        setup_ring_params(ap, sge->freelQ[1].fq_pa,
            sge->freelQ[1].fq_entries_n, A_SG_FL1BASELWR,
            A_SG_FL1BASEUPR, A_SG_FL1SIZE);

        /* The threshold comparison uses <. */
        t1_write_reg_4(ap, A_SG_FLTHRESHOLD, SGE_RX_SM_BUF_SIZE(ap) -
            SZ_CPL_RX_PKT - sge->rx_pkt_pad - sge->rx_offset + 1);
        setup_ring_params(ap, sge->respQ.rq_pa, sge->respQ.rq_entries_n,
            A_SG_RSPBASELWR, A_SG_RSPBASEUPR, A_SG_RSPSIZE);
        t1_write_reg_4(ap, A_SG_RSPQUEUECREDIT, (u32)sge->respQ.rq_entries_n);
        sge->sge_control = F_CMDQ0_ENABLE | F_CMDQ1_ENABLE | F_FL0_ENABLE |
            F_FL1_ENABLE | F_CPL_ENABLE | F_RESPONSE_QUEUE_ENABLE |
            V_CMDQ_PRIORITY(2) | F_DISABLE_CMDQ1_GTS | F_ISCSI_COALESCE |
#if 1
                /*
                 * if the the following bit is not set, then we'll get an
                 * interrupt everytime command Q 0 goes empty. Since we're
                 * always ringing the doorbell, we can turn it on.
                 */
            F_DISABLE_CMDQ0_GTS |
#endif
            V_RX_PKT_OFFSET(sge->rx_pkt_pad);

#if BYTE_ORDER == BIG_ENDIAN
        sge->sge_control |= F_ENABLE_BIG_ENDIAN;
#endif

        /*
         * Initialize the SGE Interrupt Timer arrray:
         * intrtimer[0] = (SGE_INTRTIMER0) usec
         * intrtimer[0<i<10] = (SGE_INTRTIMER0 + 2*i) usec
         * intrtimer[10] = (SGE_INTRTIMER1) usec
         *
         */
        sge->intrtimer[0] = board_info(sge->obj)->clock_core / 1000000;
        for (i = 1; i < SGE_INTR_MAXBUCKETS - 1; ++i) {
                sge->intrtimer[i] = SGE_INTRTIMER0 + (2 * i);
                sge->intrtimer[i] *= sge->intrtimer[0];
        }
        sge->intrtimer[SGE_INTR_MAXBUCKETS - 1] =
            sge->intrtimer[0] * SGE_INTRTIMER1;
        /* Initialize resource timer */
        sge->intrtimer_nres = (uint32_t)(sge->intrtimer[0] *
            SGE_INTRTIMER_NRES);
        /* Finally finish initialization of intrtimer[0] */
        sge->intrtimer[0] = (uint32_t)(sge->intrtimer[0] * SGE_INTRTIMER0);
        /* Initialize for a throughput oriented workload */
        sge->currIndex = SGE_INTR_MAXBUCKETS - 1;

        if (p->coalesce_enable)
                t1_write_reg_4(ap, A_SG_INTRTIMER,
                    sge->intrtimer[sge->currIndex]);
        else
                (void) t1_sge_set_coalesce_params(sge, p);
}

static inline void
setup_ring_params(ch_t *adapter, u64 addr, u32 size, int base_reg_lo,
    int base_reg_hi, int size_reg)
{
        t1_write_reg_4(adapter, base_reg_lo, (u32)addr);
        t1_write_reg_4(adapter, base_reg_hi, addr >> 32);
        t1_write_reg_4(adapter, size_reg, size);
}

/*
 * Frees RX resources.
 */
static void
free_rx_resources(pesge *sge)
{
        unsigned int size, i;

        if (sge->respQ.rq_entries) {
                size = sizeof (respQ_e) * sge->respQ.rq_entries_n;

                pe_os_free_contig(sge->obj, size, sge->respQ.rq_entries,
                    sge->respQ.rq_pa, sge->respQ.rq_dh, sge->respQ.rq_ah);
        }

        for (i = 0; i < SGE_FREELQ_N; i++) {
                struct freelQ *Q = &sge->freelQ[i];

                if (Q->fq_centries) {
                        free_freelQ_buffers(sge, Q);

                        t1_os_free(Q->fq_centries,
                            Q->fq_entries_n * sizeof (freelQ_ce_t));
                }
                if (Q->fq_entries) {
                        size = sizeof (freelQ_e) * Q->fq_entries_n;

                        /* free the freelist queue */
                        pe_os_free_contig(sge->obj, size, Q->fq_entries,
                            Q->fq_pa, Q->fq_dh, Q->fq_ah);

                }
        }
}

/*
 * Frees all RX buffers on the freelist Q. The caller must make sure that
 * the SGE is turned off before calling this function.
 */
static void
free_freelQ_buffers(pesge *sge, struct freelQ *Q)
{
        struct freelQ_ce *ce;
        struct freelQ_ce *cq = Q->fq_centries;
        uint32_t credits = Q->fq_credits;
        uint32_t entries_n = Q->fq_entries_n;
        uint32_t cidx = Q->fq_cidx;
        uint32_t i = Q->fq_id;

        ce = &cq[cidx];

        credits = entries_n;
        while (credits--) {
                mblk_t *mp;
                if ((mp = ce->fe_mp) != NULL) {
                        /* bump in-use count of receive buffers */
                        if (i != sge->jumbo_fl) {
                                atomic_add(1,
                                    &buffers_in_use[sge->obj->ch_sm_index]);
                        } else {
                                atomic_add(1,
                                    &buffers_in_use[sge->obj->ch_big_index]);
                        }

                        /*
                         * note. freeb() callback of esb-alloced mblk will
                         * cause receive buffer to be put back on sa free list.
                         */
                        freeb(mp);
                        ce->fe_mp = NULL;
                }

                ce++;
                if (++cidx == entries_n) {
                        cidx = 0;
                        ce = cq;
                }
        }

        Q->fq_cidx = cidx;
        Q->fq_credits = credits;
}

/*
 * Free TX resources.
 *
 * Assumes that SGE is stopped and all interrupts are disabled.
 */
static void
free_tx_resources(pesge *sge)
{
        unsigned int size;
        uint32_t i;

        for (i = 0; i < SGE_CMDQ_N; i++) {
                struct cmdQ *Q = &sge->cmdQ[i];

                if (Q->cq_centries) {
                        unsigned int pending = Q->cq_entries_n -
                            atomic_read(Q->cq_credits);

                        mutex_destroy(&Q->cq_qlock);

                        if (pending)
                                free_cmdQ_buffers(sge, Q, pending);

                        size = sizeof (struct cmdQ_ce) * Q->cq_entries_n;
                        t1_os_free(Q->cq_centries, size);
                }

                if (Q->cq_entries) {
                        size = sizeof (cmdQ_e) * Q->cq_entries_n;
                        pe_os_free_contig(sge->obj, size, Q->cq_entries,
                            Q->cq_pa, Q->cq_dh, Q->cq_ah);
                }
        }
}

/*
 * Return the payload capacity of the jumbo free-list buffers.
 */
static inline unsigned int jumbo_payload_capacity(pesge *sge)
{
        return (sge->freelQ[sge->jumbo_fl].fq_rx_buffer_size -
            sizeof (struct cpl_rx_data) - sge->rx_pkt_pad - sge->rx_offset);
}

/* PR2928 & PR3309 */
void
t1_sge_set_ptimeout(adapter_t *adapter, u32 val)
{
        pesge *sge = adapter->sge;

        if (is_T2(adapter))
                sge->ptimeout = max(val, 1);
}

/* PR2928 & PR3309 */
u32
t1_sge_get_ptimeout(adapter_t *adapter)
{
        pesge *sge = adapter->sge;

        return (is_T2(adapter) ? sge->ptimeout : 0);
}

void
sge_add_fake_arp(pesge *sge, void *bp)
{
        sge->pskb = bp;
}

#ifdef SUN_KSTATS
static int
sge_kstat_setup(pesge *sge)
{
        int status;
        p_kstat_t ksp;
        size_t ch_kstat_sz;
        p_ch_kstat_t chkp;
        char kstat_name[32];
        int instance;
        int i;

        status = -1;
        ch_kstat_sz = sizeof (ch_kstat_t);
        instance = ddi_get_instance(sge->obj->ch_dip);
        if ((ksp = kstat_create(CHNAME "_debug", instance,
            NULL, "net_debug", KSTAT_TYPE_NAMED,
            ch_kstat_sz / sizeof (kstat_named_t), 0)) == NULL)
                goto sge_kstat_setup_exit;
        chkp = (p_ch_kstat_t)ksp->ks_data;
        kstat_named_init(&chkp->respQ_empty,            "respQ_empty",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->respQ_overflow,         "respQ_overflow",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->freelistQ_empty,        "freelistQ_empty",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->pkt_too_big,            "pkt_too_big",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->pkt_mismatch,           "pkt_mismatch",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->cmdQ_full[0],           "cmdQ_full[0]",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->cmdQ_full[1],           "cmdQ_full[1]",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->tx_reclaims[0],         "tx_reclaims[0]",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->tx_reclaims[1],         "tx_reclaims[1]",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->tx_msg_pullups,         "tx_msg_pullups",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->tx_hdr_pullups,         "tx_hdr_pullups",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->tx_tcp_ip_frag,         "tx_tcp_ip_frag",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->tx_udp_ip_frag,         "tx_udp_ip_frag",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->tx_soft_cksums,         "tx_soft_cksums",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->tx_need_cpl_space,      "tx_need_cpl_space",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->tx_multi_mblks,         "tx_multi_mblks",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->tx_no_dvma1,    "tx_num_multi_dvma_fails",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->tx_no_dvma2,    "tx_num_single_dvma_fails",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->tx_no_dma1,     "tx_num_multi_dma_fails",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->tx_no_dma2,     "tx_num_single_dma_fails",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_cmdq0,               "rx_cmdq0",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_cmdq1,               "rx_cmdq1",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_flq0,                "rx_flq0",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_flq1,                "rx_flq1",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_flq0_sz,             "rx_flq0_buffer_sz",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_flq1_sz,             "rx_flq1_buffer_sz",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_pkt_drops,           "rx_pkt_drops",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_pkt_copied,          "rx_pkt_copied",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_pause_on,            "rx_pause_on",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_pause_off,           "rx_pause_off",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_pause_ms,            "rx_pause_ms",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_pause_spike,         "rx_pause_spike",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_fl_credits,          "rx_fl_credits",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_flbuf_fails,         "rx_flbuf_fails",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_flbuf_allocs,        "rx_flbuf_allocs",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_badEopSop,           "rx_badEopSop",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_flq0_cnt,            "rx_flq0_cnt",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->rx_flq1_cnt,            "rx_flq1_cnt",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->arp_sent,               "arp_sent",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->tx_doorbells,           "tx_doorbells",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->intr_doorbells,         "intr_doorbells",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->intr1_doorbells,        "intr1_doorbells",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->sleep_cnt,              "sleep_cnt",
            KSTAT_DATA_UINT32);
        kstat_named_init(&chkp->pe_allocb_cnt,          "pe_allocb_cnt",
            KSTAT_DATA_UINT32);
        for (i = 0; i < MBLK_MAX; i++) {
                (void) sprintf(kstat_name, "tx_descs[%02d]", i);
                kstat_named_init(&chkp->tx_descs[i],
                    kstat_name, KSTAT_DATA_UINT32);
        }
        ksp->ks_update = sge_kstat_update;
        ksp->ks_private = (void *)sge;
        sge->ksp = ksp;
        kstat_install(ksp);
        status = 0;

sge_kstat_setup_exit:
        return (status);
}

static void
sge_kstat_remove(pesge *sge)
{
        if (sge->ksp)
                kstat_delete(sge->ksp);
}

static int
sge_kstat_update(p_kstat_t ksp, int rw)
{
        pesge *sge;
        p_ch_stats_t statsp;
        p_ch_kstat_t chkp;
        int i;

        sge = (pesge *)ksp->ks_private;
        statsp = (p_ch_stats_t)&sge->intr_cnt;
        chkp = (p_ch_kstat_t)ksp->ks_data;
        if (rw == KSTAT_WRITE) {
                statsp->respQ_empty     = chkp->respQ_empty.value.ui32;
                statsp->respQ_overflow  = chkp->respQ_overflow.value.ui32;
                statsp->freelistQ_empty = chkp->freelistQ_empty.value.ui32;
                statsp->pkt_too_big     = chkp->pkt_too_big.value.ui32;
                statsp->pkt_mismatch    = chkp->pkt_mismatch.value.ui32;
                statsp->cmdQ_full[0]    = chkp->cmdQ_full[0].value.ui32;
                statsp->cmdQ_full[1]    = chkp->cmdQ_full[1].value.ui32;
                statsp->tx_reclaims[0]  = chkp->tx_reclaims[0].value.ui32;
                statsp->tx_reclaims[1]  = chkp->tx_reclaims[1].value.ui32;
                statsp->tx_msg_pullups  = chkp->tx_msg_pullups.value.ui32;
                statsp->tx_hdr_pullups  = chkp->tx_hdr_pullups.value.ui32;
                statsp->tx_tcp_ip_frag  = chkp->tx_tcp_ip_frag.value.ui32;
                statsp->tx_udp_ip_frag  = chkp->tx_udp_ip_frag.value.ui32;
                statsp->tx_soft_cksums  = chkp->tx_soft_cksums.value.ui32;
                statsp->tx_need_cpl_space
                    = chkp->tx_need_cpl_space.value.ui32;
                statsp->tx_multi_mblks  = chkp->tx_multi_mblks.value.ui32;
                statsp->tx_no_dvma1     = chkp->tx_no_dvma1.value.ui32;
                statsp->tx_no_dvma2     = chkp->tx_no_dvma2.value.ui32;
                statsp->tx_no_dma1      = chkp->tx_no_dma1.value.ui32;
                statsp->tx_no_dma2      = chkp->tx_no_dma2.value.ui32;
                statsp->rx_cmdq0        = chkp->rx_cmdq0.value.ui32;
                statsp->rx_cmdq1        = chkp->rx_cmdq1.value.ui32;
                statsp->rx_flq0         = chkp->rx_flq0.value.ui32;
                statsp->rx_flq1         = chkp->rx_flq1.value.ui32;
                statsp->rx_flq0_sz      = chkp->rx_flq0_sz.value.ui32;
                statsp->rx_flq1_sz      = chkp->rx_flq1_sz.value.ui32;
                statsp->rx_pkt_drops    = chkp->rx_pkt_drops.value.ui32;
                statsp->rx_pkt_copied   = chkp->rx_pkt_copied.value.ui32;
                statsp->rx_pause_on     = chkp->rx_pause_on.value.ui32;
                statsp->rx_pause_off    = chkp->rx_pause_off.value.ui32;
                statsp->rx_pause_ms     = chkp->rx_pause_ms.value.ui32;
                statsp->rx_pause_spike  = chkp->rx_pause_spike.value.ui32;
                statsp->rx_fl_credits   = chkp->rx_fl_credits.value.ui32;
                statsp->rx_flbuf_fails  = chkp->rx_flbuf_fails.value.ui32;
                statsp->rx_flbuf_allocs = chkp->rx_flbuf_allocs.value.ui32;
                statsp->rx_badEopSop    = chkp->rx_badEopSop.value.ui32;
                statsp->rx_flq0_cnt     = chkp->rx_flq0_cnt.value.ui32;
                statsp->rx_flq1_cnt     = chkp->rx_flq1_cnt.value.ui32;
                statsp->arp_sent        = chkp->arp_sent.value.ui32;
                statsp->tx_doorbells    = chkp->tx_doorbells.value.ui32;
                statsp->intr_doorbells  = chkp->intr_doorbells.value.ui32;
                statsp->intr1_doorbells = chkp->intr1_doorbells.value.ui32;
                statsp->sleep_cnt       = chkp->sleep_cnt.value.ui32;
                statsp->pe_allocb_cnt   = chkp->pe_allocb_cnt.value.ui32;
                for (i = 0; i < MBLK_MAX; i++) {
                        statsp->tx_descs[i] = chkp->tx_descs[i].value.ui32;
                }
        } else {
                chkp->respQ_empty.value.ui32    = statsp->respQ_empty;
                chkp->respQ_overflow.value.ui32 = statsp->respQ_overflow;
                chkp->freelistQ_empty.value.ui32
                    = statsp->freelistQ_empty;
                chkp->pkt_too_big.value.ui32    = statsp->pkt_too_big;
                chkp->pkt_mismatch.value.ui32   = statsp->pkt_mismatch;
                chkp->cmdQ_full[0].value.ui32   = statsp->cmdQ_full[0];
                chkp->cmdQ_full[1].value.ui32   = statsp->cmdQ_full[1];
                chkp->tx_reclaims[0].value.ui32 = statsp->tx_reclaims[0];
                chkp->tx_reclaims[1].value.ui32 = statsp->tx_reclaims[1];
                chkp->tx_msg_pullups.value.ui32 = statsp->tx_msg_pullups;
                chkp->tx_hdr_pullups.value.ui32 = statsp->tx_hdr_pullups;
                chkp->tx_tcp_ip_frag.value.ui32 = statsp->tx_tcp_ip_frag;
                chkp->tx_udp_ip_frag.value.ui32 = statsp->tx_udp_ip_frag;
                chkp->tx_soft_cksums.value.ui32 = statsp->tx_soft_cksums;
                chkp->tx_need_cpl_space.value.ui32
                    = statsp->tx_need_cpl_space;
                chkp->tx_multi_mblks.value.ui32 = statsp->tx_multi_mblks;
                chkp->tx_no_dvma1.value.ui32    = statsp->tx_no_dvma1;
                chkp->tx_no_dvma2.value.ui32    = statsp->tx_no_dvma2;
                chkp->tx_no_dma1.value.ui32     = statsp->tx_no_dma1;
                chkp->tx_no_dma2.value.ui32     = statsp->tx_no_dma2;
                chkp->rx_cmdq0.value.ui32       = statsp->rx_cmdq0;
                chkp->rx_cmdq1.value.ui32       = statsp->rx_cmdq1;
                chkp->rx_flq0.value.ui32        = statsp->rx_flq0;
                chkp->rx_flq1.value.ui32        = statsp->rx_flq1;
                chkp->rx_flq0_sz.value.ui32     = statsp->rx_flq0_sz;
                chkp->rx_flq1_sz.value.ui32     = statsp->rx_flq1_sz;
                chkp->rx_pkt_drops.value.ui32   = statsp->rx_pkt_drops;
                chkp->rx_pkt_copied.value.ui32  = statsp->rx_pkt_copied;
                chkp->rx_pause_on.value.ui32    = statsp->rx_pause_on;
                chkp->rx_pause_off.value.ui32   = statsp->rx_pause_off;
                chkp->rx_pause_ms.value.ui32    = statsp->rx_pause_ms;
                chkp->rx_pause_spike.value.ui32 = statsp->rx_pause_spike;
                chkp->rx_fl_credits.value.ui32  = statsp->rx_fl_credits;
                chkp->rx_flbuf_fails.value.ui32
                    = statsp->rx_flbuf_fails;
                chkp->rx_flbuf_allocs.value.ui32
                    = statsp->rx_flbuf_allocs;
                chkp->rx_badEopSop.value.ui32   = statsp->rx_badEopSop;
                chkp->rx_flq0_cnt.value.ui32    = statsp->rx_flq0_cnt;
                chkp->rx_flq1_cnt.value.ui32    = statsp->rx_flq1_cnt;
                chkp->arp_sent.value.ui32       = statsp->arp_sent;
                chkp->tx_doorbells.value.ui32   = statsp->tx_doorbells;
                chkp->intr_doorbells.value.ui32 = statsp->intr_doorbells;
                chkp->intr1_doorbells.value.ui32
                    = statsp->intr1_doorbells;
                chkp->sleep_cnt.value.ui32      = statsp->sleep_cnt;
                chkp->pe_allocb_cnt.value.ui32  = statsp->pe_allocb_cnt;
                for (i = 0; i < MBLK_MAX; i++) {
                        chkp->tx_descs[i].value.ui32 = statsp->tx_descs[i];
                }
        }
        return (0);
}
#endif

static uint16_t
calc_ocsum(mblk_t *mp, int offset)
{
        uint8_t *addrp;
        uint32_t src;
        uint32_t dst;

        ipha_t *ihdr = (ipha_t *)(mp->b_rptr + offset);
        uint32_t sum;
        int iplen = IPH_HDR_LENGTH(ihdr);
        struct udphdr *udpp = (struct udphdr *)(mp->b_rptr + offset + iplen);
        uchar_t *byte;
        int len;

        addrp = (uint8_t *)&ihdr->ipha_src;
        src =  ((uint32_t)(addrp[0]) << 24) | ((uint32_t)(addrp[1]) << 16) |
            ((uint32_t)(addrp[2]) << 8) | (uint32_t)(addrp[3]);

        addrp = (uint8_t *)&ihdr->ipha_dst;
        dst =  ((uint32_t)(addrp[0]) << 24) | ((uint32_t)(addrp[1]) << 16) |
            ((uint32_t)(addrp[2]) << 8) | (uint32_t)(addrp[3]);

        sum = (uint16_t)(src >> 16) +
            (uint16_t)(src) +
            (uint16_t)(dst >> 16) +
            (uint16_t)(dst) + (udpp->uh_ulen + htons(IPPROTO_UDP));

        sum = (uint16_t)(sum >> 16) + (uint16_t)(sum);

        if (sum > 0xffff)
                sum -= 0xffff;

        udpp->uh_sum = 0;
        byte = mp->b_rptr + offset + iplen;
        do {
                len = (mp->b_wptr - byte);
                sum = bcksum(byte, len, sum);
                if (sum > 0xffff)
                        sum -= 0xffff;
                mp = mp->b_cont;
                if (mp)
                        byte = mp->b_rptr;
        } while (mp);

        sum = ~sum & 0xffff;

        return (sum);
}