root/usr/src/uts/common/io/cxgbe/t4nex/adapter.h
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source. A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * This file is part of the Chelsio T4 support code.
 *
 * Copyright (C) 2011-2013 Chelsio Communications.  All rights reserved.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the LICENSE file included in this
 * release for licensing terms and conditions.
 */

/*
 * Copyright 2025 Oxide Computer Company
 */

#ifndef __CXGBE_ADAPTER_H
#define __CXGBE_ADAPTER_H

#include <sys/ddi.h>
#include <sys/mac_provider.h>
#include <sys/ethernet.h>
#include <sys/list.h>
#include <sys/containerof.h>
#include <sys/ddi_ufm.h>
#include <sys/mac_provider.h>

#include "firmware/t4fw_interface.h"
#include "shared.h"

struct adapter;
struct port_info;
typedef struct adapter adapter_t;
struct sge_fl;

/* See the _Ingress Context Contents_ section of the T4 Programmers Guide. */
typedef enum t4_iq_esize {
        T4_IQ_ESIZE_16B = 0,
        T4_IQ_ESIZE_32B = 1,
        T4_IQ_ESIZE_64B = 2,
        T4_IQ_ESIZE_128B = 3,
} t4_iq_esize_t;

#define FW_IQ_QSIZE     256
/* At least 64 bytes mandated by the firmware spec */
#define FW_IQ_ESIZE     T4_IQ_ESIZE_64B

#define T4_RX_DEF_QSIZE 1024
/* At least 64 so CPL_RX_PKT will fit */
#define RX_IQ_ESIZE     T4_IQ_ESIZE_64B

/* A flit is an 8 byte quantity. */
#define FLIT_NUM_BYTES          8
#define FLITS_TO_BYTES(nflits)  ((nflits) * FLIT_NUM_BYTES)

/*
 * Egress Queues (EQ) are made up of units called "host credits". Each credit is
 * always 8 flits (64 bytes) in size. The number of entries in the queue as well
 * as the producer and consumer indexes (pidx/cidx) are phrased in units of
 * credits.
 *
 * A freelist (FL) is a type of EQ. It consists of 16-byte aligned, 8-byte
 * pointers to data buffers meant to hold the data of incoming packets. Since an
 * EQ host credit is always 8 flits, and an FL buffer pointer is a single flit,
 * each credit holds 8 FL buffer pointers.
 *
 */
#define FLITS_PER_EQ_HC         8
#define EQ_HC_SIZE              FLITS_PER_EQ_HC * FLIT_NUM_BYTES
#define FL_BUF_PTR_PER_HC       FLITS_PER_EQ_HC

/*
 * Given a number of host credits, calculate the total number of flits
 * contained in those credits.
 */
#define EQ_HC_TO_FLITS(num_credits)     ((num_credits) * FLITS_PER_EQ_HC)

/*
 * Given a number of flits, calculate how many host credits are needed to hold
 * them.
 */
#define EQ_FLITS_TO_HC(num_flits)       (howmany(num_flits, FLITS_PER_EQ_HC))

/*
 * We constrain the max "usable" EQ size so that there is always room for the
 * status page, which may require 1-2 host credits.
 */
#define T4_MAX_EQ_SIZE          (UINT16_MAX - 2)
#define T4_TX_DEF_QSIZE         1024
#define TX_SGL_SEGS             36

/* The maximum number of flits/credits a single WR may consume. */
#define TX_WR_MAX_FLITS         (SGE_MAX_WR_LEN / FLIT_NUM_BYTES)
#define TX_WR_MAX_CREDITS       (TX_WR_MAX_FLITS / FLITS_PER_EQ_HC)

CTASSERT(TX_WR_MAX_FLITS == 64);
CTASSERT(TX_WR_MAX_CREDITS == 8);

#define UDBS_SEG_SHIFT  7       /* log2(UDBS_SEG_SIZE) */
#define UDBS_DB_OFFSET  8       /* offset of the 4B doorbell in a segment */
#define UDBS_WR_OFFSET  64      /* offset of the work request in a segment */

/*
 * A sentinel to mark when the interrupts for an IQ are being forwarded from
 * another IQ which is receiving the actual interrupt.
 */
#define INTR_FORWARDED  UINT_MAX

struct fl_desc {
        uint64_t dptr[FL_BUF_PTR_PER_HC];
};

struct fl_sdesc {
        struct rxbuf *rxb;
};

typedef struct t4_eq_host_credit {
        uint64_t flit[8];
} t4_eq_host_credit_t;

struct tx_sdesc {
        mblk_t *mp_head;
        mblk_t *mp_tail;
        uint32_t txb_used;      /* # of bytes of tx copy buffer used */
        uint16_t hdls_used;     /* # of dma handles used */
        uint16_t credits_used;  /* # of EQ host credits used */
        uint64_t _pad;
};

typedef enum t4_iq_flags {
        IQ_ALLOC_HOST   = (1 << 0),     /* host-side resources allocated */
        IQ_ALLOC_DEV    = (1 << 1),     /* device-side resource allocated */
        IQ_INTR         = (1 << 2),     /* iq takes direct interrupt */

        /* Runtime state flags: */
        IQ_ENABLED      = (1 << 3),
        IQ_POLLING      = (1 << 4),
} t4_iq_flags_t;

struct rxbuf_cache_params {
        dev_info_t              *dip;
        ddi_dma_attr_t          dma_attr_rx;
        ddi_device_acc_attr_t   acc_attr_rx;
        size_t                  buf_size;
};

struct sge_iq_stats {
        uint64_t sis_processed; /* # entries processed from IQ */
        uint64_t sis_overflow;  /* # entries bearing overflow flag */
};

/*
 * These values are designed to match up with what is posted to GTS registers
 * when processing an ingress queue.
 *
 * See: t4_iq_update_intr_cfg() and t4_iq_gts_update().
 */
typedef enum t4_gts_config {
        TGC_SE_INTR_ARM         = 1,
        TGC_TIMER0              = (0 << 1),
        TGC_TIMER1              = (1 << 1),
        TGC_TIMER2              = (2 << 1),
        TGC_TIMER3              = (3 << 1),
        TGC_TIMER4              = (4 << 1),
        TGC_TIMER5              = (5 << 1),
        TGC_START_COUNTER       = (6 << 1),
} t4_gts_config_t;

/*
 * Event IQs are used for firmware events, Tx EGR updates, and IQ forwarded
 * interrupts.
 *
 * Ethernet Rx IQs are used for receiving incoming packets.
 */
typedef enum t4_iq_type {
        TIQT_UNINIT,
        TIQT_EVENT,
        TIQT_ETH_RX,
} t4_iq_type_t;

/* Ingress Queue: T4 is producer, driver is consumer. */
typedef struct t4_sge_iq {
        kmutex_t tsi_lock;

        t4_iq_type_t tsi_iqtype; /* Write Once */
        t4_iq_flags_t tsi_flags; /* tsi_lock */

        /*
         * This field is non-NULL only for Rx queues. It points to the event
         * queue which receives interrupts on its behalf. The event queue
         * processes these "forwarded interrupts" in t4_process_event_iq() and
         * calls into t4_process_rx_iq() for each Rx queue with an interrupt
         * notification message.
         */
        struct t4_sge_iq *tsi_intr_evtq; /* Write Once */
        /*
         * This field is only used by the event queues.
         *
         * As the event queue processes forwarded interrupt notification
         * messages it adds the destination rx queue receving the notification
         * to this list. After the event queue finishes processing its own
         * messages, it then uses this list to process the rx queues which have
         * pending notifications.
         */
        list_node_t tsi_intr_fwd_node; /* tsi_lock */
        /*
         * This field is used by both event queues and rx queues.
         *
         * For event queues this field holds the interrupt vector assigned to
         * this queue.
         *
         * For rx queues it holds the sentinel value INTR_FORWARDED to indicate
         * it has its interrupts forwarded by the event queue. In the current
         * iteration of this driver all rx queues will always have their
         * interrupts forwarded.
         */
        uint_t tsi_intr_idx;    /* Write Once */

        ddi_dma_handle_t tsi_desc_dhdl; /* Write Once */
        ddi_acc_handle_t tsi_desc_ahdl; /* Write Once */

        /* KVA of descriptor ring */
        void *tsi_desc;         /* Write Once */
        /* bus address of descriptor ring */
        uint64_t tsi_desc_ba;   /* Write Once */
        /* current descriptor (at CIDX) */
        const void *tsi_cdesc;  /* tsi_lock */

        /* Sizing and status */
        /* size of each entry in the queue */
        t4_iq_esize_t tsi_esize;  /* Write Once */
        /* entry size in bytes */
        uint16_t tsi_esize_bytes; /* Write Once */
        /* number of entries in the queue */
        uint16_t tsi_qsize;     /* Write Once */
        /* number of usable entries in the queue */
        uint16_t tsi_cap;       /* Write Once */
        /* consumer index */
        uint16_t tsi_cidx;      /* tsi_lock */
        /* generation bit */
        uint8_t tsi_gen;        /* tsi_lock */

        /* GTS config to re-arm queue notification */
        t4_gts_config_t tsi_gts_rearm; /* tsi_lock */
        /* packet count threshold index */
        int8_t tsi_intr_pktc_idx; /* tsi_lock */

        /* SGE context ID for IQ */
        uint16_t tsi_cntxt_id;  /* Write Once */
        /* absolute SGE ID for IQ */
        uint16_t tsi_abs_id;    /* Write Once */

        /* associated adapter */
        struct adapter *tsi_adapter; /* Write Once */
        /* associated freelist (if any) */
        struct sge_fl *tsi_fl;  /* Write Once */

        struct sge_iq_stats tsi_stats; /* tsi_lock */
} t4_sge_iq_t;

/* Result of servicing IQ in t4_iq_service() call */
typedef enum t4_iq_result {
        TIR_SUCCESS,    /* All available entries processed successfully */
        TIR_DISABLED,   /* IQ is disabled */
        TIR_POLLING,    /* non-polling service req'd on polling-cfg'd IQ */
        TIR_ALLOC_FAIL, /* could not allocate packet buffer(s) */
        TIR_BUDGET_MAX, /* hit budget limit while processing entries */
} t4_iq_result_t;

/*
 * Details used when servicing an IQ as part of polling.
 */
struct t4_poll_req {
        mblk_t  *tpr_mp;
        uint_t  tpr_byte_budget;
};

typedef enum t4_eq_flags {
        /* Initialization state flags: */
        EQ_ALLOC_HOST   = (1 << 0),     /* host-side resources allocated */
        EQ_ALLOC_DEV    = (1 << 1),     /* EQ allocated in device firmware */
        EQ_ALLOC_DESC   = (1 << 2),     /* descriptor inputs allocated */

        /* Runtime state flags: */

        EQ_ENABLED      = (1 << 3),     /* ready for submitted work requests */
        /*
         * Short on resources (memory and/or descriptors) while attempting to
         * enqueue work in EQ
         */
        EQ_CORKED       = (1 << 4),
} t4_eq_flags_t;

/*
 * These are the Egress Queue doorbell methods. They are listed in order of
 * preference (WCWR most preferred, KDB least). This ordering is important as
 * the datapath uses ffs (find first set) to pick the preferred method.
 *
 * The first three are "user space" doorbells. They are mapped in BAR2 and are
 * provided to allow kernel-bypass network stacks. However, they can also be
 * used in the kernel and provide benefits such as write combining and per-queue
 * registers (versus KDB which is a single register).
 *
 * The WCWR, Write Combining Work Request, is the preferred method. It allows
 * the driver to push a WR directly to the device without the need for it to
 * perform a DMA read of the hardware ring (to read the EQ host credit). Instead
 * it comes in via the BAR2/UDB memory space and the device increments the pidx
 * accordingly. However, the WCWR is limited to a single WR. It's use is
 * intended for low latency situations or low rate of work, not for throughput.
 *
 * The maximum WCWR for T4 is 256 bytes. For T5/T6 it is 64-128 bytes, depending
 * on the write-combining size of the platform.
 *
 * T4 Firmware Interface Specification, ยง9.2 Egress Queues and Work Requests.
 */
typedef enum t4_doorbells {
        DOORBELL_WCWR   = (1 << 0),
        DOORBELL_UDBWC  = (1 << 1),
        DOORBELL_UDB    = (1 << 2),
        DOORBELL_KDB    = (1 << 3),
} t4_doorbells_t;

/* Egress Queue: driver is producer, T4 is consumer. */
typedef struct t4_sge_eq {
        kmutex_t tse_lock;

        t4_eq_flags_t tse_flags;        /* tse_lock */

        ddi_dma_handle_t tse_ring_dhdl; /* Write Once */
        ddi_acc_handle_t tse_ring_ahdl; /* Write Once */

        /*
         * The ring type is pointer to void because the ring does not consist of
         * descriptors but rather host credits. These host credits carry
         * variable length work requests (WR) as well as the status page (SP) at
         * the end of the ring. We use void* to facilitate the type punning
         * required to work with these various types of EQ entries.
         *
         * In order to access credits and their individual flits we make use to
         * the t4_eq_host_credit_t type.
         */
        void *tse_ring;         /* KVA of ring - Write Once */
        uint64_t tse_ring_ba;   /* bus address of ring - Write Once */

        /*
         * tse_qsize: The number of host credits that may be used for data. This
         * value is static for the lifetime of the queue.
         *
         * tse_qsize_spg: The total number of host credits in the queue. This is
         * 1-2 more credits than tse_qsize to account for the status page at the
         * end of the queue. The status page credits cannot be used for sending
         * data, rather the beginning of the status page is considered the end
         * of the queue as far as the datapath is concerned.
         *
         * tse_avail: The number of host credits that are currently available
         * for use by the host. This is never more than 'tse_qsize - 1' in order
         * to avoid 'tse_pidx==tse_cidx' which we use to indicate an empty
         * queue. This number is updated as credits are used/recycled.
         *
         * tse_pending: The number of credits that have been written by the host
         * but still require a doorbell before the device can consume them. Said
         * another way, it's the number of credits the host's pidx is ahead of
         * the device's cidx.
         */
        uint16_t tse_qsize;     /* Write Once */
        uint16_t tse_qsize_spg; /* Write Once */
        uint16_t tse_avail;     /* tse_lock */
        uint16_t tse_pending;   /* tse_lock */

        /*
         * The pidx is the driver's position in the queue, pointing to the next
         * credit to consume. The cidx is the device's position in the queue,
         * pointing to the last credit it has consumed as of the last status
         * update.
         */
        uint16_t tse_cidx;      /* tse_lock */
        uint16_t tse_pidx;      /* tse_lock */

        /* Doorbell bits */
        t4_doorbells_t tse_doorbells; /* Write Once */
        /* KVA of doorbell (lies within BAR2) */
        caddr_t tse_udb;        /* Write Once */
        /* relative qid within the doorbell page */
        uint_t tse_udb_qid;     /* Write Once */

        struct sge_qstat *tse_spg;      /* status page - Write Once */
        /* IQ that gets egr_update msg for EQ */
        uint16_t tse_iqid;              /* Write Once */
        /* tx channel used by the EQ */
        uint8_t tse_tx_chan;            /* Write Once */
        /* SGE context id for the EQ */
        uint32_t tse_cntxt_id;          /* Write Once */
} t4_sge_eq_t;

typedef enum t4_sfl_flags {
        SFL_STARVING    = (1 << 0),     /* on the list of starving fl's */
        SFL_DOOMED      = (1 << 1),     /* about to be destroyed */
} t4_sfl_flags_t;

struct sge_fl_stats {
        /* These stats describe the receiving of data. */
        uint64_t copy;          /* # of frames copied (allocb) */
        uint64_t copy_fail;     /* # of allocb failures */
        uint64_t wrap;          /* # of frames wrapped (desballoc) */
        uint64_t wrap_fail;     /* # of deballoc failures */

        /* These stats describe the refilling of rx (FL) buffers. */
        uint64_t rxb_recycle;   /* # of rx buffers recycled */
        uint64_t rxb_alloc;     /* # of rx buffers allocated */
        uint64_t rxb_alloc_fail; /* # of rx buffers that failed to allocb */
};

struct sge_fl {
        /*
         * EQ for passing freelist entries to adapter.
         * Must be first field in struct
         */
        t4_sge_eq_t eq;         /* Write Once */

        /*
         * Index at which new buffers are to be placed in the FL descriptor
         * which is currently being produced for the device.
         */
        uint8_t cidx_sdesc;     /* FL_LOCK */
        uint8_t pidx_sdesc;     /* FL_LOCK */

        /* KVA of the software descriptor ring. */
        struct fl_sdesc *sdesc; /* Write Once */
        /* Total number of buffers in the FL.  */
        uint32_t bufs_cap;      /* Write Once */
        /*
         * Number of buffers available to receive data, buffers owned by the
         * device.
         */
        uint32_t bufs_avail;     /* FL_LOCK */
        /* Number of buffers at which the FL is considered "starving". */
        uint32_t bufs_lowat;    /* Write Once */
        /* The byte offset in the current FL buffer. */
        uint32_t offset;        /* FL_LOCK */
        /* Any packet smaller or equal to this is copied (allocb). */
        uint16_t copy_threshold; /* Write Once */

        /* Starvation-related state for this freelist. */
        t4_sfl_flags_t sfl_flags; /* adapter->sfl_lock */
        list_node_t sfl_node;     /* adapter->sfl_lock */

        struct sge_fl_stats stats; /* FL_LOCK */
};

struct sge_txq_stats {
        /* stats for common events first */
        uint64_t txpkts;        /* # of ethernet packets */
        uint64_t txbytes;       /* # of ethernet bytes */
        uint64_t txcsum;        /* # of times hardware assisted with checksum */
        uint64_t tso_wrs;       /* # of IPv4 TSO work requests */
        uint64_t imm_wrs;       /* # of work requests with immediate data */
        uint64_t sgl_wrs;       /* # of work requests with direct SGL */
        uint64_t txpkt_wrs;     /* # of txpkt work requests (not coalesced) */
        uint64_t txpkts_wrs;    /* # of coalesced tx work requests */
        uint64_t txpkts_pkts;   /* # of frames in coalesced tx work requests */
        uint64_t txb_used;      /* # of tx copy buffers used (64 byte each) */
        uint64_t hdl_used;      /* # of DMA handles used */

        /* stats for not-that-common events */
        uint32_t txb_full;      /* txb ran out of space */
        uint32_t dma_hdl_failed; /* couldn't obtain DMA handle */
        uint32_t dma_map_failed; /* couldn't obtain DMA mapping */
        uint32_t qfull;         /* out of hardware descriptors */
        uint32_t pullup_early;  /* # of pullups before starting frame's SGL */
        uint32_t pullup_late;   /* # of pullups while building frame's SGL */
        uint32_t pullup_failed; /* # of failed pullups */
        uint32_t csum_failed;   /* # of csum reqs we failed to fulfill */
};

/* Ethernet packet transmission queue */
struct sge_txq {
        t4_sge_eq_t eq;

        struct port_info *port;
        struct tx_sdesc *sdesc; /* KVA of software descriptor ring */

        mac_ring_handle_t ring_handle;

        /* DMA handles used for tx */
        ddi_dma_handle_t *tx_dhdl;
        uint32_t tx_dhdl_total; /* Total # of handles */
        uint32_t tx_dhdl_pidx;  /* next handle to be used */
        uint32_t tx_dhdl_cidx;  /* reclaimed up to this index */
        uint32_t tx_dhdl_avail; /* # of available handles */

        /* Copy buffers for tx */
        ddi_dma_handle_t txb_dhdl;
        ddi_acc_handle_t txb_ahdl;
        caddr_t txb_va;         /* KVA of copy buffers area */
        uint64_t txb_ba;        /* bus address of copy buffers area */
        uint32_t txb_size;      /* total size */
        uint32_t txb_next;      /* offset of next useable area in the buffer */
        uint32_t txb_avail;     /* # of bytes available */
        uint16_t copy_threshold; /* anything this size or less is copied up */

        kstat_t *ksp;
        struct sge_txq_stats stats;
};

struct sge_rxq_stats {
        /* stats for common events first */
        uint64_t rxcsum;        /* # of times hardware assisted with checksum */
        uint64_t rxpkts;        /* # of ethernet packets */
        uint64_t rxbytes;       /* # of ethernet bytes */
};

/* Ethernet packet receive queue */
struct sge_rxq {
        t4_sge_iq_t iq;
        struct sge_fl fl;       /* Freelist for packet receive buffers */

        struct port_info *port;

        mac_ring_handle_t ring_handle;
        uint64_t ring_gen_num;

        kstat_t *ksp;
        struct sge_rxq_stats stats;
};

typedef enum t4_port_flags {
        TPF_INIT_DONE   = (1 << 0),
        TPF_OPEN        = (1 << 1),
        TPF_VI_ENABLED  = (1 << 2),
} t4_port_flags_t;

typedef enum t4_port_feat {
        CXGBE_HW_LSO    = (1 << 0),
        CXGBE_HW_CSUM   = (1 << 1),
} t4_port_feat_t;


struct port_info {
        kmutex_t        lock;
        dev_info_t      *dip;
        struct adapter  *adapter;
        uint8_t         port_id;

        t4_port_flags_t flags;
        t4_port_feat_t  features;

        mac_handle_t    mh;
        int             mtu;
        uint8_t         hw_addr[ETHERADDRL];
        int16_t         xact_addr_filt; /* index of exact MAC address filter */

        uint16_t        rxq_count;      /* # of RX queues */
        uint16_t        rxq_start;      /* index of first RX queue */
        uint16_t        txq_count;      /* # of TX queues */
        uint16_t        txq_start;      /* index of first TX queue */

        /*
         * Array of IQs for queue events, such as interrupt forward events
         * for Rx queue processing and completion events for Tx queues.
         * Only available when TIP_PER_PORT is selected. The size is based
         * on adapter.intr_queue_cfg.intr_per_port.
         */
        t4_sge_iq_t     *intr_iqs;

        kstat_t *ksp_config;
        kstat_t *ksp_info;
        kstat_t *ksp_fec;

        /* Port attributes/data set by common code: */
        uint16_t        viid;
        uint16_t        rss_size;       /* size of VI's RSS table slice */

        uint8_t         port_type;
        int8_t          mdio_addr;
        uint8_t         mod_type;

        uint8_t         lport;
        uint8_t         tx_chan;
        uint8_t         rx_chan;
        uint8_t         rx_cchan;

        uint8_t         rss_mode;

        uint8_t         tmr_idx;
        int8_t          pktc_idx;
        uint8_t         dbq_timer_idx;

        struct link_config link_cfg;
        uint8_t         macaddr_cnt;

        u8 vivld;
        u8 vin;
        u8 smt_idx;

        /* Mirroring bits utilized by common code (unused by our driver) */
        u16 viid_mirror;
        u8 vivld_mirror;
        u8 vin_mirror;
};

struct sge_info {
        uint_t fl_starve_threshold;
        uint64_t dbq_timer_tick;
        uint16_t dbq_timers[SGE_NDBQTIMERS];

        uint_t eq_spg_len;      /* EQ status page length in host credits */
        uint_t pktshift;        /* padding between CPL & packet data */
        uint_t fl_align;        /* response queue message alignment */
        uint8_t fwq_tmr_idx;    /* Intr. coalesce timer for FWQ */
        int8_t fwq_pktc_idx;    /* Intr. coalesce count for FWQ */

        t4_sge_iq_t fwq;        /* Firmware event queue */

        uint_t rxq_count;       /* total RX queues (all ports and the rest) */
        uint_t txq_count;       /* total TX queues (all ports and the rest) */
        struct sge_txq *txq;    /* NIC TX queues */
        struct sge_rxq *rxq;    /* NIC RX queues */

        /*
         * Adapters uses 16-bit "context IDs" to uniquely identify queues.
         *
         * References to the queues, indexed by said context IDs are maintained
         * here, using the start/end values queried from the adapter.
         */
        uint_t iqmap_start;     /* IQ context id map start index */
        uint_t rxqmap_start;    /* IQ context id map start index */
        uint_t eqmap_start;     /* EQ context id map start index */
        uint_t iqmap_sz;        /* size of IQ context id map */
        uint_t eqmap_sz;        /* size of EQ context id map */
        t4_sge_iq_t **iqmap;    /* iq->cntxt_id to IQ mapping */
        t4_sge_eq_t **eqmap;    /* eq->cntxt_id to EQ mapping */

        /* Device access and DMA attributes for all the descriptor rings */
        ddi_device_acc_attr_t acc_attr_desc;
        ddi_dma_attr_t  dma_attr_desc;

        /* Device access and DMA attributes for TX buffers */
        ddi_device_acc_attr_t acc_attr_tx;
        ddi_dma_attr_t  dma_attr_tx;

        /* Device access and DMA attributes for RX buffers are in rxb_params */
        kmem_cache_t *rxbuf_cache;
        struct rxbuf_cache_params rxb_params;
};

struct driver_properties {
        uint8_t ethq_tmr_idx;
        int8_t ethq_pktc_idx;
        uint8_t dbq_timer_idx;
        uint8_t fwq_tmr_idx;
        int8_t fwq_pktc_idx;
        uint16_t qsize_txq;
        uint16_t qsize_rxq;

        uint_t holdoff_timer_us[SGE_NTIMERS];
        uint_t holdoff_pktcnt[SGE_NCOUNTERS];

        bool write_combine;
        int t4_fw_install;
};

typedef struct t4_mbox_waiter {
        list_node_t node;
        kthread_t *thread;
} t4_mbox_waiter_t;

typedef enum t4_adapter_flags {
        /* Initialization progress status bits */
        TAF_INIT_DONE   = (1 << 0),
        TAF_FW_OK       = (1 << 1),
        TAF_INTR_ALLOC  = (1 << 2),

        /* State & capability bits */
        TAF_MASTER_PF   = (1 << 8),
        TAF_DBQ_TIMER   = (1 << 9),
} t4_adapter_flags_t;

/* Plan for interrupt allocation */
typedef enum t4_intr_plan {
        /* Everything on a single interrupt */
        TIP_SINGLE,
        /* One for device errors, one FWQ (including forwarded intrs) */
        TIP_ERR_QUEUES,
        /* 1 + 1 for errors and FWQ, with rest divided evenly between ports */
        TIP_PER_PORT,
} t4_intr_plan_t;

struct t4_intrs_queues {
        /* The DDI_INTR_TYPE_* value negotiated. */
        int intr_type;

        /*
         * The plan for interrupt allocation, based on the interrupt type
         * and number of interrupts available. See the block comment in
         * t4_nexus.c for more information.
         */
        t4_intr_plan_t intr_plan;

        /*
         * The number of interrupts available (intr_avail) for use vs. the
         * number of interrupts the driver has decided to make use of
         * (intr_count). These values may be different depending on the
         * number available and the port count of the attached part.
         */
        int intr_avail;
        int intr_count;

        /*
         * The number of interrupts per port for use with event queues.
         * These interrupts are used to take delivery of Tx recycling
         * messages and Rx packet delivery.
         */
        uint_t intr_per_port;

        /*
         * Track the number of IQs allocated for use with interrupts. We track
         * this to know how many IQs we have leftover for Rx queue usage.
         */
        uint_t num_iqs;

        /* The maximum number of RX/TX queues per port. */
        uint_t port_max_rxq;
        uint_t port_max_txq;
};

/*
 * WO - Write Once at initialization time.
 */
struct adapter {
        list_node_t node;
        dev_info_t *dip;
        dev_t dev;

        unsigned int pf;
        unsigned int mbox;

        unsigned int vpd_busy;
        unsigned int vpd_flag;

        u32 t4_bar0;

        uint_t open;    /* character device is open */

        /* PCI config space access handle */
        ddi_acc_handle_t pci_regh;

        /* MMIO register access handle */
        ddi_acc_handle_t regh;
        caddr_t regp;
        /* BAR2 register access handle */
        ddi_acc_handle_t bar2_hdl;
        caddr_t bar2_ptr;

        /* Interrupt information */
        ddi_intr_handle_t *intr_handle;
        int intr_cap;
        uint_t intr_pri;

        struct driver_properties props;
        kstat_t *ksp;
        kstat_t *ksp_stat;

        struct sge_info sge;

        struct port_info *port[MAX_NPORTS];
        uint8_t chan_map[NCHAN];
        uint32_t filter_mode;

        t4_adapter_flags_t flags;
        t4_doorbells_t doorbells;

        unsigned int cfcsum;
        struct adapter_params params;
        struct t4_intrs_queues intr_queue_cfg;

        kmutex_t lock;
        kcondvar_t cv;

        /*
         * Starving freelist state
         *
         * sfl_lock protects the `sfl_flags` and `sfl_node` fields in all sge_fl
         * structs owned by this adapter.
         */
        kmutex_t sfl_lock;
        list_t sfl_list;
        timeout_id_t sfl_timer;

        /* Sensors */
        id_t temp_sensor;
        id_t volt_sensor;

        ddi_ufm_handle_t *ufm_hdl;

        /* support for single-threading access to adapter mailbox registers */
        kmutex_t mbox_lock;
        kcondvar_t mbox_cv;
        list_t mbox_list;
};

#define ADAPTER_LOCK(sc)                mutex_enter(&(sc)->lock)
#define ADAPTER_UNLOCK(sc)              mutex_exit(&(sc)->lock)
#define ADAPTER_LOCK_ASSERT_OWNED(sc)   ASSERT(mutex_owned(&(sc)->lock))
#define ADAPTER_LOCK_ASSERT_NOTOWNED(sc) ASSERT(!mutex_owned(&(sc)->lock))

#define PORT_LOCK(pi)                   mutex_enter(&(pi)->lock)
#define PORT_UNLOCK(pi)                 mutex_exit(&(pi)->lock)
#define PORT_LOCK_ASSERT_OWNED(pi)      ASSERT(mutex_owned(&(pi)->lock))
#define PORT_LOCK_ASSERT_NOTOWNED(pi)   ASSERT(!mutex_owned(&(pi)->lock))

#define IQ_LOCK(iq)                     mutex_enter(&(iq)->tsi_lock)
#define IQ_UNLOCK(iq)                   mutex_exit(&(iq)->tsi_lock)
#define IQ_LOCK_ASSERT_OWNED(iq)        ASSERT(mutex_owned(&(iq)->tsi_lock))
#define IQ_LOCK_ASSERT_NOTOWNED(iq)     ASSERT(!mutex_owned(&(iq)->tsi_lock))

#define EQ_LOCK(eq)                     mutex_enter(&(eq)->tse_lock)
#define EQ_UNLOCK(eq)                   mutex_exit(&(eq)->tse_lock)
#define EQ_LOCK_ASSERT_OWNED(eq)        ASSERT(mutex_owned(&(eq)->tse_lock))
#define EQ_LOCK_ASSERT_NOTOWNED(eq)     ASSERT(!mutex_owned(&(eq)->tse_lock))

/* Freelist state is protected by its EQ lock */
#define FL_LOCK(fl)                     EQ_LOCK(&(fl)->eq)
#define FL_UNLOCK(fl)                   EQ_UNLOCK(&(fl)->eq)
#define FL_LOCK_ASSERT_OWNED(fl)        EQ_LOCK_ASSERT_OWNED(&(fl)->eq)
#define FL_LOCK_ASSERT_NOTOWNED(fl)     EQ_LOCK_ASSERT_NOTOWNED(&(fl)->eq)

#define TXQ_LOCK(txq)                   EQ_LOCK(&(txq)->eq)
#define TXQ_UNLOCK(txq)                 EQ_UNLOCK(&(txq)->eq)
#define TXQ_LOCK_ASSERT_OWNED(txq)      EQ_LOCK_ASSERT_OWNED(&(txq)->eq)
#define TXQ_LOCK_ASSERT_NOTOWNED(txq)   EQ_LOCK_ASSERT_NOTOWNED(&(txq)->eq)

#define for_each_txq(pi, iter, txq) \
        txq = &pi->adapter->sge.txq[pi->txq_start]; \
        for (iter = 0; iter < pi->txq_count; ++iter, ++txq)
#define for_each_rxq(pi, iter, rxq) \
        rxq = &pi->adapter->sge.rxq[pi->rxq_start]; \
        for (iter = 0; iter < pi->rxq_count; ++iter, ++rxq)

static inline struct port_info *
adap2pinfo(struct adapter *sc, int idx)
{
        return (sc->port[idx]);
}

static inline unsigned int t4_use_ldst(struct adapter *adap)
{
        return (adap->flags & TAF_FW_OK);
}

static inline void t4_db_full(struct adapter *adap) {}
static inline void t4_db_dropped(struct adapter *adap) {}

/* Is chip version equal to specified value? */
static inline bool
t4_cver_eq(const adapter_t *adap, uint8_t ver)
{
        return (CHELSIO_CHIP_VERSION(adap->params.chip) == ver);
}

/* Is chip version greater than or equal to specified value? */
static inline bool
t4_cver_ge(const adapter_t *adap, uint8_t ver)
{
        return (CHELSIO_CHIP_VERSION(adap->params.chip) >= ver);
}

/* t4_nexus.c */
int t4_port_full_init(struct port_info *);

uint32_t t4_read_reg(struct adapter *, uint32_t);
void t4_write_reg(struct adapter *, uint32_t, uint32_t);
uint64_t t4_read_reg64(struct adapter *, uint32_t);
void t4_write_reg64(struct adapter *, uint32_t, uint64_t);

void t4_mbox_waiter_add(struct adapter *, t4_mbox_waiter_t *);
void t4_mbox_waiter_remove(struct adapter *, t4_mbox_waiter_t *);
bool t4_mbox_wait_owner(struct adapter *, uint_t, bool);

/* t4_debug.c */
void t4_debug_init(void);
void t4_debug_fini(void);

/* t4_sge.c */
void t4_sge_init(struct adapter *);
int t4_alloc_evt_iqs(struct adapter *);
void t4_free_evt_iqs(struct adapter *);
void t4_port_kstats_init(struct port_info *);
void t4_port_kstats_fini(struct port_info *);
int t4_port_queues_init(struct port_info *);
void t4_port_queues_fini(struct port_info *);
void t4_port_queues_enable(struct port_info *pi);
void t4_port_queues_disable(struct port_info *pi);
uint_t t4_intr_all(caddr_t, caddr_t);
uint_t t4_intr_err(caddr_t, caddr_t);
uint_t t4_intr_fwq(caddr_t, caddr_t);
uint_t t4_intr_port_queue(caddr_t, caddr_t);
void t4_iq_gts_update(t4_sge_iq_t *, t4_gts_config_t, uint16_t);
void t4_iq_update_intr_cfg(t4_sge_iq_t *, uint8_t, int8_t);
void t4_eq_update_dbq_timer(t4_sge_eq_t *, struct port_info *);

mblk_t *t4_eth_tx(void *, mblk_t *);
t4_iq_result_t t4_process_rx_iq(t4_sge_iq_t *, uint_t, struct t4_poll_req *);

/* t4_mac.c */
void t4_os_link_changed(struct adapter *sc, int idx, int link_stat);
void t4_mac_tx_update(struct port_info *pi, struct sge_txq *txq);
int t4_addmac(void *arg, const uint8_t *ucaddr);
const char **t4_get_priv_props(struct port_info *, size_t *);
uint8_t t4_choose_holdoff_timer(struct adapter *, uint_t);
int8_t t4_choose_holdoff_pktcnt(struct adapter *, int);
uint_t t4_choose_dbq_timer(struct adapter *, uint_t);
extern mac_callbacks_t t4_mac_callbacks;

/* t4_ioctl.c */
int t4_ioctl(struct adapter *sc, int cmd, void *data, int mode);

#endif /* __CXGBE_ADAPTER_H */