3561 lines
94 KiB
C
3561 lines
94 KiB
C
/* $OpenBSD: if_bnxt.c,v 1.45 2024/01/19 03:25:13 jmatthew Exp $ */
|
|
/*-
|
|
* Broadcom NetXtreme-C/E network driver.
|
|
*
|
|
* Copyright (c) 2016 Broadcom, All Rights Reserved.
|
|
* The term Broadcom refers to Broadcom Limited and/or its subsidiaries
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS'
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2018 Jonathan Matthew <jmatthew@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
|
|
#include "bpfilter.h"
|
|
#include "vlan.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/device.h>
|
|
#include <sys/stdint.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/atomic.h>
|
|
#include <sys/intrmap.h>
|
|
|
|
#include <machine/bus.h>
|
|
|
|
#include <dev/pci/pcireg.h>
|
|
#include <dev/pci/pcivar.h>
|
|
#include <dev/pci/pcidevs.h>
|
|
|
|
#include <dev/pci/if_bnxtreg.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_media.h>
|
|
#include <net/route.h>
|
|
#include <net/toeplitz.h>
|
|
|
|
#if NBPFILTER > 0
|
|
#include <net/bpf.h>
|
|
#endif
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/ip6.h>
|
|
#include <netinet/if_ether.h>
|
|
#include <netinet/tcp.h>
|
|
#include <netinet/tcp_timer.h>
|
|
#include <netinet/tcp_var.h>
|
|
|
|
#define BNXT_HWRM_BAR 0x10
|
|
#define BNXT_DOORBELL_BAR 0x18
|
|
|
|
#define BNXT_MAX_QUEUES 8
|
|
|
|
#define BNXT_CP_RING_ID_BASE 0
|
|
#define BNXT_RX_RING_ID_BASE (BNXT_MAX_QUEUES + 1)
|
|
#define BNXT_AG_RING_ID_BASE ((BNXT_MAX_QUEUES * 2) + 1)
|
|
#define BNXT_TX_RING_ID_BASE ((BNXT_MAX_QUEUES * 3) + 1)
|
|
|
|
#define BNXT_MAX_MTU 9500
|
|
#define BNXT_AG_BUFFER_SIZE 8192
|
|
|
|
#define BNXT_CP_PAGES 4
|
|
|
|
#define BNXT_MAX_TX_SEGS 31
|
|
#define BNXT_TX_SLOTS(bs) (bs->bs_map->dm_nsegs + 1)
|
|
|
|
#define BNXT_HWRM_SHORT_REQ_LEN sizeof(struct hwrm_short_input)
|
|
|
|
#define BNXT_HWRM_LOCK_INIT(_sc, _name) \
|
|
mtx_init_flags(&sc->sc_lock, IPL_NET, _name, 0)
|
|
#define BNXT_HWRM_LOCK(_sc) mtx_enter(&_sc->sc_lock)
|
|
#define BNXT_HWRM_UNLOCK(_sc) mtx_leave(&_sc->sc_lock)
|
|
#define BNXT_HWRM_LOCK_DESTROY(_sc) /* nothing */
|
|
#define BNXT_HWRM_LOCK_ASSERT(_sc) MUTEX_ASSERT_LOCKED(&_sc->sc_lock)
|
|
|
|
#define BNXT_FLAG_VF 0x0001
|
|
#define BNXT_FLAG_NPAR 0x0002
|
|
#define BNXT_FLAG_WOL_CAP 0x0004
|
|
#define BNXT_FLAG_SHORT_CMD 0x0008
|
|
#define BNXT_FLAG_MSIX 0x0010
|
|
|
|
/* NVRam stuff has a five minute timeout */
|
|
#define BNXT_NVM_TIMEO (5 * 60 * 1000)
|
|
|
|
#define NEXT_CP_CONS_V(_ring, _cons, _v_bit) \
|
|
do { \
|
|
if (++(_cons) == (_ring)->ring_size) \
|
|
((_cons) = 0, (_v_bit) = !_v_bit); \
|
|
} while (0);
|
|
|
|
struct bnxt_ring {
|
|
uint64_t paddr;
|
|
uint64_t doorbell;
|
|
caddr_t vaddr;
|
|
uint32_t ring_size;
|
|
uint16_t id;
|
|
uint16_t phys_id;
|
|
};
|
|
|
|
struct bnxt_cp_ring {
|
|
struct bnxt_ring ring;
|
|
void *irq;
|
|
struct bnxt_softc *softc;
|
|
uint32_t cons;
|
|
int v_bit;
|
|
uint32_t commit_cons;
|
|
int commit_v_bit;
|
|
struct ctx_hw_stats *stats;
|
|
uint32_t stats_ctx_id;
|
|
struct bnxt_dmamem *ring_mem;
|
|
};
|
|
|
|
struct bnxt_grp_info {
|
|
uint32_t grp_id;
|
|
uint16_t stats_ctx;
|
|
uint16_t rx_ring_id;
|
|
uint16_t cp_ring_id;
|
|
uint16_t ag_ring_id;
|
|
};
|
|
|
|
struct bnxt_vnic_info {
|
|
uint16_t id;
|
|
uint16_t def_ring_grp;
|
|
uint16_t cos_rule;
|
|
uint16_t lb_rule;
|
|
uint16_t mru;
|
|
|
|
uint32_t flags;
|
|
#define BNXT_VNIC_FLAG_DEFAULT 0x01
|
|
#define BNXT_VNIC_FLAG_BD_STALL 0x02
|
|
#define BNXT_VNIC_FLAG_VLAN_STRIP 0x04
|
|
|
|
uint64_t filter_id;
|
|
uint32_t flow_id;
|
|
|
|
uint16_t rss_id;
|
|
};
|
|
|
|
struct bnxt_slot {
|
|
bus_dmamap_t bs_map;
|
|
struct mbuf *bs_m;
|
|
};
|
|
|
|
struct bnxt_dmamem {
|
|
bus_dmamap_t bdm_map;
|
|
bus_dma_segment_t bdm_seg;
|
|
size_t bdm_size;
|
|
caddr_t bdm_kva;
|
|
};
|
|
#define BNXT_DMA_MAP(_bdm) ((_bdm)->bdm_map)
|
|
#define BNXT_DMA_LEN(_bdm) ((_bdm)->bdm_size)
|
|
#define BNXT_DMA_DVA(_bdm) ((u_int64_t)(_bdm)->bdm_map->dm_segs[0].ds_addr)
|
|
#define BNXT_DMA_KVA(_bdm) ((void *)(_bdm)->bdm_kva)
|
|
|
|
struct bnxt_rx_queue {
|
|
struct bnxt_softc *rx_softc;
|
|
struct ifiqueue *rx_ifiq;
|
|
struct bnxt_dmamem *rx_ring_mem; /* rx and ag */
|
|
struct bnxt_ring rx_ring;
|
|
struct bnxt_ring rx_ag_ring;
|
|
struct if_rxring rxr[2];
|
|
struct bnxt_slot *rx_slots;
|
|
struct bnxt_slot *rx_ag_slots;
|
|
int rx_prod;
|
|
int rx_cons;
|
|
int rx_ag_prod;
|
|
int rx_ag_cons;
|
|
struct timeout rx_refill;
|
|
};
|
|
|
|
struct bnxt_tx_queue {
|
|
struct bnxt_softc *tx_softc;
|
|
struct ifqueue *tx_ifq;
|
|
struct bnxt_dmamem *tx_ring_mem;
|
|
struct bnxt_ring tx_ring;
|
|
struct bnxt_slot *tx_slots;
|
|
int tx_prod;
|
|
int tx_cons;
|
|
int tx_ring_prod;
|
|
int tx_ring_cons;
|
|
};
|
|
|
|
struct bnxt_queue {
|
|
char q_name[8];
|
|
int q_index;
|
|
void *q_ihc;
|
|
struct bnxt_softc *q_sc;
|
|
struct bnxt_cp_ring q_cp;
|
|
struct bnxt_rx_queue q_rx;
|
|
struct bnxt_tx_queue q_tx;
|
|
struct bnxt_grp_info q_rg;
|
|
};
|
|
|
|
struct bnxt_softc {
|
|
struct device sc_dev;
|
|
struct arpcom sc_ac;
|
|
struct ifmedia sc_media;
|
|
|
|
struct mutex sc_lock;
|
|
|
|
pci_chipset_tag_t sc_pc;
|
|
pcitag_t sc_tag;
|
|
bus_dma_tag_t sc_dmat;
|
|
|
|
bus_space_tag_t sc_hwrm_t;
|
|
bus_space_handle_t sc_hwrm_h;
|
|
bus_size_t sc_hwrm_s;
|
|
|
|
struct bnxt_dmamem *sc_cmd_resp;
|
|
uint16_t sc_cmd_seq;
|
|
uint16_t sc_max_req_len;
|
|
uint32_t sc_cmd_timeo;
|
|
uint32_t sc_flags;
|
|
|
|
bus_space_tag_t sc_db_t;
|
|
bus_space_handle_t sc_db_h;
|
|
bus_size_t sc_db_s;
|
|
|
|
void *sc_ih;
|
|
|
|
int sc_hwrm_ver;
|
|
int sc_tx_queue_id;
|
|
|
|
struct bnxt_vnic_info sc_vnic;
|
|
struct bnxt_dmamem *sc_stats_ctx_mem;
|
|
struct bnxt_dmamem *sc_rx_cfg;
|
|
|
|
struct bnxt_cp_ring sc_cp_ring;
|
|
|
|
int sc_nqueues;
|
|
struct intrmap *sc_intrmap;
|
|
struct bnxt_queue sc_queues[BNXT_MAX_QUEUES];
|
|
};
|
|
#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
|
|
|
|
const struct pci_matchid bnxt_devices[] = {
|
|
{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM57301 },
|
|
{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM57302 },
|
|
{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM57304 },
|
|
{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM57311 },
|
|
{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM57312 },
|
|
{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM57314 },
|
|
{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM57402 },
|
|
{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM57404 },
|
|
{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM57406 },
|
|
{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM57407 },
|
|
{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM57412 },
|
|
{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM57414 },
|
|
{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM57416 },
|
|
{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM57416_SFP },
|
|
{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM57417 },
|
|
{ PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM57417_SFP }
|
|
};
|
|
|
|
int bnxt_match(struct device *, void *, void *);
|
|
void bnxt_attach(struct device *, struct device *, void *);
|
|
|
|
void bnxt_up(struct bnxt_softc *);
|
|
void bnxt_down(struct bnxt_softc *);
|
|
void bnxt_iff(struct bnxt_softc *);
|
|
int bnxt_ioctl(struct ifnet *, u_long, caddr_t);
|
|
int bnxt_rxrinfo(struct bnxt_softc *, struct if_rxrinfo *);
|
|
void bnxt_start(struct ifqueue *);
|
|
int bnxt_admin_intr(void *);
|
|
int bnxt_intr(void *);
|
|
void bnxt_watchdog(struct ifnet *);
|
|
void bnxt_media_status(struct ifnet *, struct ifmediareq *);
|
|
int bnxt_media_change(struct ifnet *);
|
|
int bnxt_media_autonegotiate(struct bnxt_softc *);
|
|
|
|
struct cmpl_base *bnxt_cpr_next_cmpl(struct bnxt_softc *, struct bnxt_cp_ring *);
|
|
void bnxt_cpr_commit(struct bnxt_softc *, struct bnxt_cp_ring *);
|
|
void bnxt_cpr_rollback(struct bnxt_softc *, struct bnxt_cp_ring *);
|
|
|
|
void bnxt_mark_cpr_invalid(struct bnxt_cp_ring *);
|
|
void bnxt_write_cp_doorbell(struct bnxt_softc *, struct bnxt_ring *,
|
|
int);
|
|
void bnxt_write_cp_doorbell_index(struct bnxt_softc *,
|
|
struct bnxt_ring *, uint32_t, int);
|
|
void bnxt_write_rx_doorbell(struct bnxt_softc *, struct bnxt_ring *,
|
|
int);
|
|
void bnxt_write_tx_doorbell(struct bnxt_softc *, struct bnxt_ring *,
|
|
int);
|
|
|
|
int bnxt_rx_fill(struct bnxt_queue *);
|
|
u_int bnxt_rx_fill_slots(struct bnxt_softc *, struct bnxt_ring *, void *,
|
|
struct bnxt_slot *, uint *, int, uint16_t, u_int);
|
|
void bnxt_refill(void *);
|
|
int bnxt_rx(struct bnxt_softc *, struct bnxt_rx_queue *,
|
|
struct bnxt_cp_ring *, struct mbuf_list *, int *, int *,
|
|
struct cmpl_base *);
|
|
|
|
void bnxt_txeof(struct bnxt_softc *, struct bnxt_tx_queue *, int *,
|
|
struct cmpl_base *);
|
|
|
|
int bnxt_set_cp_ring_aggint(struct bnxt_softc *, struct bnxt_cp_ring *);
|
|
|
|
int _hwrm_send_message(struct bnxt_softc *, void *, uint32_t);
|
|
int hwrm_send_message(struct bnxt_softc *, void *, uint32_t);
|
|
void bnxt_hwrm_cmd_hdr_init(struct bnxt_softc *, void *, uint16_t);
|
|
int bnxt_hwrm_err_map(uint16_t err);
|
|
|
|
/* HWRM Function Prototypes */
|
|
int bnxt_hwrm_ring_alloc(struct bnxt_softc *, uint8_t,
|
|
struct bnxt_ring *, uint16_t, uint32_t, int);
|
|
int bnxt_hwrm_ring_free(struct bnxt_softc *, uint8_t,
|
|
struct bnxt_ring *);
|
|
int bnxt_hwrm_ver_get(struct bnxt_softc *);
|
|
int bnxt_hwrm_queue_qportcfg(struct bnxt_softc *);
|
|
int bnxt_hwrm_func_drv_rgtr(struct bnxt_softc *);
|
|
int bnxt_hwrm_func_qcaps(struct bnxt_softc *);
|
|
int bnxt_hwrm_func_qcfg(struct bnxt_softc *);
|
|
int bnxt_hwrm_func_reset(struct bnxt_softc *);
|
|
int bnxt_hwrm_vnic_ctx_alloc(struct bnxt_softc *, uint16_t *);
|
|
int bnxt_hwrm_vnic_ctx_free(struct bnxt_softc *, uint16_t *);
|
|
int bnxt_hwrm_vnic_cfg(struct bnxt_softc *,
|
|
struct bnxt_vnic_info *);
|
|
int bnxt_hwrm_vnic_cfg_placement(struct bnxt_softc *,
|
|
struct bnxt_vnic_info *vnic);
|
|
int bnxt_hwrm_stat_ctx_alloc(struct bnxt_softc *,
|
|
struct bnxt_cp_ring *, uint64_t);
|
|
int bnxt_hwrm_stat_ctx_free(struct bnxt_softc *,
|
|
struct bnxt_cp_ring *);
|
|
int bnxt_hwrm_ring_grp_alloc(struct bnxt_softc *,
|
|
struct bnxt_grp_info *);
|
|
int bnxt_hwrm_ring_grp_free(struct bnxt_softc *,
|
|
struct bnxt_grp_info *);
|
|
int bnxt_hwrm_vnic_alloc(struct bnxt_softc *,
|
|
struct bnxt_vnic_info *);
|
|
int bnxt_hwrm_vnic_free(struct bnxt_softc *,
|
|
struct bnxt_vnic_info *);
|
|
int bnxt_hwrm_cfa_l2_set_rx_mask(struct bnxt_softc *,
|
|
uint32_t, uint32_t, uint64_t, uint32_t);
|
|
int bnxt_hwrm_set_filter(struct bnxt_softc *,
|
|
struct bnxt_vnic_info *);
|
|
int bnxt_hwrm_free_filter(struct bnxt_softc *,
|
|
struct bnxt_vnic_info *);
|
|
int bnxt_hwrm_vnic_rss_cfg(struct bnxt_softc *,
|
|
struct bnxt_vnic_info *, uint32_t, daddr_t, daddr_t);
|
|
int bnxt_cfg_async_cr(struct bnxt_softc *, struct bnxt_cp_ring *);
|
|
int bnxt_hwrm_nvm_get_dev_info(struct bnxt_softc *, uint16_t *,
|
|
uint16_t *, uint32_t *, uint32_t *, uint32_t *, uint32_t *);
|
|
int bnxt_hwrm_port_phy_qcfg(struct bnxt_softc *,
|
|
struct ifmediareq *);
|
|
int bnxt_hwrm_func_rgtr_async_events(struct bnxt_softc *);
|
|
int bnxt_get_sffpage(struct bnxt_softc *, struct if_sffpage *);
|
|
|
|
/* not used yet: */
|
|
#if 0
|
|
int bnxt_hwrm_func_drv_unrgtr(struct bnxt_softc *softc, bool shutdown);
|
|
|
|
int bnxt_hwrm_port_qstats(struct bnxt_softc *softc);
|
|
|
|
|
|
int bnxt_hwrm_vnic_tpa_cfg(struct bnxt_softc *softc);
|
|
void bnxt_validate_hw_lro_settings(struct bnxt_softc *softc);
|
|
int bnxt_hwrm_fw_reset(struct bnxt_softc *softc, uint8_t processor,
|
|
uint8_t *selfreset);
|
|
int bnxt_hwrm_fw_qstatus(struct bnxt_softc *softc, uint8_t type,
|
|
uint8_t *selfreset);
|
|
int bnxt_hwrm_fw_get_time(struct bnxt_softc *softc, uint16_t *year,
|
|
uint8_t *month, uint8_t *day, uint8_t *hour, uint8_t *minute,
|
|
uint8_t *second, uint16_t *millisecond, uint16_t *zone);
|
|
int bnxt_hwrm_fw_set_time(struct bnxt_softc *softc, uint16_t year,
|
|
uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second,
|
|
uint16_t millisecond, uint16_t zone);
|
|
|
|
#endif
|
|
|
|
|
|
const struct cfattach bnxt_ca = {
|
|
sizeof(struct bnxt_softc), bnxt_match, bnxt_attach
|
|
};
|
|
|
|
struct cfdriver bnxt_cd = {
|
|
NULL, "bnxt", DV_IFNET
|
|
};
|
|
|
|
struct bnxt_dmamem *
|
|
bnxt_dmamem_alloc(struct bnxt_softc *sc, size_t size)
|
|
{
|
|
struct bnxt_dmamem *m;
|
|
int nsegs;
|
|
|
|
m = malloc(sizeof(*m), M_DEVBUF, M_NOWAIT | M_ZERO);
|
|
if (m == NULL)
|
|
return (NULL);
|
|
|
|
m->bdm_size = size;
|
|
|
|
if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
|
|
BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &m->bdm_map) != 0)
|
|
goto bdmfree;
|
|
|
|
if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &m->bdm_seg, 1,
|
|
&nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO) != 0)
|
|
goto destroy;
|
|
|
|
if (bus_dmamem_map(sc->sc_dmat, &m->bdm_seg, nsegs, size, &m->bdm_kva,
|
|
BUS_DMA_NOWAIT) != 0)
|
|
goto free;
|
|
|
|
if (bus_dmamap_load(sc->sc_dmat, m->bdm_map, m->bdm_kva, size, NULL,
|
|
BUS_DMA_NOWAIT) != 0)
|
|
goto unmap;
|
|
|
|
return (m);
|
|
|
|
unmap:
|
|
bus_dmamem_unmap(sc->sc_dmat, m->bdm_kva, m->bdm_size);
|
|
free:
|
|
bus_dmamem_free(sc->sc_dmat, &m->bdm_seg, 1);
|
|
destroy:
|
|
bus_dmamap_destroy(sc->sc_dmat, m->bdm_map);
|
|
bdmfree:
|
|
free(m, M_DEVBUF, sizeof *m);
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
void
|
|
bnxt_dmamem_free(struct bnxt_softc *sc, struct bnxt_dmamem *m)
|
|
{
|
|
bus_dmamap_unload(sc->sc_dmat, m->bdm_map);
|
|
bus_dmamem_unmap(sc->sc_dmat, m->bdm_kva, m->bdm_size);
|
|
bus_dmamem_free(sc->sc_dmat, &m->bdm_seg, 1);
|
|
bus_dmamap_destroy(sc->sc_dmat, m->bdm_map);
|
|
free(m, M_DEVBUF, sizeof *m);
|
|
}
|
|
|
|
int
|
|
bnxt_match(struct device *parent, void *match, void *aux)
|
|
{
|
|
return (pci_matchbyid(aux, bnxt_devices, nitems(bnxt_devices)));
|
|
}
|
|
|
|
void
|
|
bnxt_attach(struct device *parent, struct device *self, void *aux)
|
|
{
|
|
struct bnxt_softc *sc = (struct bnxt_softc *)self;
|
|
struct ifnet *ifp = &sc->sc_ac.ac_if;
|
|
struct pci_attach_args *pa = aux;
|
|
struct bnxt_cp_ring *cpr;
|
|
pci_intr_handle_t ih;
|
|
const char *intrstr;
|
|
u_int memtype;
|
|
int i;
|
|
|
|
sc->sc_pc = pa->pa_pc;
|
|
sc->sc_tag = pa->pa_tag;
|
|
sc->sc_dmat = pa->pa_dmat;
|
|
|
|
memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, BNXT_HWRM_BAR);
|
|
if (pci_mapreg_map(pa, BNXT_HWRM_BAR, memtype, 0, &sc->sc_hwrm_t,
|
|
&sc->sc_hwrm_h, NULL, &sc->sc_hwrm_s, 0)) {
|
|
printf(": failed to map hwrm\n");
|
|
return;
|
|
}
|
|
|
|
memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, BNXT_DOORBELL_BAR);
|
|
if (pci_mapreg_map(pa, BNXT_DOORBELL_BAR, memtype, 0, &sc->sc_db_t,
|
|
&sc->sc_db_h, NULL, &sc->sc_db_s, 0)) {
|
|
printf(": failed to map doorbell\n");
|
|
goto unmap_1;
|
|
}
|
|
|
|
BNXT_HWRM_LOCK_INIT(sc, DEVNAME(sc));
|
|
sc->sc_cmd_resp = bnxt_dmamem_alloc(sc, PAGE_SIZE);
|
|
if (sc->sc_cmd_resp == NULL) {
|
|
printf(": failed to allocate command response buffer\n");
|
|
goto unmap_2;
|
|
}
|
|
|
|
if (bnxt_hwrm_ver_get(sc) != 0) {
|
|
printf(": failed to query version info\n");
|
|
goto free_resp;
|
|
}
|
|
|
|
if (bnxt_hwrm_nvm_get_dev_info(sc, NULL, NULL, NULL, NULL, NULL, NULL)
|
|
!= 0) {
|
|
printf(": failed to get nvram info\n");
|
|
goto free_resp;
|
|
}
|
|
|
|
if (bnxt_hwrm_func_drv_rgtr(sc) != 0) {
|
|
printf(": failed to register driver with firmware\n");
|
|
goto free_resp;
|
|
}
|
|
|
|
if (bnxt_hwrm_func_rgtr_async_events(sc) != 0) {
|
|
printf(": failed to register async events\n");
|
|
goto free_resp;
|
|
}
|
|
|
|
if (bnxt_hwrm_func_qcaps(sc) != 0) {
|
|
printf(": failed to get queue capabilities\n");
|
|
goto free_resp;
|
|
}
|
|
|
|
/*
|
|
* devices advertise msi support, but there's no way to tell a
|
|
* completion queue to use msi mode, only legacy or msi-x.
|
|
*/
|
|
if (pci_intr_map_msix(pa, 0, &ih) == 0) {
|
|
int nmsix;
|
|
|
|
sc->sc_flags |= BNXT_FLAG_MSIX;
|
|
intrstr = pci_intr_string(sc->sc_pc, ih);
|
|
|
|
nmsix = pci_intr_msix_count(pa);
|
|
if (nmsix > 1) {
|
|
sc->sc_ih = pci_intr_establish(sc->sc_pc, ih,
|
|
IPL_NET | IPL_MPSAFE, bnxt_admin_intr, sc, DEVNAME(sc));
|
|
sc->sc_intrmap = intrmap_create(&sc->sc_dev,
|
|
nmsix - 1, BNXT_MAX_QUEUES, INTRMAP_POWEROF2);
|
|
sc->sc_nqueues = intrmap_count(sc->sc_intrmap);
|
|
KASSERT(sc->sc_nqueues > 0);
|
|
KASSERT(powerof2(sc->sc_nqueues));
|
|
} else {
|
|
sc->sc_ih = pci_intr_establish(sc->sc_pc, ih,
|
|
IPL_NET | IPL_MPSAFE, bnxt_intr, &sc->sc_queues[0],
|
|
DEVNAME(sc));
|
|
sc->sc_nqueues = 1;
|
|
}
|
|
} else if (pci_intr_map(pa, &ih) == 0) {
|
|
intrstr = pci_intr_string(sc->sc_pc, ih);
|
|
sc->sc_ih = pci_intr_establish(sc->sc_pc, ih, IPL_NET | IPL_MPSAFE,
|
|
bnxt_intr, &sc->sc_queues[0], DEVNAME(sc));
|
|
sc->sc_nqueues = 1;
|
|
} else {
|
|
printf(": unable to map interrupt\n");
|
|
goto free_resp;
|
|
}
|
|
if (sc->sc_ih == NULL) {
|
|
printf(": unable to establish interrupt");
|
|
if (intrstr != NULL)
|
|
printf(" at %s", intrstr);
|
|
printf("\n");
|
|
goto deintr;
|
|
}
|
|
printf("%s, %d queues, address %s\n", intrstr, sc->sc_nqueues,
|
|
ether_sprintf(sc->sc_ac.ac_enaddr));
|
|
|
|
if (bnxt_hwrm_func_qcfg(sc) != 0) {
|
|
printf("%s: failed to query function config\n", DEVNAME(sc));
|
|
goto deintr;
|
|
}
|
|
|
|
if (bnxt_hwrm_queue_qportcfg(sc) != 0) {
|
|
printf("%s: failed to query port config\n", DEVNAME(sc));
|
|
goto deintr;
|
|
}
|
|
|
|
if (bnxt_hwrm_func_reset(sc) != 0) {
|
|
printf("%s: reset failed\n", DEVNAME(sc));
|
|
goto deintr;
|
|
}
|
|
|
|
if (sc->sc_intrmap == NULL)
|
|
cpr = &sc->sc_queues[0].q_cp;
|
|
else
|
|
cpr = &sc->sc_cp_ring;
|
|
|
|
cpr->stats_ctx_id = HWRM_NA_SIGNATURE;
|
|
cpr->ring.phys_id = (uint16_t)HWRM_NA_SIGNATURE;
|
|
cpr->softc = sc;
|
|
cpr->ring.id = 0;
|
|
cpr->ring.doorbell = cpr->ring.id * 0x80;
|
|
cpr->ring.ring_size = (PAGE_SIZE * BNXT_CP_PAGES) /
|
|
sizeof(struct cmpl_base);
|
|
cpr->ring_mem = bnxt_dmamem_alloc(sc, PAGE_SIZE *
|
|
BNXT_CP_PAGES);
|
|
if (cpr->ring_mem == NULL) {
|
|
printf("%s: failed to allocate completion queue memory\n",
|
|
DEVNAME(sc));
|
|
goto deintr;
|
|
}
|
|
cpr->ring.vaddr = BNXT_DMA_KVA(cpr->ring_mem);
|
|
cpr->ring.paddr = BNXT_DMA_DVA(cpr->ring_mem);
|
|
cpr->cons = UINT32_MAX;
|
|
cpr->v_bit = 1;
|
|
bnxt_mark_cpr_invalid(cpr);
|
|
if (bnxt_hwrm_ring_alloc(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_L2_CMPL,
|
|
&cpr->ring, (uint16_t)HWRM_NA_SIGNATURE,
|
|
HWRM_NA_SIGNATURE, 1) != 0) {
|
|
printf("%s: failed to allocate completion queue\n",
|
|
DEVNAME(sc));
|
|
goto free_cp_mem;
|
|
}
|
|
if (bnxt_cfg_async_cr(sc, cpr) != 0) {
|
|
printf("%s: failed to set async completion ring\n",
|
|
DEVNAME(sc));
|
|
goto free_cp_mem;
|
|
}
|
|
bnxt_write_cp_doorbell(sc, &cpr->ring, 1);
|
|
|
|
if (bnxt_set_cp_ring_aggint(sc, cpr) != 0) {
|
|
printf("%s: failed to set interrupt aggregation\n",
|
|
DEVNAME(sc));
|
|
goto free_cp_mem;
|
|
}
|
|
|
|
strlcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ);
|
|
ifp->if_softc = sc;
|
|
ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX;
|
|
ifp->if_xflags = IFXF_MPSAFE;
|
|
ifp->if_ioctl = bnxt_ioctl;
|
|
ifp->if_qstart = bnxt_start;
|
|
ifp->if_watchdog = bnxt_watchdog;
|
|
ifp->if_hardmtu = BNXT_MAX_MTU;
|
|
ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_CSUM_IPv4 |
|
|
IFCAP_CSUM_UDPv4 | IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv6 |
|
|
IFCAP_CSUM_TCPv6;
|
|
ifp->if_capabilities |= IFCAP_TSOv4 | IFCAP_TSOv6;
|
|
#if NVLAN > 0
|
|
ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
|
|
#endif
|
|
ifq_init_maxlen(&ifp->if_snd, 1024); /* ? */
|
|
|
|
ifmedia_init(&sc->sc_media, IFM_IMASK, bnxt_media_change,
|
|
bnxt_media_status);
|
|
|
|
if_attach(ifp);
|
|
ether_ifattach(ifp);
|
|
|
|
if_attach_iqueues(ifp, sc->sc_nqueues);
|
|
if_attach_queues(ifp, sc->sc_nqueues);
|
|
for (i = 0; i < sc->sc_nqueues; i++) {
|
|
struct ifiqueue *ifiq = ifp->if_iqs[i];
|
|
struct ifqueue *ifq = ifp->if_ifqs[i];
|
|
struct bnxt_queue *bq = &sc->sc_queues[i];
|
|
struct bnxt_cp_ring *cp = &bq->q_cp;
|
|
struct bnxt_rx_queue *rx = &bq->q_rx;
|
|
struct bnxt_tx_queue *tx = &bq->q_tx;
|
|
|
|
bq->q_index = i;
|
|
bq->q_sc = sc;
|
|
|
|
rx->rx_softc = sc;
|
|
rx->rx_ifiq = ifiq;
|
|
timeout_set(&rx->rx_refill, bnxt_refill, bq);
|
|
ifiq->ifiq_softc = rx;
|
|
|
|
tx->tx_softc = sc;
|
|
tx->tx_ifq = ifq;
|
|
ifq->ifq_softc = tx;
|
|
|
|
if (sc->sc_intrmap != NULL) {
|
|
cp->stats_ctx_id = HWRM_NA_SIGNATURE;
|
|
cp->ring.phys_id = (uint16_t)HWRM_NA_SIGNATURE;
|
|
cp->ring.id = i + 1; /* first cp ring is async only */
|
|
cp->softc = sc;
|
|
cp->ring.doorbell = bq->q_cp.ring.id * 0x80;
|
|
cp->ring.ring_size = (PAGE_SIZE * BNXT_CP_PAGES) /
|
|
sizeof(struct cmpl_base);
|
|
if (pci_intr_map_msix(pa, i + 1, &ih) != 0) {
|
|
printf("%s: unable to map queue interrupt %d\n",
|
|
DEVNAME(sc), i);
|
|
goto intrdisestablish;
|
|
}
|
|
snprintf(bq->q_name, sizeof(bq->q_name), "%s:%d",
|
|
DEVNAME(sc), i);
|
|
bq->q_ihc = pci_intr_establish_cpu(sc->sc_pc, ih,
|
|
IPL_NET | IPL_MPSAFE, intrmap_cpu(sc->sc_intrmap, i),
|
|
bnxt_intr, bq, bq->q_name);
|
|
if (bq->q_ihc == NULL) {
|
|
printf("%s: unable to establish interrupt %d\n",
|
|
DEVNAME(sc), i);
|
|
goto intrdisestablish;
|
|
}
|
|
}
|
|
}
|
|
|
|
bnxt_media_autonegotiate(sc);
|
|
bnxt_hwrm_port_phy_qcfg(sc, NULL);
|
|
return;
|
|
|
|
intrdisestablish:
|
|
for (i = 0; i < sc->sc_nqueues; i++) {
|
|
struct bnxt_queue *bq = &sc->sc_queues[i];
|
|
if (bq->q_ihc == NULL)
|
|
continue;
|
|
pci_intr_disestablish(sc->sc_pc, bq->q_ihc);
|
|
bq->q_ihc = NULL;
|
|
}
|
|
free_cp_mem:
|
|
bnxt_dmamem_free(sc, cpr->ring_mem);
|
|
deintr:
|
|
if (sc->sc_intrmap != NULL) {
|
|
intrmap_destroy(sc->sc_intrmap);
|
|
sc->sc_intrmap = NULL;
|
|
}
|
|
pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
|
|
sc->sc_ih = NULL;
|
|
free_resp:
|
|
bnxt_dmamem_free(sc, sc->sc_cmd_resp);
|
|
unmap_2:
|
|
bus_space_unmap(sc->sc_db_t, sc->sc_db_h, sc->sc_db_s);
|
|
sc->sc_db_s = 0;
|
|
unmap_1:
|
|
bus_space_unmap(sc->sc_hwrm_t, sc->sc_hwrm_h, sc->sc_hwrm_s);
|
|
sc->sc_hwrm_s = 0;
|
|
}
|
|
|
|
void
|
|
bnxt_free_slots(struct bnxt_softc *sc, struct bnxt_slot *slots, int allocated,
|
|
int total)
|
|
{
|
|
struct bnxt_slot *bs;
|
|
|
|
int i = allocated;
|
|
while (i-- > 0) {
|
|
bs = &slots[i];
|
|
bus_dmamap_destroy(sc->sc_dmat, bs->bs_map);
|
|
if (bs->bs_m != NULL)
|
|
m_freem(bs->bs_m);
|
|
}
|
|
free(slots, M_DEVBUF, total * sizeof(*bs));
|
|
}
|
|
|
|
int
|
|
bnxt_set_cp_ring_aggint(struct bnxt_softc *sc, struct bnxt_cp_ring *cpr)
|
|
{
|
|
struct hwrm_ring_cmpl_ring_cfg_aggint_params_input aggint;
|
|
|
|
/*
|
|
* set interrupt aggregation parameters for around 10k interrupts
|
|
* per second. the timers are in units of 80usec, and the counters
|
|
* are based on the minimum rx ring size of 32.
|
|
*/
|
|
memset(&aggint, 0, sizeof(aggint));
|
|
bnxt_hwrm_cmd_hdr_init(sc, &aggint,
|
|
HWRM_RING_CMPL_RING_CFG_AGGINT_PARAMS);
|
|
aggint.ring_id = htole16(cpr->ring.phys_id);
|
|
aggint.num_cmpl_dma_aggr = htole16(32);
|
|
aggint.num_cmpl_dma_aggr_during_int = aggint.num_cmpl_dma_aggr;
|
|
aggint.cmpl_aggr_dma_tmr = htole16((1000000000 / 20000) / 80);
|
|
aggint.cmpl_aggr_dma_tmr_during_int = aggint.cmpl_aggr_dma_tmr;
|
|
aggint.int_lat_tmr_min = htole16((1000000000 / 20000) / 80);
|
|
aggint.int_lat_tmr_max = htole16((1000000000 / 10000) / 80);
|
|
aggint.num_cmpl_aggr_int = htole16(16);
|
|
return (hwrm_send_message(sc, &aggint, sizeof(aggint)));
|
|
}
|
|
|
|
int
|
|
bnxt_queue_up(struct bnxt_softc *sc, struct bnxt_queue *bq)
|
|
{
|
|
struct ifnet *ifp = &sc->sc_ac.ac_if;
|
|
struct bnxt_cp_ring *cp = &bq->q_cp;
|
|
struct bnxt_rx_queue *rx = &bq->q_rx;
|
|
struct bnxt_tx_queue *tx = &bq->q_tx;
|
|
struct bnxt_grp_info *rg = &bq->q_rg;
|
|
struct bnxt_slot *bs;
|
|
int i;
|
|
|
|
tx->tx_ring_mem = bnxt_dmamem_alloc(sc, PAGE_SIZE);
|
|
if (tx->tx_ring_mem == NULL) {
|
|
printf("%s: failed to allocate tx ring %d\n", DEVNAME(sc), bq->q_index);
|
|
return ENOMEM;
|
|
}
|
|
|
|
rx->rx_ring_mem = bnxt_dmamem_alloc(sc, PAGE_SIZE * 2);
|
|
if (rx->rx_ring_mem == NULL) {
|
|
printf("%s: failed to allocate rx ring %d\n", DEVNAME(sc), bq->q_index);
|
|
goto free_tx;
|
|
}
|
|
|
|
/* completion ring is already allocated if we're not using an intrmap */
|
|
if (sc->sc_intrmap != NULL) {
|
|
cp->ring_mem = bnxt_dmamem_alloc(sc, PAGE_SIZE * BNXT_CP_PAGES);
|
|
if (cp->ring_mem == NULL) {
|
|
printf("%s: failed to allocate completion ring %d mem\n",
|
|
DEVNAME(sc), bq->q_index);
|
|
goto free_rx;
|
|
}
|
|
cp->ring.vaddr = BNXT_DMA_KVA(cp->ring_mem);
|
|
cp->ring.paddr = BNXT_DMA_DVA(cp->ring_mem);
|
|
cp->cons = UINT32_MAX;
|
|
cp->v_bit = 1;
|
|
bnxt_mark_cpr_invalid(cp);
|
|
|
|
if (bnxt_hwrm_ring_alloc(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_L2_CMPL,
|
|
&cp->ring, (uint16_t)HWRM_NA_SIGNATURE,
|
|
HWRM_NA_SIGNATURE, 1) != 0) {
|
|
printf("%s: failed to allocate completion queue %d\n",
|
|
DEVNAME(sc), bq->q_index);
|
|
goto free_rx;
|
|
}
|
|
|
|
if (bnxt_set_cp_ring_aggint(sc, cp) != 0) {
|
|
printf("%s: failed to set interrupt %d aggregation\n",
|
|
DEVNAME(sc), bq->q_index);
|
|
goto free_rx;
|
|
}
|
|
bnxt_write_cp_doorbell(sc, &cp->ring, 1);
|
|
}
|
|
|
|
if (bnxt_hwrm_stat_ctx_alloc(sc, &bq->q_cp,
|
|
BNXT_DMA_DVA(sc->sc_stats_ctx_mem) +
|
|
(bq->q_index * sizeof(struct ctx_hw_stats))) != 0) {
|
|
printf("%s: failed to set up stats context\n", DEVNAME(sc));
|
|
goto free_rx;
|
|
}
|
|
|
|
tx->tx_ring.phys_id = (uint16_t)HWRM_NA_SIGNATURE;
|
|
tx->tx_ring.id = BNXT_TX_RING_ID_BASE + bq->q_index;
|
|
tx->tx_ring.doorbell = tx->tx_ring.id * 0x80;
|
|
tx->tx_ring.ring_size = PAGE_SIZE / sizeof(struct tx_bd_short);
|
|
tx->tx_ring.vaddr = BNXT_DMA_KVA(tx->tx_ring_mem);
|
|
tx->tx_ring.paddr = BNXT_DMA_DVA(tx->tx_ring_mem);
|
|
if (bnxt_hwrm_ring_alloc(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_TX,
|
|
&tx->tx_ring, cp->ring.phys_id, HWRM_NA_SIGNATURE, 1) != 0) {
|
|
printf("%s: failed to set up tx ring\n",
|
|
DEVNAME(sc));
|
|
goto dealloc_stats;
|
|
}
|
|
bnxt_write_tx_doorbell(sc, &tx->tx_ring, 0);
|
|
|
|
rx->rx_ring.phys_id = (uint16_t)HWRM_NA_SIGNATURE;
|
|
rx->rx_ring.id = BNXT_RX_RING_ID_BASE + bq->q_index;
|
|
rx->rx_ring.doorbell = rx->rx_ring.id * 0x80;
|
|
rx->rx_ring.ring_size = PAGE_SIZE / sizeof(struct rx_prod_pkt_bd);
|
|
rx->rx_ring.vaddr = BNXT_DMA_KVA(rx->rx_ring_mem);
|
|
rx->rx_ring.paddr = BNXT_DMA_DVA(rx->rx_ring_mem);
|
|
if (bnxt_hwrm_ring_alloc(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_RX,
|
|
&rx->rx_ring, cp->ring.phys_id, HWRM_NA_SIGNATURE, 1) != 0) {
|
|
printf("%s: failed to set up rx ring\n",
|
|
DEVNAME(sc));
|
|
goto dealloc_tx;
|
|
}
|
|
bnxt_write_rx_doorbell(sc, &rx->rx_ring, 0);
|
|
|
|
rx->rx_ag_ring.phys_id = (uint16_t)HWRM_NA_SIGNATURE;
|
|
rx->rx_ag_ring.id = BNXT_AG_RING_ID_BASE + bq->q_index;
|
|
rx->rx_ag_ring.doorbell = rx->rx_ag_ring.id * 0x80;
|
|
rx->rx_ag_ring.ring_size = PAGE_SIZE / sizeof(struct rx_prod_pkt_bd);
|
|
rx->rx_ag_ring.vaddr = BNXT_DMA_KVA(rx->rx_ring_mem) + PAGE_SIZE;
|
|
rx->rx_ag_ring.paddr = BNXT_DMA_DVA(rx->rx_ring_mem) + PAGE_SIZE;
|
|
if (bnxt_hwrm_ring_alloc(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_RX,
|
|
&rx->rx_ag_ring, cp->ring.phys_id, HWRM_NA_SIGNATURE, 1) != 0) {
|
|
printf("%s: failed to set up rx ag ring\n",
|
|
DEVNAME(sc));
|
|
goto dealloc_rx;
|
|
}
|
|
bnxt_write_rx_doorbell(sc, &rx->rx_ag_ring, 0);
|
|
|
|
rg->grp_id = HWRM_NA_SIGNATURE;
|
|
rg->stats_ctx = cp->stats_ctx_id;
|
|
rg->rx_ring_id = rx->rx_ring.phys_id;
|
|
rg->ag_ring_id = rx->rx_ag_ring.phys_id;
|
|
rg->cp_ring_id = cp->ring.phys_id;
|
|
if (bnxt_hwrm_ring_grp_alloc(sc, rg) != 0) {
|
|
printf("%s: failed to allocate ring group\n",
|
|
DEVNAME(sc));
|
|
goto dealloc_ag;
|
|
}
|
|
|
|
rx->rx_slots = mallocarray(sizeof(*bs), rx->rx_ring.ring_size,
|
|
M_DEVBUF, M_WAITOK | M_ZERO);
|
|
if (rx->rx_slots == NULL) {
|
|
printf("%s: failed to allocate rx slots\n", DEVNAME(sc));
|
|
goto dealloc_ring_group;
|
|
}
|
|
|
|
for (i = 0; i < rx->rx_ring.ring_size; i++) {
|
|
bs = &rx->rx_slots[i];
|
|
if (bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES, 0,
|
|
BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &bs->bs_map) != 0) {
|
|
printf("%s: failed to allocate rx dma maps\n",
|
|
DEVNAME(sc));
|
|
goto destroy_rx_slots;
|
|
}
|
|
}
|
|
|
|
rx->rx_ag_slots = mallocarray(sizeof(*bs), rx->rx_ag_ring.ring_size,
|
|
M_DEVBUF, M_WAITOK | M_ZERO);
|
|
if (rx->rx_ag_slots == NULL) {
|
|
printf("%s: failed to allocate rx ag slots\n", DEVNAME(sc));
|
|
goto destroy_rx_slots;
|
|
}
|
|
|
|
for (i = 0; i < rx->rx_ag_ring.ring_size; i++) {
|
|
bs = &rx->rx_ag_slots[i];
|
|
if (bus_dmamap_create(sc->sc_dmat, BNXT_AG_BUFFER_SIZE, 1,
|
|
BNXT_AG_BUFFER_SIZE, 0, BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
|
|
&bs->bs_map) != 0) {
|
|
printf("%s: failed to allocate rx ag dma maps\n",
|
|
DEVNAME(sc));
|
|
goto destroy_rx_ag_slots;
|
|
}
|
|
}
|
|
|
|
tx->tx_slots = mallocarray(sizeof(*bs), tx->tx_ring.ring_size,
|
|
M_DEVBUF, M_WAITOK | M_ZERO);
|
|
if (tx->tx_slots == NULL) {
|
|
printf("%s: failed to allocate tx slots\n", DEVNAME(sc));
|
|
goto destroy_rx_ag_slots;
|
|
}
|
|
|
|
for (i = 0; i < tx->tx_ring.ring_size; i++) {
|
|
bs = &tx->tx_slots[i];
|
|
if (bus_dmamap_create(sc->sc_dmat, MAXMCLBYTES, BNXT_MAX_TX_SEGS,
|
|
BNXT_MAX_MTU, 0, BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
|
|
&bs->bs_map) != 0) {
|
|
printf("%s: failed to allocate tx dma maps\n",
|
|
DEVNAME(sc));
|
|
goto destroy_tx_slots;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* initially, the rx ring must be filled at least some distance beyond
|
|
* the current consumer index, as it looks like the firmware assumes the
|
|
* ring is full on creation, but doesn't prefetch the whole thing.
|
|
* once the whole ring has been used once, we should be able to back off
|
|
* to 2 or so slots, but we currently don't have a way of doing that.
|
|
*/
|
|
if_rxr_init(&rx->rxr[0], 32, rx->rx_ring.ring_size - 1);
|
|
if_rxr_init(&rx->rxr[1], 32, rx->rx_ag_ring.ring_size - 1);
|
|
rx->rx_prod = 0;
|
|
rx->rx_cons = 0;
|
|
rx->rx_ag_prod = 0;
|
|
rx->rx_ag_cons = 0;
|
|
bnxt_rx_fill(bq);
|
|
|
|
tx->tx_cons = 0;
|
|
tx->tx_prod = 0;
|
|
tx->tx_ring_cons = 0;
|
|
tx->tx_ring_prod = 0;
|
|
ifq_clr_oactive(ifp->if_ifqs[bq->q_index]);
|
|
ifq_restart(ifp->if_ifqs[bq->q_index]);
|
|
return 0;
|
|
|
|
destroy_tx_slots:
|
|
bnxt_free_slots(sc, tx->tx_slots, i, tx->tx_ring.ring_size);
|
|
tx->tx_slots = NULL;
|
|
|
|
i = rx->rx_ag_ring.ring_size;
|
|
destroy_rx_ag_slots:
|
|
bnxt_free_slots(sc, rx->rx_ag_slots, i, rx->rx_ag_ring.ring_size);
|
|
rx->rx_ag_slots = NULL;
|
|
|
|
i = rx->rx_ring.ring_size;
|
|
destroy_rx_slots:
|
|
bnxt_free_slots(sc, rx->rx_slots, i, rx->rx_ring.ring_size);
|
|
rx->rx_slots = NULL;
|
|
dealloc_ring_group:
|
|
bnxt_hwrm_ring_grp_free(sc, &bq->q_rg);
|
|
dealloc_ag:
|
|
bnxt_hwrm_ring_free(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_RX,
|
|
&rx->rx_ag_ring);
|
|
dealloc_tx:
|
|
bnxt_hwrm_ring_free(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_TX,
|
|
&tx->tx_ring);
|
|
dealloc_rx:
|
|
bnxt_hwrm_ring_free(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_RX,
|
|
&rx->rx_ring);
|
|
dealloc_stats:
|
|
bnxt_hwrm_stat_ctx_free(sc, cp);
|
|
free_rx:
|
|
bnxt_dmamem_free(sc, rx->rx_ring_mem);
|
|
rx->rx_ring_mem = NULL;
|
|
free_tx:
|
|
bnxt_dmamem_free(sc, tx->tx_ring_mem);
|
|
tx->tx_ring_mem = NULL;
|
|
return ENOMEM;
|
|
}
|
|
|
|
void
|
|
bnxt_queue_down(struct bnxt_softc *sc, struct bnxt_queue *bq)
|
|
{
|
|
struct bnxt_cp_ring *cp = &bq->q_cp;
|
|
struct bnxt_rx_queue *rx = &bq->q_rx;
|
|
struct bnxt_tx_queue *tx = &bq->q_tx;
|
|
|
|
bnxt_free_slots(sc, tx->tx_slots, tx->tx_ring.ring_size,
|
|
tx->tx_ring.ring_size);
|
|
tx->tx_slots = NULL;
|
|
|
|
bnxt_free_slots(sc, rx->rx_ag_slots, rx->rx_ag_ring.ring_size,
|
|
rx->rx_ag_ring.ring_size);
|
|
rx->rx_ag_slots = NULL;
|
|
|
|
bnxt_free_slots(sc, rx->rx_slots, rx->rx_ring.ring_size,
|
|
rx->rx_ring.ring_size);
|
|
rx->rx_slots = NULL;
|
|
|
|
bnxt_hwrm_ring_grp_free(sc, &bq->q_rg);
|
|
bnxt_hwrm_stat_ctx_free(sc, &bq->q_cp);
|
|
|
|
/* may need to wait for 500ms here before we can free the rings */
|
|
|
|
bnxt_hwrm_ring_free(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_TX,
|
|
&tx->tx_ring);
|
|
bnxt_hwrm_ring_free(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_RX,
|
|
&rx->rx_ag_ring);
|
|
bnxt_hwrm_ring_free(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_RX,
|
|
&rx->rx_ring);
|
|
|
|
/* if no intrmap, leave cp ring in place for async events */
|
|
if (sc->sc_intrmap != NULL) {
|
|
bnxt_hwrm_ring_free(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_L2_CMPL,
|
|
&cp->ring);
|
|
|
|
bnxt_dmamem_free(sc, cp->ring_mem);
|
|
cp->ring_mem = NULL;
|
|
}
|
|
|
|
bnxt_dmamem_free(sc, rx->rx_ring_mem);
|
|
rx->rx_ring_mem = NULL;
|
|
|
|
bnxt_dmamem_free(sc, tx->tx_ring_mem);
|
|
tx->tx_ring_mem = NULL;
|
|
}
|
|
|
|
void
|
|
bnxt_up(struct bnxt_softc *sc)
|
|
{
|
|
struct ifnet *ifp = &sc->sc_ac.ac_if;
|
|
int i;
|
|
|
|
sc->sc_stats_ctx_mem = bnxt_dmamem_alloc(sc,
|
|
sizeof(struct ctx_hw_stats) * sc->sc_nqueues);
|
|
if (sc->sc_stats_ctx_mem == NULL) {
|
|
printf("%s: failed to allocate stats contexts\n", DEVNAME(sc));
|
|
return;
|
|
}
|
|
|
|
sc->sc_rx_cfg = bnxt_dmamem_alloc(sc, PAGE_SIZE * 2);
|
|
if (sc->sc_rx_cfg == NULL) {
|
|
printf("%s: failed to allocate rx config buffer\n",
|
|
DEVNAME(sc));
|
|
goto free_stats;
|
|
}
|
|
|
|
for (i = 0; i < sc->sc_nqueues; i++) {
|
|
if (bnxt_queue_up(sc, &sc->sc_queues[i]) != 0) {
|
|
goto down_queues;
|
|
}
|
|
}
|
|
|
|
sc->sc_vnic.rss_id = (uint16_t)HWRM_NA_SIGNATURE;
|
|
if (bnxt_hwrm_vnic_ctx_alloc(sc, &sc->sc_vnic.rss_id) != 0) {
|
|
printf("%s: failed to allocate vnic rss context\n",
|
|
DEVNAME(sc));
|
|
goto down_all_queues;
|
|
}
|
|
|
|
sc->sc_vnic.id = (uint16_t)HWRM_NA_SIGNATURE;
|
|
sc->sc_vnic.def_ring_grp = sc->sc_queues[0].q_rg.grp_id;
|
|
sc->sc_vnic.mru = BNXT_MAX_MTU;
|
|
sc->sc_vnic.cos_rule = (uint16_t)HWRM_NA_SIGNATURE;
|
|
sc->sc_vnic.lb_rule = (uint16_t)HWRM_NA_SIGNATURE;
|
|
sc->sc_vnic.flags = BNXT_VNIC_FLAG_DEFAULT |
|
|
BNXT_VNIC_FLAG_VLAN_STRIP;
|
|
if (bnxt_hwrm_vnic_alloc(sc, &sc->sc_vnic) != 0) {
|
|
printf("%s: failed to allocate vnic\n", DEVNAME(sc));
|
|
goto dealloc_vnic_ctx;
|
|
}
|
|
|
|
if (bnxt_hwrm_vnic_cfg(sc, &sc->sc_vnic) != 0) {
|
|
printf("%s: failed to configure vnic\n", DEVNAME(sc));
|
|
goto dealloc_vnic;
|
|
}
|
|
|
|
if (bnxt_hwrm_vnic_cfg_placement(sc, &sc->sc_vnic) != 0) {
|
|
printf("%s: failed to configure vnic placement mode\n",
|
|
DEVNAME(sc));
|
|
goto dealloc_vnic;
|
|
}
|
|
|
|
sc->sc_vnic.filter_id = -1;
|
|
if (bnxt_hwrm_set_filter(sc, &sc->sc_vnic) != 0) {
|
|
printf("%s: failed to set vnic filter\n", DEVNAME(sc));
|
|
goto dealloc_vnic;
|
|
}
|
|
|
|
if (sc->sc_nqueues > 1) {
|
|
uint16_t *rss_table = (BNXT_DMA_KVA(sc->sc_rx_cfg) + PAGE_SIZE);
|
|
uint8_t *hash_key = (uint8_t *)(rss_table + HW_HASH_INDEX_SIZE);
|
|
|
|
for (i = 0; i < HW_HASH_INDEX_SIZE; i++) {
|
|
struct bnxt_queue *bq;
|
|
|
|
bq = &sc->sc_queues[i % sc->sc_nqueues];
|
|
rss_table[i] = htole16(bq->q_rg.grp_id);
|
|
}
|
|
stoeplitz_to_key(hash_key, HW_HASH_KEY_SIZE);
|
|
|
|
if (bnxt_hwrm_vnic_rss_cfg(sc, &sc->sc_vnic,
|
|
HWRM_VNIC_RSS_CFG_INPUT_HASH_TYPE_IPV4 |
|
|
HWRM_VNIC_RSS_CFG_INPUT_HASH_TYPE_TCP_IPV4 |
|
|
HWRM_VNIC_RSS_CFG_INPUT_HASH_TYPE_IPV6 |
|
|
HWRM_VNIC_RSS_CFG_INPUT_HASH_TYPE_TCP_IPV6,
|
|
BNXT_DMA_DVA(sc->sc_rx_cfg) + PAGE_SIZE,
|
|
BNXT_DMA_DVA(sc->sc_rx_cfg) + PAGE_SIZE +
|
|
(HW_HASH_INDEX_SIZE * sizeof(uint16_t))) != 0) {
|
|
printf("%s: failed to set RSS config\n", DEVNAME(sc));
|
|
goto dealloc_vnic;
|
|
}
|
|
}
|
|
|
|
bnxt_iff(sc);
|
|
SET(ifp->if_flags, IFF_RUNNING);
|
|
|
|
return;
|
|
|
|
dealloc_vnic:
|
|
bnxt_hwrm_vnic_free(sc, &sc->sc_vnic);
|
|
dealloc_vnic_ctx:
|
|
bnxt_hwrm_vnic_ctx_free(sc, &sc->sc_vnic.rss_id);
|
|
|
|
down_all_queues:
|
|
i = sc->sc_nqueues;
|
|
down_queues:
|
|
while (i-- > 0)
|
|
bnxt_queue_down(sc, &sc->sc_queues[i]);
|
|
|
|
bnxt_dmamem_free(sc, sc->sc_rx_cfg);
|
|
sc->sc_rx_cfg = NULL;
|
|
free_stats:
|
|
bnxt_dmamem_free(sc, sc->sc_stats_ctx_mem);
|
|
sc->sc_stats_ctx_mem = NULL;
|
|
}
|
|
|
|
void
|
|
bnxt_down(struct bnxt_softc *sc)
|
|
{
|
|
struct ifnet *ifp = &sc->sc_ac.ac_if;
|
|
int i;
|
|
|
|
CLR(ifp->if_flags, IFF_RUNNING);
|
|
|
|
intr_barrier(sc->sc_ih);
|
|
|
|
for (i = 0; i < sc->sc_nqueues; i++) {
|
|
ifq_clr_oactive(ifp->if_ifqs[i]);
|
|
ifq_barrier(ifp->if_ifqs[i]);
|
|
|
|
timeout_del_barrier(&sc->sc_queues[i].q_rx.rx_refill);
|
|
|
|
if (sc->sc_intrmap != NULL)
|
|
intr_barrier(sc->sc_queues[i].q_ihc);
|
|
}
|
|
|
|
bnxt_hwrm_free_filter(sc, &sc->sc_vnic);
|
|
bnxt_hwrm_vnic_free(sc, &sc->sc_vnic);
|
|
bnxt_hwrm_vnic_ctx_free(sc, &sc->sc_vnic.rss_id);
|
|
|
|
for (i = 0; i < sc->sc_nqueues; i++)
|
|
bnxt_queue_down(sc, &sc->sc_queues[i]);
|
|
|
|
bnxt_dmamem_free(sc, sc->sc_rx_cfg);
|
|
sc->sc_rx_cfg = NULL;
|
|
|
|
bnxt_dmamem_free(sc, sc->sc_stats_ctx_mem);
|
|
sc->sc_stats_ctx_mem = NULL;
|
|
}
|
|
|
|
void
|
|
bnxt_iff(struct bnxt_softc *sc)
|
|
{
|
|
struct ifnet *ifp = &sc->sc_ac.ac_if;
|
|
struct ether_multi *enm;
|
|
struct ether_multistep step;
|
|
char *mc_list;
|
|
uint32_t rx_mask, mc_count;
|
|
|
|
rx_mask = HWRM_CFA_L2_SET_RX_MASK_INPUT_MASK_BCAST
|
|
| HWRM_CFA_L2_SET_RX_MASK_INPUT_MASK_MCAST
|
|
| HWRM_CFA_L2_SET_RX_MASK_INPUT_MASK_ANYVLAN_NONVLAN;
|
|
|
|
mc_list = BNXT_DMA_KVA(sc->sc_rx_cfg);
|
|
mc_count = 0;
|
|
|
|
if (ifp->if_flags & IFF_PROMISC) {
|
|
SET(ifp->if_flags, IFF_ALLMULTI);
|
|
rx_mask |= HWRM_CFA_L2_SET_RX_MASK_INPUT_MASK_PROMISCUOUS;
|
|
} else if ((sc->sc_ac.ac_multirangecnt > 0) ||
|
|
(sc->sc_ac.ac_multicnt > (PAGE_SIZE / ETHER_ADDR_LEN))) {
|
|
SET(ifp->if_flags, IFF_ALLMULTI);
|
|
rx_mask |= HWRM_CFA_L2_SET_RX_MASK_INPUT_MASK_ALL_MCAST;
|
|
} else {
|
|
CLR(ifp->if_flags, IFF_ALLMULTI);
|
|
ETHER_FIRST_MULTI(step, &sc->sc_ac, enm);
|
|
while (enm != NULL) {
|
|
memcpy(mc_list, enm->enm_addrlo, ETHER_ADDR_LEN);
|
|
mc_list += ETHER_ADDR_LEN;
|
|
mc_count++;
|
|
|
|
ETHER_NEXT_MULTI(step, enm);
|
|
}
|
|
}
|
|
|
|
bnxt_hwrm_cfa_l2_set_rx_mask(sc, sc->sc_vnic.id, rx_mask,
|
|
BNXT_DMA_DVA(sc->sc_rx_cfg), mc_count);
|
|
}
|
|
|
|
int
|
|
bnxt_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
|
|
{
|
|
struct bnxt_softc *sc = (struct bnxt_softc *)ifp->if_softc;
|
|
struct ifreq *ifr = (struct ifreq *)data;
|
|
int s, error = 0;
|
|
|
|
s = splnet();
|
|
switch (cmd) {
|
|
case SIOCSIFADDR:
|
|
ifp->if_flags |= IFF_UP;
|
|
/* FALLTHROUGH */
|
|
|
|
case SIOCSIFFLAGS:
|
|
if (ISSET(ifp->if_flags, IFF_UP)) {
|
|
if (ISSET(ifp->if_flags, IFF_RUNNING))
|
|
error = ENETRESET;
|
|
else
|
|
bnxt_up(sc);
|
|
} else {
|
|
if (ISSET(ifp->if_flags, IFF_RUNNING))
|
|
bnxt_down(sc);
|
|
}
|
|
break;
|
|
|
|
case SIOCGIFMEDIA:
|
|
case SIOCSIFMEDIA:
|
|
error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
|
|
break;
|
|
|
|
case SIOCGIFRXR:
|
|
error = bnxt_rxrinfo(sc, (struct if_rxrinfo *)ifr->ifr_data);
|
|
break;
|
|
|
|
case SIOCGIFSFFPAGE:
|
|
error = bnxt_get_sffpage(sc, (struct if_sffpage *)data);
|
|
break;
|
|
|
|
default:
|
|
error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
|
|
}
|
|
|
|
if (error == ENETRESET) {
|
|
if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
|
|
(IFF_UP | IFF_RUNNING))
|
|
bnxt_iff(sc);
|
|
error = 0;
|
|
}
|
|
|
|
splx(s);
|
|
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
bnxt_rxrinfo(struct bnxt_softc *sc, struct if_rxrinfo *ifri)
|
|
{
|
|
struct if_rxring_info *ifr;
|
|
int i;
|
|
int error;
|
|
|
|
ifr = mallocarray(sc->sc_nqueues * 2, sizeof(*ifr), M_TEMP,
|
|
M_WAITOK | M_ZERO | M_CANFAIL);
|
|
if (ifr == NULL)
|
|
return (ENOMEM);
|
|
|
|
for (i = 0; i < sc->sc_nqueues; i++) {
|
|
ifr[(i * 2)].ifr_size = MCLBYTES;
|
|
ifr[(i * 2)].ifr_info = sc->sc_queues[i].q_rx.rxr[0];
|
|
|
|
ifr[(i * 2) + 1].ifr_size = BNXT_AG_BUFFER_SIZE;
|
|
ifr[(i * 2) + 1].ifr_info = sc->sc_queues[i].q_rx.rxr[1];
|
|
}
|
|
|
|
error = if_rxr_info_ioctl(ifri, sc->sc_nqueues * 2, ifr);
|
|
free(ifr, M_TEMP, sc->sc_nqueues * 2 * sizeof(*ifr));
|
|
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
bnxt_load_mbuf(struct bnxt_softc *sc, struct bnxt_slot *bs, struct mbuf *m)
|
|
{
|
|
switch (bus_dmamap_load_mbuf(sc->sc_dmat, bs->bs_map, m,
|
|
BUS_DMA_STREAMING | BUS_DMA_NOWAIT)) {
|
|
case 0:
|
|
break;
|
|
|
|
case EFBIG:
|
|
if (m_defrag(m, M_DONTWAIT) == 0 &&
|
|
bus_dmamap_load_mbuf(sc->sc_dmat, bs->bs_map, m,
|
|
BUS_DMA_STREAMING | BUS_DMA_NOWAIT) == 0)
|
|
break;
|
|
|
|
default:
|
|
return (1);
|
|
}
|
|
|
|
bs->bs_m = m;
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
bnxt_start(struct ifqueue *ifq)
|
|
{
|
|
struct ifnet *ifp = ifq->ifq_if;
|
|
struct tx_bd_short *txring;
|
|
struct tx_bd_long_hi *txhi;
|
|
struct bnxt_tx_queue *tx = ifq->ifq_softc;
|
|
struct bnxt_softc *sc = tx->tx_softc;
|
|
struct bnxt_slot *bs;
|
|
struct ether_extracted ext;
|
|
bus_dmamap_t map;
|
|
struct mbuf *m;
|
|
u_int idx, free, used, laststart;
|
|
uint16_t txflags, lflags;
|
|
int i, slen;
|
|
|
|
txring = (struct tx_bd_short *)BNXT_DMA_KVA(tx->tx_ring_mem);
|
|
|
|
idx = tx->tx_ring_prod;
|
|
free = tx->tx_ring_cons;
|
|
if (free <= idx)
|
|
free += tx->tx_ring.ring_size;
|
|
free -= idx;
|
|
|
|
used = 0;
|
|
|
|
for (;;) {
|
|
/* +1 for tx_bd_long_hi */
|
|
if (used + BNXT_MAX_TX_SEGS + 1 > free) {
|
|
ifq_set_oactive(ifq);
|
|
break;
|
|
}
|
|
|
|
m = ifq_dequeue(ifq);
|
|
if (m == NULL)
|
|
break;
|
|
|
|
bs = &tx->tx_slots[tx->tx_prod];
|
|
if (bnxt_load_mbuf(sc, bs, m) != 0) {
|
|
m_freem(m);
|
|
ifp->if_oerrors++;
|
|
continue;
|
|
}
|
|
|
|
#if NBPFILTER > 0
|
|
if (ifp->if_bpf)
|
|
bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
|
|
#endif
|
|
map = bs->bs_map;
|
|
bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
|
|
BUS_DMASYNC_PREWRITE);
|
|
used += BNXT_TX_SLOTS(bs);
|
|
|
|
/* first segment */
|
|
laststart = idx;
|
|
txring[idx].len = htole16(map->dm_segs[0].ds_len);
|
|
txring[idx].opaque = tx->tx_prod;
|
|
txring[idx].addr = htole64(map->dm_segs[0].ds_addr);
|
|
if (m->m_pkthdr.csum_flags & M_TCP_TSO)
|
|
slen = m->m_pkthdr.ph_mss;
|
|
else
|
|
slen = map->dm_mapsize;
|
|
|
|
if (slen < 512)
|
|
txflags = TX_BD_LONG_FLAGS_LHINT_LT512;
|
|
else if (slen < 1024)
|
|
txflags = TX_BD_LONG_FLAGS_LHINT_LT1K;
|
|
else if (slen < 2048)
|
|
txflags = TX_BD_LONG_FLAGS_LHINT_LT2K;
|
|
else
|
|
txflags = TX_BD_LONG_FLAGS_LHINT_GTE2K;
|
|
txflags |= TX_BD_LONG_TYPE_TX_BD_LONG |
|
|
TX_BD_LONG_FLAGS_NO_CMPL;
|
|
txflags |= (BNXT_TX_SLOTS(bs) << TX_BD_LONG_FLAGS_BD_CNT_SFT) &
|
|
TX_BD_LONG_FLAGS_BD_CNT_MASK;
|
|
if (map->dm_nsegs == 1)
|
|
txflags |= TX_BD_SHORT_FLAGS_PACKET_END;
|
|
txring[idx].flags_type = htole16(txflags);
|
|
|
|
idx++;
|
|
if (idx == tx->tx_ring.ring_size)
|
|
idx = 0;
|
|
|
|
/* long tx descriptor */
|
|
txhi = (struct tx_bd_long_hi *)&txring[idx];
|
|
memset(txhi, 0, sizeof(*txhi));
|
|
|
|
lflags = 0;
|
|
if (m->m_pkthdr.csum_flags & M_TCP_TSO) {
|
|
uint16_t hdrsize;
|
|
uint32_t outlen;
|
|
uint32_t paylen;
|
|
|
|
ether_extract_headers(m, &ext);
|
|
if (ext.tcp) {
|
|
lflags |= TX_BD_LONG_LFLAGS_LSO;
|
|
hdrsize = sizeof(*ext.eh);
|
|
if (ext.ip4)
|
|
hdrsize += ext.ip4->ip_hl << 2;
|
|
else if (ext.ip6)
|
|
hdrsize += sizeof(*ext.ip6);
|
|
else
|
|
tcpstat_inc(tcps_outbadtso);
|
|
|
|
hdrsize += ext.tcp->th_off << 2;
|
|
txhi->hdr_size = htole16(hdrsize / 2);
|
|
|
|
outlen = m->m_pkthdr.ph_mss;
|
|
txhi->mss = htole32(outlen);
|
|
|
|
paylen = m->m_pkthdr.len - hdrsize;
|
|
tcpstat_add(tcps_outpkttso,
|
|
(paylen + outlen + 1) / outlen);
|
|
} else {
|
|
tcpstat_inc(tcps_outbadtso);
|
|
}
|
|
} else {
|
|
if (m->m_pkthdr.csum_flags & (M_UDP_CSUM_OUT |
|
|
M_TCP_CSUM_OUT))
|
|
lflags |= TX_BD_LONG_LFLAGS_TCP_UDP_CHKSUM;
|
|
if (m->m_pkthdr.csum_flags & M_IPV4_CSUM_OUT)
|
|
lflags |= TX_BD_LONG_LFLAGS_IP_CHKSUM;
|
|
}
|
|
txhi->lflags = htole16(lflags);
|
|
|
|
#if NVLAN > 0
|
|
if (m->m_flags & M_VLANTAG) {
|
|
txhi->cfa_meta = htole32(m->m_pkthdr.ether_vtag |
|
|
TX_BD_LONG_CFA_META_VLAN_TPID_TPID8100 |
|
|
TX_BD_LONG_CFA_META_KEY_VLAN_TAG);
|
|
}
|
|
#endif
|
|
|
|
idx++;
|
|
if (idx == tx->tx_ring.ring_size)
|
|
idx = 0;
|
|
|
|
/* remaining segments */
|
|
txflags = TX_BD_SHORT_TYPE_TX_BD_SHORT;
|
|
for (i = 1; i < map->dm_nsegs; i++) {
|
|
if (i == map->dm_nsegs - 1)
|
|
txflags |= TX_BD_SHORT_FLAGS_PACKET_END;
|
|
txring[idx].flags_type = htole16(txflags);
|
|
|
|
txring[idx].len =
|
|
htole16(bs->bs_map->dm_segs[i].ds_len);
|
|
txring[idx].opaque = tx->tx_prod;
|
|
txring[idx].addr =
|
|
htole64(bs->bs_map->dm_segs[i].ds_addr);
|
|
|
|
idx++;
|
|
if (idx == tx->tx_ring.ring_size)
|
|
idx = 0;
|
|
}
|
|
|
|
if (++tx->tx_prod >= tx->tx_ring.ring_size)
|
|
tx->tx_prod = 0;
|
|
}
|
|
|
|
/* unset NO_CMPL on the first bd of the last packet */
|
|
if (used != 0) {
|
|
txring[laststart].flags_type &=
|
|
~htole16(TX_BD_SHORT_FLAGS_NO_CMPL);
|
|
}
|
|
|
|
bnxt_write_tx_doorbell(sc, &tx->tx_ring, idx);
|
|
tx->tx_ring_prod = idx;
|
|
}
|
|
|
|
void
|
|
bnxt_handle_async_event(struct bnxt_softc *sc, struct cmpl_base *cmpl)
|
|
{
|
|
struct hwrm_async_event_cmpl *ae = (struct hwrm_async_event_cmpl *)cmpl;
|
|
uint16_t type = le16toh(ae->event_id);
|
|
|
|
switch (type) {
|
|
case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE:
|
|
case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CHANGE:
|
|
case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CFG_CHANGE:
|
|
bnxt_hwrm_port_phy_qcfg(sc, NULL);
|
|
break;
|
|
|
|
default:
|
|
printf("%s: unexpected async event %x\n", DEVNAME(sc), type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
struct cmpl_base *
|
|
bnxt_cpr_next_cmpl(struct bnxt_softc *sc, struct bnxt_cp_ring *cpr)
|
|
{
|
|
struct cmpl_base *cmpl;
|
|
uint32_t cons;
|
|
int v_bit;
|
|
|
|
cons = cpr->cons + 1;
|
|
v_bit = cpr->v_bit;
|
|
if (cons == cpr->ring.ring_size) {
|
|
cons = 0;
|
|
v_bit = !v_bit;
|
|
}
|
|
cmpl = &((struct cmpl_base *)cpr->ring.vaddr)[cons];
|
|
|
|
if ((!!(cmpl->info3_v & htole32(CMPL_BASE_V))) != (!!v_bit))
|
|
return (NULL);
|
|
|
|
cpr->cons = cons;
|
|
cpr->v_bit = v_bit;
|
|
return (cmpl);
|
|
}
|
|
|
|
void
|
|
bnxt_cpr_commit(struct bnxt_softc *sc, struct bnxt_cp_ring *cpr)
|
|
{
|
|
cpr->commit_cons = cpr->cons;
|
|
cpr->commit_v_bit = cpr->v_bit;
|
|
}
|
|
|
|
void
|
|
bnxt_cpr_rollback(struct bnxt_softc *sc, struct bnxt_cp_ring *cpr)
|
|
{
|
|
cpr->cons = cpr->commit_cons;
|
|
cpr->v_bit = cpr->commit_v_bit;
|
|
}
|
|
|
|
int
|
|
bnxt_admin_intr(void *xsc)
|
|
{
|
|
struct bnxt_softc *sc = (struct bnxt_softc *)xsc;
|
|
struct bnxt_cp_ring *cpr = &sc->sc_cp_ring;
|
|
struct cmpl_base *cmpl;
|
|
uint16_t type;
|
|
|
|
bnxt_write_cp_doorbell(sc, &cpr->ring, 0);
|
|
cmpl = bnxt_cpr_next_cmpl(sc, cpr);
|
|
while (cmpl != NULL) {
|
|
type = le16toh(cmpl->type) & CMPL_BASE_TYPE_MASK;
|
|
switch (type) {
|
|
case CMPL_BASE_TYPE_HWRM_ASYNC_EVENT:
|
|
bnxt_handle_async_event(sc, cmpl);
|
|
break;
|
|
default:
|
|
printf("%s: unexpected completion type %u\n",
|
|
DEVNAME(sc), type);
|
|
}
|
|
|
|
bnxt_cpr_commit(sc, cpr);
|
|
cmpl = bnxt_cpr_next_cmpl(sc, cpr);
|
|
}
|
|
|
|
bnxt_write_cp_doorbell_index(sc, &cpr->ring,
|
|
(cpr->commit_cons+1) % cpr->ring.ring_size, 1);
|
|
return (1);
|
|
}
|
|
|
|
int
|
|
bnxt_intr(void *xq)
|
|
{
|
|
struct bnxt_queue *q = (struct bnxt_queue *)xq;
|
|
struct bnxt_softc *sc = q->q_sc;
|
|
struct ifnet *ifp = &sc->sc_ac.ac_if;
|
|
struct bnxt_cp_ring *cpr = &q->q_cp;
|
|
struct bnxt_rx_queue *rx = &q->q_rx;
|
|
struct bnxt_tx_queue *tx = &q->q_tx;
|
|
struct cmpl_base *cmpl;
|
|
struct mbuf_list ml = MBUF_LIST_INITIALIZER();
|
|
uint16_t type;
|
|
int rxfree, txfree, agfree, rv, rollback;
|
|
|
|
bnxt_write_cp_doorbell(sc, &cpr->ring, 0);
|
|
rxfree = 0;
|
|
txfree = 0;
|
|
agfree = 0;
|
|
rv = -1;
|
|
cmpl = bnxt_cpr_next_cmpl(sc, cpr);
|
|
while (cmpl != NULL) {
|
|
type = le16toh(cmpl->type) & CMPL_BASE_TYPE_MASK;
|
|
rollback = 0;
|
|
switch (type) {
|
|
case CMPL_BASE_TYPE_HWRM_ASYNC_EVENT:
|
|
bnxt_handle_async_event(sc, cmpl);
|
|
break;
|
|
case CMPL_BASE_TYPE_RX_L2:
|
|
if (ISSET(ifp->if_flags, IFF_RUNNING))
|
|
rollback = bnxt_rx(sc, rx, cpr, &ml, &rxfree,
|
|
&agfree, cmpl);
|
|
break;
|
|
case CMPL_BASE_TYPE_TX_L2:
|
|
if (ISSET(ifp->if_flags, IFF_RUNNING))
|
|
bnxt_txeof(sc, tx, &txfree, cmpl);
|
|
break;
|
|
default:
|
|
printf("%s: unexpected completion type %u\n",
|
|
DEVNAME(sc), type);
|
|
}
|
|
|
|
if (rollback) {
|
|
bnxt_cpr_rollback(sc, cpr);
|
|
break;
|
|
}
|
|
rv = 1;
|
|
bnxt_cpr_commit(sc, cpr);
|
|
cmpl = bnxt_cpr_next_cmpl(sc, cpr);
|
|
}
|
|
|
|
/*
|
|
* comments in bnxtreg.h suggest we should be writing cpr->cons here,
|
|
* but writing cpr->cons + 1 makes it stop interrupting.
|
|
*/
|
|
bnxt_write_cp_doorbell_index(sc, &cpr->ring,
|
|
(cpr->commit_cons+1) % cpr->ring.ring_size, 1);
|
|
|
|
if (rxfree != 0) {
|
|
rx->rx_cons += rxfree;
|
|
if (rx->rx_cons >= rx->rx_ring.ring_size)
|
|
rx->rx_cons -= rx->rx_ring.ring_size;
|
|
|
|
rx->rx_ag_cons += agfree;
|
|
if (rx->rx_ag_cons >= rx->rx_ag_ring.ring_size)
|
|
rx->rx_ag_cons -= rx->rx_ag_ring.ring_size;
|
|
|
|
if_rxr_put(&rx->rxr[0], rxfree);
|
|
if_rxr_put(&rx->rxr[1], agfree);
|
|
|
|
if (ifiq_input(rx->rx_ifiq, &ml)) {
|
|
if_rxr_livelocked(&rx->rxr[0]);
|
|
if_rxr_livelocked(&rx->rxr[1]);
|
|
}
|
|
|
|
bnxt_rx_fill(q);
|
|
if ((rx->rx_cons == rx->rx_prod) ||
|
|
(rx->rx_ag_cons == rx->rx_ag_prod))
|
|
timeout_add(&rx->rx_refill, 0);
|
|
}
|
|
if (txfree != 0) {
|
|
if (ifq_is_oactive(tx->tx_ifq))
|
|
ifq_restart(tx->tx_ifq);
|
|
}
|
|
return (rv);
|
|
}
|
|
|
|
void
|
|
bnxt_watchdog(struct ifnet *ifp)
|
|
{
|
|
}
|
|
|
|
void
|
|
bnxt_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
|
|
{
|
|
struct bnxt_softc *sc = (struct bnxt_softc *)ifp->if_softc;
|
|
bnxt_hwrm_port_phy_qcfg(sc, ifmr);
|
|
}
|
|
|
|
uint64_t
|
|
bnxt_get_media_type(uint64_t speed, int phy_type)
|
|
{
|
|
switch (phy_type) {
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_UNKNOWN:
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_BASECR:
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_25G_BASECR_CA_L:
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_25G_BASECR_CA_S:
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_25G_BASECR_CA_N:
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_100G_BASECR4:
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_40G_BASECR4:
|
|
switch (speed) {
|
|
case IF_Gbps(1):
|
|
return IFM_1000_T;
|
|
case IF_Gbps(10):
|
|
return IFM_10G_SFP_CU;
|
|
case IF_Gbps(25):
|
|
return IFM_25G_CR;
|
|
case IF_Gbps(40):
|
|
return IFM_40G_CR4;
|
|
case IF_Gbps(50):
|
|
return IFM_50G_CR2;
|
|
case IF_Gbps(100):
|
|
return IFM_100G_CR4;
|
|
}
|
|
break;
|
|
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_BASELR:
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_100G_BASELR4:
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_40G_BASELR4:
|
|
switch (speed) {
|
|
case IF_Gbps(1):
|
|
return IFM_1000_LX;
|
|
case IF_Gbps(10):
|
|
return IFM_10G_LR;
|
|
case IF_Gbps(25):
|
|
return IFM_25G_LR;
|
|
case IF_Gbps(40):
|
|
return IFM_40G_LR4;
|
|
case IF_Gbps(100):
|
|
return IFM_100G_LR4;
|
|
}
|
|
break;
|
|
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_BASESR:
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_25G_BASESR:
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_100G_BASESR4:
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_100G_BASESR10:
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_1G_BASESX:
|
|
switch (speed) {
|
|
case IF_Gbps(1):
|
|
return IFM_1000_SX;
|
|
case IF_Gbps(10):
|
|
return IFM_10G_SR;
|
|
case IF_Gbps(25):
|
|
return IFM_25G_SR;
|
|
case IF_Gbps(40):
|
|
return IFM_40G_SR4;
|
|
case IF_Gbps(100):
|
|
return IFM_100G_SR4;
|
|
}
|
|
break;
|
|
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_100G_BASEER4:
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_40G_BASEER4:
|
|
switch (speed) {
|
|
case IF_Gbps(10):
|
|
return IFM_10G_ER;
|
|
case IF_Gbps(25):
|
|
return IFM_25G_ER;
|
|
}
|
|
/* missing IFM_40G_ER4, IFM_100G_ER4 */
|
|
break;
|
|
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_BASEKR4:
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_BASEKR2:
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_BASEKR:
|
|
switch (speed) {
|
|
case IF_Gbps(10):
|
|
return IFM_10G_KR;
|
|
case IF_Gbps(20):
|
|
return IFM_20G_KR2;
|
|
case IF_Gbps(25):
|
|
return IFM_25G_KR;
|
|
case IF_Gbps(40):
|
|
return IFM_40G_KR4;
|
|
case IF_Gbps(50):
|
|
return IFM_50G_KR2;
|
|
case IF_Gbps(100):
|
|
return IFM_100G_KR4;
|
|
}
|
|
break;
|
|
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_BASEKX:
|
|
switch (speed) {
|
|
case IF_Gbps(1):
|
|
return IFM_1000_KX;
|
|
case IF_Mbps(2500):
|
|
return IFM_2500_KX;
|
|
case IF_Gbps(10):
|
|
return IFM_10G_KX4;
|
|
}
|
|
break;
|
|
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_BASET:
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_BASETE:
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_1G_BASET:
|
|
switch (speed) {
|
|
case IF_Mbps(10):
|
|
return IFM_10_T;
|
|
case IF_Mbps(100):
|
|
return IFM_100_TX;
|
|
case IF_Gbps(1):
|
|
return IFM_1000_T;
|
|
case IF_Mbps(2500):
|
|
return IFM_2500_T;
|
|
case IF_Gbps(10):
|
|
return IFM_10G_T;
|
|
}
|
|
break;
|
|
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_SGMIIEXTPHY:
|
|
switch (speed) {
|
|
case IF_Gbps(1):
|
|
return IFM_1000_SGMII;
|
|
}
|
|
break;
|
|
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_PHY_TYPE_40G_ACTIVE_CABLE:
|
|
switch (speed) {
|
|
case IF_Gbps(10):
|
|
return IFM_10G_AOC;
|
|
case IF_Gbps(25):
|
|
return IFM_25G_AOC;
|
|
case IF_Gbps(40):
|
|
return IFM_40G_AOC;
|
|
case IF_Gbps(100):
|
|
return IFM_100G_AOC;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
bnxt_add_media_type(struct bnxt_softc *sc, int supported_speeds, uint64_t speed, uint64_t ifmt)
|
|
{
|
|
int speed_bit = 0;
|
|
switch (speed) {
|
|
case IF_Gbps(1):
|
|
speed_bit = HWRM_PORT_PHY_QCFG_OUTPUT_SUPPORT_SPEEDS_1GB;
|
|
break;
|
|
case IF_Gbps(2):
|
|
speed_bit = HWRM_PORT_PHY_QCFG_OUTPUT_SUPPORT_SPEEDS_2GB;
|
|
break;
|
|
case IF_Mbps(2500):
|
|
speed_bit = HWRM_PORT_PHY_QCFG_OUTPUT_SUPPORT_SPEEDS_2_5GB;
|
|
break;
|
|
case IF_Gbps(10):
|
|
speed_bit = HWRM_PORT_PHY_QCFG_OUTPUT_SUPPORT_SPEEDS_10GB;
|
|
break;
|
|
case IF_Gbps(20):
|
|
speed_bit = HWRM_PORT_PHY_QCFG_OUTPUT_SUPPORT_SPEEDS_20GB;
|
|
break;
|
|
case IF_Gbps(25):
|
|
speed_bit = HWRM_PORT_PHY_QCFG_OUTPUT_SUPPORT_SPEEDS_25GB;
|
|
break;
|
|
case IF_Gbps(40):
|
|
speed_bit = HWRM_PORT_PHY_QCFG_OUTPUT_SUPPORT_SPEEDS_40GB;
|
|
break;
|
|
case IF_Gbps(50):
|
|
speed_bit = HWRM_PORT_PHY_QCFG_OUTPUT_SUPPORT_SPEEDS_50GB;
|
|
break;
|
|
case IF_Gbps(100):
|
|
speed_bit = HWRM_PORT_PHY_QCFG_OUTPUT_SUPPORT_SPEEDS_100GB;
|
|
break;
|
|
}
|
|
if (supported_speeds & speed_bit)
|
|
ifmedia_add(&sc->sc_media, IFM_ETHER | ifmt, 0, NULL);
|
|
}
|
|
|
|
int
|
|
bnxt_hwrm_port_phy_qcfg(struct bnxt_softc *softc, struct ifmediareq *ifmr)
|
|
{
|
|
struct ifnet *ifp = &softc->sc_ac.ac_if;
|
|
struct hwrm_port_phy_qcfg_input req = {0};
|
|
struct hwrm_port_phy_qcfg_output *resp =
|
|
BNXT_DMA_KVA(softc->sc_cmd_resp);
|
|
int link_state = LINK_STATE_DOWN;
|
|
uint64_t speeds[] = {
|
|
IF_Gbps(1), IF_Gbps(2), IF_Mbps(2500), IF_Gbps(10), IF_Gbps(20),
|
|
IF_Gbps(25), IF_Gbps(40), IF_Gbps(50), IF_Gbps(100)
|
|
};
|
|
uint64_t media_type;
|
|
int duplex;
|
|
int rc = 0;
|
|
int i;
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_PORT_PHY_QCFG);
|
|
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc) {
|
|
printf("%s: failed to query port phy config\n", DEVNAME(softc));
|
|
goto exit;
|
|
}
|
|
|
|
if (softc->sc_hwrm_ver > 0x10800)
|
|
duplex = resp->duplex_state;
|
|
else
|
|
duplex = resp->duplex_cfg;
|
|
|
|
if (resp->link == HWRM_PORT_PHY_QCFG_OUTPUT_LINK_LINK) {
|
|
if (duplex == HWRM_PORT_PHY_QCFG_OUTPUT_DUPLEX_STATE_HALF)
|
|
link_state = LINK_STATE_HALF_DUPLEX;
|
|
else
|
|
link_state = LINK_STATE_FULL_DUPLEX;
|
|
|
|
switch (resp->link_speed) {
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_LINK_SPEED_10MB:
|
|
ifp->if_baudrate = IF_Mbps(10);
|
|
break;
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_LINK_SPEED_100MB:
|
|
ifp->if_baudrate = IF_Mbps(100);
|
|
break;
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_LINK_SPEED_1GB:
|
|
ifp->if_baudrate = IF_Gbps(1);
|
|
break;
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_LINK_SPEED_2GB:
|
|
ifp->if_baudrate = IF_Gbps(2);
|
|
break;
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_LINK_SPEED_2_5GB:
|
|
ifp->if_baudrate = IF_Mbps(2500);
|
|
break;
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_LINK_SPEED_10GB:
|
|
ifp->if_baudrate = IF_Gbps(10);
|
|
break;
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_LINK_SPEED_20GB:
|
|
ifp->if_baudrate = IF_Gbps(20);
|
|
break;
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_LINK_SPEED_25GB:
|
|
ifp->if_baudrate = IF_Gbps(25);
|
|
break;
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_LINK_SPEED_40GB:
|
|
ifp->if_baudrate = IF_Gbps(40);
|
|
break;
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_LINK_SPEED_50GB:
|
|
ifp->if_baudrate = IF_Gbps(50);
|
|
break;
|
|
case HWRM_PORT_PHY_QCFG_OUTPUT_LINK_SPEED_100GB:
|
|
ifp->if_baudrate = IF_Gbps(100);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ifmedia_delete_instance(&softc->sc_media, IFM_INST_ANY);
|
|
for (i = 0; i < nitems(speeds); i++) {
|
|
media_type = bnxt_get_media_type(speeds[i], resp->phy_type);
|
|
if (media_type != 0)
|
|
bnxt_add_media_type(softc, resp->support_speeds,
|
|
speeds[i], media_type);
|
|
}
|
|
ifmedia_add(&softc->sc_media, IFM_ETHER|IFM_AUTO, 0, NULL);
|
|
ifmedia_set(&softc->sc_media, IFM_ETHER|IFM_AUTO);
|
|
|
|
if (ifmr != NULL) {
|
|
ifmr->ifm_status = IFM_AVALID;
|
|
if (LINK_STATE_IS_UP(ifp->if_link_state)) {
|
|
ifmr->ifm_status |= IFM_ACTIVE;
|
|
ifmr->ifm_active = IFM_ETHER | IFM_AUTO;
|
|
if (resp->pause & HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_TX)
|
|
ifmr->ifm_active |= IFM_ETH_TXPAUSE;
|
|
if (resp->pause & HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_RX)
|
|
ifmr->ifm_active |= IFM_ETH_RXPAUSE;
|
|
if (duplex == HWRM_PORT_PHY_QCFG_OUTPUT_DUPLEX_STATE_HALF)
|
|
ifmr->ifm_active |= IFM_HDX;
|
|
else
|
|
ifmr->ifm_active |= IFM_FDX;
|
|
|
|
media_type = bnxt_get_media_type(ifp->if_baudrate, resp->phy_type);
|
|
if (media_type != 0)
|
|
ifmr->ifm_active |= media_type;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
|
|
if (rc == 0 && (link_state != ifp->if_link_state)) {
|
|
ifp->if_link_state = link_state;
|
|
if_link_state_change(ifp);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
bnxt_media_change(struct ifnet *ifp)
|
|
{
|
|
struct bnxt_softc *sc = (struct bnxt_softc *)ifp->if_softc;
|
|
struct hwrm_port_phy_cfg_input req = {0};
|
|
uint64_t link_speed;
|
|
|
|
if (IFM_TYPE(sc->sc_media.ifm_media) != IFM_ETHER)
|
|
return EINVAL;
|
|
|
|
if (sc->sc_flags & BNXT_FLAG_NPAR)
|
|
return ENODEV;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(sc, &req, HWRM_PORT_PHY_CFG);
|
|
|
|
switch (IFM_SUBTYPE(sc->sc_media.ifm_media)) {
|
|
case IFM_100G_CR4:
|
|
case IFM_100G_SR4:
|
|
case IFM_100G_KR4:
|
|
case IFM_100G_LR4:
|
|
case IFM_100G_AOC:
|
|
link_speed = HWRM_PORT_PHY_QCFG_OUTPUT_FORCE_LINK_SPEED_100GB;
|
|
break;
|
|
|
|
case IFM_50G_CR2:
|
|
case IFM_50G_KR2:
|
|
link_speed = HWRM_PORT_PHY_QCFG_OUTPUT_FORCE_LINK_SPEED_50GB;
|
|
break;
|
|
|
|
case IFM_40G_CR4:
|
|
case IFM_40G_SR4:
|
|
case IFM_40G_LR4:
|
|
case IFM_40G_KR4:
|
|
case IFM_40G_AOC:
|
|
link_speed = HWRM_PORT_PHY_QCFG_OUTPUT_FORCE_LINK_SPEED_40GB;
|
|
break;
|
|
|
|
case IFM_25G_CR:
|
|
case IFM_25G_KR:
|
|
case IFM_25G_SR:
|
|
case IFM_25G_LR:
|
|
case IFM_25G_ER:
|
|
case IFM_25G_AOC:
|
|
link_speed = HWRM_PORT_PHY_QCFG_OUTPUT_FORCE_LINK_SPEED_25GB;
|
|
break;
|
|
|
|
case IFM_10G_LR:
|
|
case IFM_10G_SR:
|
|
case IFM_10G_CX4:
|
|
case IFM_10G_T:
|
|
case IFM_10G_SFP_CU:
|
|
case IFM_10G_LRM:
|
|
case IFM_10G_KX4:
|
|
case IFM_10G_KR:
|
|
case IFM_10G_CR1:
|
|
case IFM_10G_ER:
|
|
case IFM_10G_AOC:
|
|
link_speed = HWRM_PORT_PHY_QCFG_OUTPUT_FORCE_LINK_SPEED_10GB;
|
|
break;
|
|
|
|
case IFM_2500_SX:
|
|
case IFM_2500_KX:
|
|
case IFM_2500_T:
|
|
link_speed = HWRM_PORT_PHY_QCFG_OUTPUT_FORCE_LINK_SPEED_2_5GB;
|
|
break;
|
|
|
|
case IFM_1000_T:
|
|
case IFM_1000_LX:
|
|
case IFM_1000_SX:
|
|
case IFM_1000_CX:
|
|
case IFM_1000_KX:
|
|
link_speed = HWRM_PORT_PHY_QCFG_OUTPUT_FORCE_LINK_SPEED_1GB;
|
|
break;
|
|
|
|
case IFM_100_TX:
|
|
link_speed = HWRM_PORT_PHY_QCFG_OUTPUT_FORCE_LINK_SPEED_100MB;
|
|
break;
|
|
|
|
default:
|
|
link_speed = 0;
|
|
}
|
|
|
|
req.enables |= htole32(HWRM_PORT_PHY_CFG_INPUT_ENABLES_AUTO_DUPLEX);
|
|
req.auto_duplex = HWRM_PORT_PHY_CFG_INPUT_AUTO_DUPLEX_BOTH;
|
|
if (link_speed == 0) {
|
|
req.auto_mode |=
|
|
HWRM_PORT_PHY_CFG_INPUT_AUTO_MODE_ALL_SPEEDS;
|
|
req.flags |=
|
|
htole32(HWRM_PORT_PHY_CFG_INPUT_FLAGS_RESTART_AUTONEG);
|
|
req.enables |=
|
|
htole32(HWRM_PORT_PHY_CFG_INPUT_ENABLES_AUTO_MODE);
|
|
} else {
|
|
req.force_link_speed = htole16(link_speed);
|
|
req.flags |= htole32(HWRM_PORT_PHY_CFG_INPUT_FLAGS_FORCE);
|
|
}
|
|
req.flags |= htole32(HWRM_PORT_PHY_CFG_INPUT_FLAGS_RESET_PHY);
|
|
|
|
return hwrm_send_message(sc, &req, sizeof(req));
|
|
}
|
|
|
|
int
|
|
bnxt_media_autonegotiate(struct bnxt_softc *sc)
|
|
{
|
|
struct hwrm_port_phy_cfg_input req = {0};
|
|
|
|
if (sc->sc_flags & BNXT_FLAG_NPAR)
|
|
return ENODEV;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(sc, &req, HWRM_PORT_PHY_CFG);
|
|
req.auto_mode |= HWRM_PORT_PHY_CFG_INPUT_AUTO_MODE_ALL_SPEEDS;
|
|
req.auto_duplex = HWRM_PORT_PHY_CFG_INPUT_AUTO_DUPLEX_BOTH;
|
|
req.enables |= htole32(HWRM_PORT_PHY_CFG_INPUT_ENABLES_AUTO_MODE |
|
|
HWRM_PORT_PHY_CFG_INPUT_ENABLES_AUTO_DUPLEX);
|
|
req.flags |= htole32(HWRM_PORT_PHY_CFG_INPUT_FLAGS_RESTART_AUTONEG);
|
|
req.flags |= htole32(HWRM_PORT_PHY_CFG_INPUT_FLAGS_RESET_PHY);
|
|
|
|
return hwrm_send_message(sc, &req, sizeof(req));
|
|
}
|
|
|
|
|
|
void
|
|
bnxt_mark_cpr_invalid(struct bnxt_cp_ring *cpr)
|
|
{
|
|
struct cmpl_base *cmp = (void *)cpr->ring.vaddr;
|
|
int i;
|
|
|
|
for (i = 0; i < cpr->ring.ring_size; i++)
|
|
cmp[i].info3_v = !cpr->v_bit;
|
|
}
|
|
|
|
void
|
|
bnxt_write_cp_doorbell(struct bnxt_softc *sc, struct bnxt_ring *ring,
|
|
int enable)
|
|
{
|
|
uint32_t val = CMPL_DOORBELL_KEY_CMPL;
|
|
if (enable == 0)
|
|
val |= CMPL_DOORBELL_MASK;
|
|
|
|
bus_space_barrier(sc->sc_db_t, sc->sc_db_h, ring->doorbell, 4,
|
|
BUS_SPACE_BARRIER_WRITE);
|
|
bus_space_barrier(sc->sc_db_t, sc->sc_db_h, 0, sc->sc_db_s,
|
|
BUS_SPACE_BARRIER_WRITE);
|
|
bus_space_write_4(sc->sc_db_t, sc->sc_db_h, ring->doorbell,
|
|
htole32(val));
|
|
}
|
|
|
|
void
|
|
bnxt_write_cp_doorbell_index(struct bnxt_softc *sc, struct bnxt_ring *ring,
|
|
uint32_t index, int enable)
|
|
{
|
|
uint32_t val = CMPL_DOORBELL_KEY_CMPL | CMPL_DOORBELL_IDX_VALID |
|
|
(index & CMPL_DOORBELL_IDX_MASK);
|
|
if (enable == 0)
|
|
val |= CMPL_DOORBELL_MASK;
|
|
bus_space_barrier(sc->sc_db_t, sc->sc_db_h, ring->doorbell, 4,
|
|
BUS_SPACE_BARRIER_WRITE);
|
|
bus_space_write_4(sc->sc_db_t, sc->sc_db_h, ring->doorbell,
|
|
htole32(val));
|
|
bus_space_barrier(sc->sc_db_t, sc->sc_db_h, 0, sc->sc_db_s,
|
|
BUS_SPACE_BARRIER_WRITE);
|
|
}
|
|
|
|
void
|
|
bnxt_write_rx_doorbell(struct bnxt_softc *sc, struct bnxt_ring *ring, int index)
|
|
{
|
|
uint32_t val = RX_DOORBELL_KEY_RX | index;
|
|
bus_space_barrier(sc->sc_db_t, sc->sc_db_h, ring->doorbell, 4,
|
|
BUS_SPACE_BARRIER_WRITE);
|
|
bus_space_write_4(sc->sc_db_t, sc->sc_db_h, ring->doorbell,
|
|
htole32(val));
|
|
|
|
/* second write isn't necessary on all hardware */
|
|
bus_space_barrier(sc->sc_db_t, sc->sc_db_h, ring->doorbell, 4,
|
|
BUS_SPACE_BARRIER_WRITE);
|
|
bus_space_write_4(sc->sc_db_t, sc->sc_db_h, ring->doorbell,
|
|
htole32(val));
|
|
}
|
|
|
|
void
|
|
bnxt_write_tx_doorbell(struct bnxt_softc *sc, struct bnxt_ring *ring, int index)
|
|
{
|
|
uint32_t val = TX_DOORBELL_KEY_TX | index;
|
|
bus_space_barrier(sc->sc_db_t, sc->sc_db_h, ring->doorbell, 4,
|
|
BUS_SPACE_BARRIER_WRITE);
|
|
bus_space_write_4(sc->sc_db_t, sc->sc_db_h, ring->doorbell,
|
|
htole32(val));
|
|
|
|
/* second write isn't necessary on all hardware */
|
|
bus_space_barrier(sc->sc_db_t, sc->sc_db_h, ring->doorbell, 4,
|
|
BUS_SPACE_BARRIER_WRITE);
|
|
bus_space_write_4(sc->sc_db_t, sc->sc_db_h, ring->doorbell,
|
|
htole32(val));
|
|
}
|
|
|
|
u_int
|
|
bnxt_rx_fill_slots(struct bnxt_softc *sc, struct bnxt_ring *ring, void *ring_mem,
|
|
struct bnxt_slot *slots, uint *prod, int bufsize, uint16_t bdtype,
|
|
u_int nslots)
|
|
{
|
|
struct rx_prod_pkt_bd *rxring;
|
|
struct bnxt_slot *bs;
|
|
struct mbuf *m;
|
|
uint p, fills;
|
|
|
|
rxring = (struct rx_prod_pkt_bd *)ring_mem;
|
|
p = *prod;
|
|
for (fills = 0; fills < nslots; fills++) {
|
|
bs = &slots[p];
|
|
m = MCLGETL(NULL, M_DONTWAIT, bufsize);
|
|
if (m == NULL)
|
|
break;
|
|
|
|
m->m_len = m->m_pkthdr.len = bufsize;
|
|
if (bus_dmamap_load_mbuf(sc->sc_dmat, bs->bs_map, m,
|
|
BUS_DMA_NOWAIT) != 0) {
|
|
m_freem(m);
|
|
break;
|
|
}
|
|
bs->bs_m = m;
|
|
|
|
rxring[p].flags_type = htole16(bdtype);
|
|
rxring[p].len = htole16(bufsize);
|
|
rxring[p].opaque = p;
|
|
rxring[p].addr = htole64(bs->bs_map->dm_segs[0].ds_addr);
|
|
|
|
if (++p >= ring->ring_size)
|
|
p = 0;
|
|
}
|
|
|
|
if (fills != 0)
|
|
bnxt_write_rx_doorbell(sc, ring, p);
|
|
*prod = p;
|
|
|
|
return (nslots - fills);
|
|
}
|
|
|
|
int
|
|
bnxt_rx_fill(struct bnxt_queue *q)
|
|
{
|
|
struct bnxt_rx_queue *rx = &q->q_rx;
|
|
struct bnxt_softc *sc = q->q_sc;
|
|
u_int slots;
|
|
int rv = 0;
|
|
|
|
slots = if_rxr_get(&rx->rxr[0], rx->rx_ring.ring_size);
|
|
if (slots > 0) {
|
|
slots = bnxt_rx_fill_slots(sc, &rx->rx_ring,
|
|
BNXT_DMA_KVA(rx->rx_ring_mem), rx->rx_slots,
|
|
&rx->rx_prod, MCLBYTES,
|
|
RX_PROD_PKT_BD_TYPE_RX_PROD_PKT, slots);
|
|
if_rxr_put(&rx->rxr[0], slots);
|
|
} else
|
|
rv = 1;
|
|
|
|
slots = if_rxr_get(&rx->rxr[1], rx->rx_ag_ring.ring_size);
|
|
if (slots > 0) {
|
|
slots = bnxt_rx_fill_slots(sc, &rx->rx_ag_ring,
|
|
BNXT_DMA_KVA(rx->rx_ring_mem) + PAGE_SIZE,
|
|
rx->rx_ag_slots, &rx->rx_ag_prod,
|
|
BNXT_AG_BUFFER_SIZE,
|
|
RX_PROD_AGG_BD_TYPE_RX_PROD_AGG, slots);
|
|
if_rxr_put(&rx->rxr[1], slots);
|
|
} else
|
|
rv = 1;
|
|
|
|
return (rv);
|
|
}
|
|
|
|
void
|
|
bnxt_refill(void *xq)
|
|
{
|
|
struct bnxt_queue *q = xq;
|
|
struct bnxt_rx_queue *rx = &q->q_rx;
|
|
|
|
bnxt_rx_fill(q);
|
|
|
|
if (rx->rx_cons == rx->rx_prod)
|
|
timeout_add(&rx->rx_refill, 1);
|
|
}
|
|
|
|
int
|
|
bnxt_rx(struct bnxt_softc *sc, struct bnxt_rx_queue *rx,
|
|
struct bnxt_cp_ring *cpr, struct mbuf_list *ml, int *slots, int *agslots,
|
|
struct cmpl_base *cmpl)
|
|
{
|
|
struct mbuf *m, *am;
|
|
struct bnxt_slot *bs;
|
|
struct rx_pkt_cmpl *rxlo = (struct rx_pkt_cmpl *)cmpl;
|
|
struct rx_pkt_cmpl_hi *rxhi;
|
|
struct rx_abuf_cmpl *ag;
|
|
uint32_t flags;
|
|
uint16_t errors;
|
|
|
|
/* second part of the rx completion */
|
|
rxhi = (struct rx_pkt_cmpl_hi *)bnxt_cpr_next_cmpl(sc, cpr);
|
|
if (rxhi == NULL) {
|
|
return (1);
|
|
}
|
|
|
|
/* packets over 2k in size use an aggregation buffer completion too */
|
|
ag = NULL;
|
|
if ((rxlo->agg_bufs_v1 >> RX_PKT_CMPL_AGG_BUFS_SFT) != 0) {
|
|
ag = (struct rx_abuf_cmpl *)bnxt_cpr_next_cmpl(sc, cpr);
|
|
if (ag == NULL) {
|
|
return (1);
|
|
}
|
|
}
|
|
|
|
bs = &rx->rx_slots[rxlo->opaque];
|
|
bus_dmamap_sync(sc->sc_dmat, bs->bs_map, 0, bs->bs_map->dm_mapsize,
|
|
BUS_DMASYNC_POSTREAD);
|
|
bus_dmamap_unload(sc->sc_dmat, bs->bs_map);
|
|
|
|
m = bs->bs_m;
|
|
bs->bs_m = NULL;
|
|
m->m_pkthdr.len = m->m_len = letoh16(rxlo->len);
|
|
(*slots)++;
|
|
|
|
/* checksum flags */
|
|
flags = lemtoh32(&rxhi->flags2);
|
|
errors = lemtoh16(&rxhi->errors_v2);
|
|
if ((flags & RX_PKT_CMPL_FLAGS2_IP_CS_CALC) != 0 &&
|
|
(errors & RX_PKT_CMPL_ERRORS_IP_CS_ERROR) == 0)
|
|
m->m_pkthdr.csum_flags |= M_IPV4_CSUM_IN_OK;
|
|
|
|
if ((flags & RX_PKT_CMPL_FLAGS2_L4_CS_CALC) != 0 &&
|
|
(errors & RX_PKT_CMPL_ERRORS_L4_CS_ERROR) == 0)
|
|
m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK |
|
|
M_UDP_CSUM_IN_OK;
|
|
|
|
#if NVLAN > 0
|
|
if ((flags & RX_PKT_CMPL_FLAGS2_META_FORMAT_MASK) ==
|
|
RX_PKT_CMPL_FLAGS2_META_FORMAT_VLAN) {
|
|
m->m_pkthdr.ether_vtag = lemtoh16(&rxhi->metadata);
|
|
m->m_flags |= M_VLANTAG;
|
|
}
|
|
#endif
|
|
|
|
if (lemtoh16(&rxlo->flags_type) & RX_PKT_CMPL_FLAGS_RSS_VALID) {
|
|
m->m_pkthdr.ph_flowid = lemtoh32(&rxlo->rss_hash);
|
|
m->m_pkthdr.csum_flags |= M_FLOWID;
|
|
}
|
|
|
|
if (ag != NULL) {
|
|
bs = &rx->rx_ag_slots[ag->opaque];
|
|
bus_dmamap_sync(sc->sc_dmat, bs->bs_map, 0,
|
|
bs->bs_map->dm_mapsize, BUS_DMASYNC_POSTREAD);
|
|
bus_dmamap_unload(sc->sc_dmat, bs->bs_map);
|
|
|
|
am = bs->bs_m;
|
|
bs->bs_m = NULL;
|
|
am->m_len = letoh16(ag->len);
|
|
m->m_next = am;
|
|
m->m_pkthdr.len += am->m_len;
|
|
(*agslots)++;
|
|
}
|
|
|
|
ml_enqueue(ml, m);
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
bnxt_txeof(struct bnxt_softc *sc, struct bnxt_tx_queue *tx, int *txfree,
|
|
struct cmpl_base *cmpl)
|
|
{
|
|
struct tx_cmpl *txcmpl = (struct tx_cmpl *)cmpl;
|
|
struct bnxt_slot *bs;
|
|
bus_dmamap_t map;
|
|
u_int idx, segs, last;
|
|
|
|
idx = tx->tx_ring_cons;
|
|
last = tx->tx_cons;
|
|
do {
|
|
bs = &tx->tx_slots[tx->tx_cons];
|
|
map = bs->bs_map;
|
|
|
|
segs = BNXT_TX_SLOTS(bs);
|
|
bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
|
|
BUS_DMASYNC_POSTWRITE);
|
|
bus_dmamap_unload(sc->sc_dmat, map);
|
|
m_freem(bs->bs_m);
|
|
bs->bs_m = NULL;
|
|
|
|
idx += segs;
|
|
(*txfree) += segs;
|
|
if (idx >= tx->tx_ring.ring_size)
|
|
idx -= tx->tx_ring.ring_size;
|
|
|
|
last = tx->tx_cons;
|
|
if (++tx->tx_cons >= tx->tx_ring.ring_size)
|
|
tx->tx_cons = 0;
|
|
|
|
} while (last != txcmpl->opaque);
|
|
tx->tx_ring_cons = idx;
|
|
}
|
|
|
|
/* bnxt_hwrm.c */
|
|
|
|
int
|
|
bnxt_hwrm_err_map(uint16_t err)
|
|
{
|
|
int rc;
|
|
|
|
switch (err) {
|
|
case HWRM_ERR_CODE_SUCCESS:
|
|
return 0;
|
|
case HWRM_ERR_CODE_INVALID_PARAMS:
|
|
case HWRM_ERR_CODE_INVALID_FLAGS:
|
|
case HWRM_ERR_CODE_INVALID_ENABLES:
|
|
return EINVAL;
|
|
case HWRM_ERR_CODE_RESOURCE_ACCESS_DENIED:
|
|
return EACCES;
|
|
case HWRM_ERR_CODE_RESOURCE_ALLOC_ERROR:
|
|
return ENOMEM;
|
|
case HWRM_ERR_CODE_CMD_NOT_SUPPORTED:
|
|
return ENOSYS;
|
|
case HWRM_ERR_CODE_FAIL:
|
|
return EIO;
|
|
case HWRM_ERR_CODE_HWRM_ERROR:
|
|
case HWRM_ERR_CODE_UNKNOWN_ERR:
|
|
default:
|
|
return EIO;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
void
|
|
bnxt_hwrm_cmd_hdr_init(struct bnxt_softc *softc, void *request,
|
|
uint16_t req_type)
|
|
{
|
|
struct input *req = request;
|
|
|
|
req->req_type = htole16(req_type);
|
|
req->cmpl_ring = 0xffff;
|
|
req->target_id = 0xffff;
|
|
req->resp_addr = htole64(BNXT_DMA_DVA(softc->sc_cmd_resp));
|
|
}
|
|
|
|
int
|
|
_hwrm_send_message(struct bnxt_softc *softc, void *msg, uint32_t msg_len)
|
|
{
|
|
struct input *req = msg;
|
|
struct hwrm_err_output *resp = BNXT_DMA_KVA(softc->sc_cmd_resp);
|
|
uint32_t *data = msg;
|
|
int i;
|
|
uint8_t *valid;
|
|
uint16_t err;
|
|
uint16_t max_req_len = HWRM_MAX_REQ_LEN;
|
|
struct hwrm_short_input short_input = {0};
|
|
|
|
/* TODO: DMASYNC in here. */
|
|
req->seq_id = htole16(softc->sc_cmd_seq++);
|
|
memset(resp, 0, PAGE_SIZE);
|
|
|
|
if (softc->sc_flags & BNXT_FLAG_SHORT_CMD) {
|
|
void *short_cmd_req = BNXT_DMA_KVA(softc->sc_cmd_resp);
|
|
|
|
memcpy(short_cmd_req, req, msg_len);
|
|
memset((uint8_t *) short_cmd_req + msg_len, 0,
|
|
softc->sc_max_req_len - msg_len);
|
|
|
|
short_input.req_type = req->req_type;
|
|
short_input.signature =
|
|
htole16(HWRM_SHORT_INPUT_SIGNATURE_SHORT_CMD);
|
|
short_input.size = htole16(msg_len);
|
|
short_input.req_addr =
|
|
htole64(BNXT_DMA_DVA(softc->sc_cmd_resp));
|
|
|
|
data = (uint32_t *)&short_input;
|
|
msg_len = sizeof(short_input);
|
|
|
|
/* Sync memory write before updating doorbell */
|
|
membar_sync();
|
|
|
|
max_req_len = BNXT_HWRM_SHORT_REQ_LEN;
|
|
}
|
|
|
|
/* Write request msg to hwrm channel */
|
|
for (i = 0; i < msg_len; i += 4) {
|
|
bus_space_write_4(softc->sc_hwrm_t,
|
|
softc->sc_hwrm_h,
|
|
i, *data);
|
|
data++;
|
|
}
|
|
|
|
/* Clear to the end of the request buffer */
|
|
for (i = msg_len; i < max_req_len; i += 4)
|
|
bus_space_write_4(softc->sc_hwrm_t, softc->sc_hwrm_h,
|
|
i, 0);
|
|
|
|
/* Ring channel doorbell */
|
|
bus_space_write_4(softc->sc_hwrm_t, softc->sc_hwrm_h, 0x100,
|
|
htole32(1));
|
|
|
|
/* Check if response len is updated */
|
|
for (i = 0; i < softc->sc_cmd_timeo; i++) {
|
|
if (resp->resp_len && resp->resp_len <= 4096)
|
|
break;
|
|
DELAY(1000);
|
|
}
|
|
if (i >= softc->sc_cmd_timeo) {
|
|
printf("%s: timeout sending %s: (timeout: %u) seq: %d\n",
|
|
DEVNAME(softc), GET_HWRM_REQ_TYPE(req->req_type),
|
|
softc->sc_cmd_timeo,
|
|
le16toh(req->seq_id));
|
|
return ETIMEDOUT;
|
|
}
|
|
/* Last byte of resp contains the valid key */
|
|
valid = (uint8_t *)resp + resp->resp_len - 1;
|
|
for (i = 0; i < softc->sc_cmd_timeo; i++) {
|
|
if (*valid == HWRM_RESP_VALID_KEY)
|
|
break;
|
|
DELAY(1000);
|
|
}
|
|
if (i >= softc->sc_cmd_timeo) {
|
|
printf("%s: timeout sending %s: "
|
|
"(timeout: %u) msg {0x%x 0x%x} len:%d v: %d\n",
|
|
DEVNAME(softc), GET_HWRM_REQ_TYPE(req->req_type),
|
|
softc->sc_cmd_timeo, le16toh(req->req_type),
|
|
le16toh(req->seq_id), msg_len,
|
|
*valid);
|
|
return ETIMEDOUT;
|
|
}
|
|
|
|
err = le16toh(resp->error_code);
|
|
if (err) {
|
|
/* HWRM_ERR_CODE_FAIL is a "normal" error, don't log */
|
|
if (err != HWRM_ERR_CODE_FAIL) {
|
|
printf("%s: %s command returned %s error.\n",
|
|
DEVNAME(softc),
|
|
GET_HWRM_REQ_TYPE(req->req_type),
|
|
GET_HWRM_ERROR_CODE(err));
|
|
}
|
|
return bnxt_hwrm_err_map(err);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
hwrm_send_message(struct bnxt_softc *softc, void *msg, uint32_t msg_len)
|
|
{
|
|
int rc;
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, msg, msg_len);
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
return rc;
|
|
}
|
|
|
|
|
|
int
|
|
bnxt_hwrm_queue_qportcfg(struct bnxt_softc *softc)
|
|
{
|
|
struct hwrm_queue_qportcfg_input req = {0};
|
|
struct hwrm_queue_qportcfg_output *resp =
|
|
BNXT_DMA_KVA(softc->sc_cmd_resp);
|
|
int rc = 0;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_QPORTCFG);
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc)
|
|
goto qportcfg_exit;
|
|
|
|
if (!resp->max_configurable_queues) {
|
|
rc = -EINVAL;
|
|
goto qportcfg_exit;
|
|
}
|
|
|
|
softc->sc_tx_queue_id = resp->queue_id0;
|
|
|
|
qportcfg_exit:
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
bnxt_hwrm_ver_get(struct bnxt_softc *softc)
|
|
{
|
|
struct hwrm_ver_get_input req = {0};
|
|
struct hwrm_ver_get_output *resp =
|
|
BNXT_DMA_KVA(softc->sc_cmd_resp);
|
|
int rc;
|
|
#if 0
|
|
const char nastr[] = "<not installed>";
|
|
const char naver[] = "<N/A>";
|
|
#endif
|
|
uint32_t dev_caps_cfg;
|
|
|
|
softc->sc_max_req_len = HWRM_MAX_REQ_LEN;
|
|
softc->sc_cmd_timeo = 1000;
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_VER_GET);
|
|
|
|
req.hwrm_intf_maj = HWRM_VERSION_MAJOR;
|
|
req.hwrm_intf_min = HWRM_VERSION_MINOR;
|
|
req.hwrm_intf_upd = HWRM_VERSION_UPDATE;
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc)
|
|
goto fail;
|
|
|
|
printf(": fw ver %d.%d.%d, ", resp->hwrm_fw_maj, resp->hwrm_fw_min,
|
|
resp->hwrm_fw_bld);
|
|
|
|
softc->sc_hwrm_ver = (resp->hwrm_intf_maj << 16) |
|
|
(resp->hwrm_intf_min << 8) | resp->hwrm_intf_upd;
|
|
#if 0
|
|
snprintf(softc->ver_info->hwrm_if_ver, BNXT_VERSTR_SIZE, "%d.%d.%d",
|
|
resp->hwrm_intf_maj, resp->hwrm_intf_min, resp->hwrm_intf_upd);
|
|
softc->ver_info->hwrm_if_major = resp->hwrm_intf_maj;
|
|
softc->ver_info->hwrm_if_minor = resp->hwrm_intf_min;
|
|
softc->ver_info->hwrm_if_update = resp->hwrm_intf_upd;
|
|
snprintf(softc->ver_info->hwrm_fw_ver, BNXT_VERSTR_SIZE, "%d.%d.%d",
|
|
resp->hwrm_fw_maj, resp->hwrm_fw_min, resp->hwrm_fw_bld);
|
|
strlcpy(softc->ver_info->driver_hwrm_if_ver, HWRM_VERSION_STR,
|
|
BNXT_VERSTR_SIZE);
|
|
strlcpy(softc->ver_info->hwrm_fw_name, resp->hwrm_fw_name,
|
|
BNXT_NAME_SIZE);
|
|
|
|
if (resp->mgmt_fw_maj == 0 && resp->mgmt_fw_min == 0 &&
|
|
resp->mgmt_fw_bld == 0) {
|
|
strlcpy(softc->ver_info->mgmt_fw_ver, naver, BNXT_VERSTR_SIZE);
|
|
strlcpy(softc->ver_info->mgmt_fw_name, nastr, BNXT_NAME_SIZE);
|
|
}
|
|
else {
|
|
snprintf(softc->ver_info->mgmt_fw_ver, BNXT_VERSTR_SIZE,
|
|
"%d.%d.%d", resp->mgmt_fw_maj, resp->mgmt_fw_min,
|
|
resp->mgmt_fw_bld);
|
|
strlcpy(softc->ver_info->mgmt_fw_name, resp->mgmt_fw_name,
|
|
BNXT_NAME_SIZE);
|
|
}
|
|
if (resp->netctrl_fw_maj == 0 && resp->netctrl_fw_min == 0 &&
|
|
resp->netctrl_fw_bld == 0) {
|
|
strlcpy(softc->ver_info->netctrl_fw_ver, naver,
|
|
BNXT_VERSTR_SIZE);
|
|
strlcpy(softc->ver_info->netctrl_fw_name, nastr,
|
|
BNXT_NAME_SIZE);
|
|
}
|
|
else {
|
|
snprintf(softc->ver_info->netctrl_fw_ver, BNXT_VERSTR_SIZE,
|
|
"%d.%d.%d", resp->netctrl_fw_maj, resp->netctrl_fw_min,
|
|
resp->netctrl_fw_bld);
|
|
strlcpy(softc->ver_info->netctrl_fw_name, resp->netctrl_fw_name,
|
|
BNXT_NAME_SIZE);
|
|
}
|
|
if (resp->roce_fw_maj == 0 && resp->roce_fw_min == 0 &&
|
|
resp->roce_fw_bld == 0) {
|
|
strlcpy(softc->ver_info->roce_fw_ver, naver, BNXT_VERSTR_SIZE);
|
|
strlcpy(softc->ver_info->roce_fw_name, nastr, BNXT_NAME_SIZE);
|
|
}
|
|
else {
|
|
snprintf(softc->ver_info->roce_fw_ver, BNXT_VERSTR_SIZE,
|
|
"%d.%d.%d", resp->roce_fw_maj, resp->roce_fw_min,
|
|
resp->roce_fw_bld);
|
|
strlcpy(softc->ver_info->roce_fw_name, resp->roce_fw_name,
|
|
BNXT_NAME_SIZE);
|
|
}
|
|
softc->ver_info->chip_num = le16toh(resp->chip_num);
|
|
softc->ver_info->chip_rev = resp->chip_rev;
|
|
softc->ver_info->chip_metal = resp->chip_metal;
|
|
softc->ver_info->chip_bond_id = resp->chip_bond_id;
|
|
softc->ver_info->chip_type = resp->chip_platform_type;
|
|
#endif
|
|
|
|
if (resp->max_req_win_len)
|
|
softc->sc_max_req_len = le16toh(resp->max_req_win_len);
|
|
if (resp->def_req_timeout)
|
|
softc->sc_cmd_timeo = le16toh(resp->def_req_timeout);
|
|
|
|
dev_caps_cfg = le32toh(resp->dev_caps_cfg);
|
|
if ((dev_caps_cfg & HWRM_VER_GET_OUTPUT_DEV_CAPS_CFG_SHORT_CMD_SUPPORTED) &&
|
|
(dev_caps_cfg & HWRM_VER_GET_OUTPUT_DEV_CAPS_CFG_SHORT_CMD_REQUIRED))
|
|
softc->sc_flags |= BNXT_FLAG_SHORT_CMD;
|
|
|
|
fail:
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
return rc;
|
|
}
|
|
|
|
|
|
int
|
|
bnxt_hwrm_func_drv_rgtr(struct bnxt_softc *softc)
|
|
{
|
|
struct hwrm_func_drv_rgtr_input req = {0};
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_FUNC_DRV_RGTR);
|
|
|
|
req.enables = htole32(HWRM_FUNC_DRV_RGTR_INPUT_ENABLES_VER |
|
|
HWRM_FUNC_DRV_RGTR_INPUT_ENABLES_OS_TYPE);
|
|
req.os_type = htole16(HWRM_FUNC_DRV_RGTR_INPUT_OS_TYPE_FREEBSD);
|
|
|
|
req.ver_maj = 6;
|
|
req.ver_min = 4;
|
|
req.ver_upd = 0;
|
|
|
|
return hwrm_send_message(softc, &req, sizeof(req));
|
|
}
|
|
|
|
#if 0
|
|
|
|
int
|
|
bnxt_hwrm_func_drv_unrgtr(struct bnxt_softc *softc, bool shutdown)
|
|
{
|
|
struct hwrm_func_drv_unrgtr_input req = {0};
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_FUNC_DRV_UNRGTR);
|
|
if (shutdown == true)
|
|
req.flags |=
|
|
HWRM_FUNC_DRV_UNRGTR_INPUT_FLAGS_PREPARE_FOR_SHUTDOWN;
|
|
return hwrm_send_message(softc, &req, sizeof(req));
|
|
}
|
|
|
|
#endif
|
|
|
|
int
|
|
bnxt_hwrm_func_qcaps(struct bnxt_softc *softc)
|
|
{
|
|
int rc = 0;
|
|
struct hwrm_func_qcaps_input req = {0};
|
|
struct hwrm_func_qcaps_output *resp =
|
|
BNXT_DMA_KVA(softc->sc_cmd_resp);
|
|
/* struct bnxt_func_info *func = &softc->func; */
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_FUNC_QCAPS);
|
|
req.fid = htole16(0xffff);
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc)
|
|
goto fail;
|
|
|
|
if (resp->flags &
|
|
htole32(HWRM_FUNC_QCAPS_OUTPUT_FLAGS_WOL_MAGICPKT_SUPPORTED))
|
|
softc->sc_flags |= BNXT_FLAG_WOL_CAP;
|
|
|
|
memcpy(softc->sc_ac.ac_enaddr, resp->mac_address, 6);
|
|
/*
|
|
func->fw_fid = le16toh(resp->fid);
|
|
memcpy(func->mac_addr, resp->mac_address, ETHER_ADDR_LEN);
|
|
func->max_rsscos_ctxs = le16toh(resp->max_rsscos_ctx);
|
|
func->max_cp_rings = le16toh(resp->max_cmpl_rings);
|
|
func->max_tx_rings = le16toh(resp->max_tx_rings);
|
|
func->max_rx_rings = le16toh(resp->max_rx_rings);
|
|
func->max_hw_ring_grps = le32toh(resp->max_hw_ring_grps);
|
|
if (!func->max_hw_ring_grps)
|
|
func->max_hw_ring_grps = func->max_tx_rings;
|
|
func->max_l2_ctxs = le16toh(resp->max_l2_ctxs);
|
|
func->max_vnics = le16toh(resp->max_vnics);
|
|
func->max_stat_ctxs = le16toh(resp->max_stat_ctx);
|
|
if (BNXT_PF(softc)) {
|
|
struct bnxt_pf_info *pf = &softc->pf;
|
|
|
|
pf->port_id = le16toh(resp->port_id);
|
|
pf->first_vf_id = le16toh(resp->first_vf_id);
|
|
pf->max_vfs = le16toh(resp->max_vfs);
|
|
pf->max_encap_records = le32toh(resp->max_encap_records);
|
|
pf->max_decap_records = le32toh(resp->max_decap_records);
|
|
pf->max_tx_em_flows = le32toh(resp->max_tx_em_flows);
|
|
pf->max_tx_wm_flows = le32toh(resp->max_tx_wm_flows);
|
|
pf->max_rx_em_flows = le32toh(resp->max_rx_em_flows);
|
|
pf->max_rx_wm_flows = le32toh(resp->max_rx_wm_flows);
|
|
}
|
|
if (!_is_valid_ether_addr(func->mac_addr)) {
|
|
device_printf(softc->dev, "Invalid ethernet address, generating random locally administered address\n");
|
|
get_random_ether_addr(func->mac_addr);
|
|
}
|
|
*/
|
|
|
|
fail:
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
return rc;
|
|
}
|
|
|
|
|
|
int
|
|
bnxt_hwrm_func_qcfg(struct bnxt_softc *softc)
|
|
{
|
|
struct hwrm_func_qcfg_input req = {0};
|
|
/* struct hwrm_func_qcfg_output *resp =
|
|
BNXT_DMA_KVA(softc->sc_cmd_resp);
|
|
struct bnxt_func_qcfg *fn_qcfg = &softc->fn_qcfg; */
|
|
int rc;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_FUNC_QCFG);
|
|
req.fid = htole16(0xffff);
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc)
|
|
goto fail;
|
|
|
|
/*
|
|
fn_qcfg->alloc_completion_rings = le16toh(resp->alloc_cmpl_rings);
|
|
fn_qcfg->alloc_tx_rings = le16toh(resp->alloc_tx_rings);
|
|
fn_qcfg->alloc_rx_rings = le16toh(resp->alloc_rx_rings);
|
|
fn_qcfg->alloc_vnics = le16toh(resp->alloc_vnics);
|
|
*/
|
|
fail:
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
return rc;
|
|
}
|
|
|
|
|
|
int
|
|
bnxt_hwrm_func_reset(struct bnxt_softc *softc)
|
|
{
|
|
struct hwrm_func_reset_input req = {0};
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_FUNC_RESET);
|
|
req.enables = 0;
|
|
|
|
return hwrm_send_message(softc, &req, sizeof(req));
|
|
}
|
|
|
|
int
|
|
bnxt_hwrm_vnic_cfg_placement(struct bnxt_softc *softc,
|
|
struct bnxt_vnic_info *vnic)
|
|
{
|
|
struct hwrm_vnic_plcmodes_cfg_input req = {0};
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_VNIC_PLCMODES_CFG);
|
|
|
|
req.flags = htole32(
|
|
HWRM_VNIC_PLCMODES_CFG_INPUT_FLAGS_JUMBO_PLACEMENT);
|
|
req.enables = htole32(
|
|
HWRM_VNIC_PLCMODES_CFG_INPUT_ENABLES_JUMBO_THRESH_VALID);
|
|
req.vnic_id = htole16(vnic->id);
|
|
req.jumbo_thresh = htole16(MCLBYTES);
|
|
|
|
return hwrm_send_message(softc, &req, sizeof(req));
|
|
}
|
|
|
|
int
|
|
bnxt_hwrm_vnic_cfg(struct bnxt_softc *softc, struct bnxt_vnic_info *vnic)
|
|
{
|
|
struct hwrm_vnic_cfg_input req = {0};
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_VNIC_CFG);
|
|
|
|
if (vnic->flags & BNXT_VNIC_FLAG_DEFAULT)
|
|
req.flags |= htole32(HWRM_VNIC_CFG_INPUT_FLAGS_DEFAULT);
|
|
if (vnic->flags & BNXT_VNIC_FLAG_BD_STALL)
|
|
req.flags |= htole32(HWRM_VNIC_CFG_INPUT_FLAGS_BD_STALL_MODE);
|
|
if (vnic->flags & BNXT_VNIC_FLAG_VLAN_STRIP)
|
|
req.flags |= htole32(HWRM_VNIC_CFG_INPUT_FLAGS_VLAN_STRIP_MODE);
|
|
req.enables = htole32(HWRM_VNIC_CFG_INPUT_ENABLES_DFLT_RING_GRP |
|
|
HWRM_VNIC_CFG_INPUT_ENABLES_RSS_RULE |
|
|
HWRM_VNIC_CFG_INPUT_ENABLES_MRU);
|
|
req.vnic_id = htole16(vnic->id);
|
|
req.dflt_ring_grp = htole16(vnic->def_ring_grp);
|
|
req.rss_rule = htole16(vnic->rss_id);
|
|
req.cos_rule = htole16(vnic->cos_rule);
|
|
req.lb_rule = htole16(vnic->lb_rule);
|
|
req.mru = htole16(vnic->mru);
|
|
|
|
return hwrm_send_message(softc, &req, sizeof(req));
|
|
}
|
|
|
|
int
|
|
bnxt_hwrm_vnic_alloc(struct bnxt_softc *softc, struct bnxt_vnic_info *vnic)
|
|
{
|
|
struct hwrm_vnic_alloc_input req = {0};
|
|
struct hwrm_vnic_alloc_output *resp =
|
|
BNXT_DMA_KVA(softc->sc_cmd_resp);
|
|
int rc;
|
|
|
|
if (vnic->id != (uint16_t)HWRM_NA_SIGNATURE) {
|
|
printf("%s: attempt to re-allocate vnic %04x\n",
|
|
DEVNAME(softc), vnic->id);
|
|
return EINVAL;
|
|
}
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_VNIC_ALLOC);
|
|
|
|
if (vnic->flags & BNXT_VNIC_FLAG_DEFAULT)
|
|
req.flags = htole32(HWRM_VNIC_ALLOC_INPUT_FLAGS_DEFAULT);
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc)
|
|
goto fail;
|
|
|
|
vnic->id = le32toh(resp->vnic_id);
|
|
|
|
fail:
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
bnxt_hwrm_vnic_free(struct bnxt_softc *softc, struct bnxt_vnic_info *vnic)
|
|
{
|
|
struct hwrm_vnic_free_input req = {0};
|
|
int rc;
|
|
|
|
if (vnic->id == (uint16_t)HWRM_NA_SIGNATURE) {
|
|
printf("%s: attempt to deallocate vnic %04x\n",
|
|
DEVNAME(softc), vnic->id);
|
|
return (EINVAL);
|
|
}
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_VNIC_FREE);
|
|
req.vnic_id = htole16(vnic->id);
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc == 0)
|
|
vnic->id = (uint16_t)HWRM_NA_SIGNATURE;
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
|
|
return (rc);
|
|
}
|
|
|
|
int
|
|
bnxt_hwrm_vnic_ctx_alloc(struct bnxt_softc *softc, uint16_t *ctx_id)
|
|
{
|
|
struct hwrm_vnic_rss_cos_lb_ctx_alloc_input req = {0};
|
|
struct hwrm_vnic_rss_cos_lb_ctx_alloc_output *resp =
|
|
BNXT_DMA_KVA(softc->sc_cmd_resp);
|
|
int rc;
|
|
|
|
if (*ctx_id != (uint16_t)HWRM_NA_SIGNATURE) {
|
|
printf("%s: attempt to re-allocate vnic ctx %04x\n",
|
|
DEVNAME(softc), *ctx_id);
|
|
return EINVAL;
|
|
}
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_VNIC_RSS_COS_LB_CTX_ALLOC);
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc)
|
|
goto fail;
|
|
|
|
*ctx_id = letoh16(resp->rss_cos_lb_ctx_id);
|
|
|
|
fail:
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
return (rc);
|
|
}
|
|
|
|
int
|
|
bnxt_hwrm_vnic_ctx_free(struct bnxt_softc *softc, uint16_t *ctx_id)
|
|
{
|
|
struct hwrm_vnic_rss_cos_lb_ctx_free_input req = {0};
|
|
int rc;
|
|
|
|
if (*ctx_id == (uint16_t)HWRM_NA_SIGNATURE) {
|
|
printf("%s: attempt to deallocate vnic ctx %04x\n",
|
|
DEVNAME(softc), *ctx_id);
|
|
return (EINVAL);
|
|
}
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_VNIC_RSS_COS_LB_CTX_FREE);
|
|
req.rss_cos_lb_ctx_id = htole32(*ctx_id);
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc == 0)
|
|
*ctx_id = (uint16_t)HWRM_NA_SIGNATURE;
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
return (rc);
|
|
}
|
|
|
|
int
|
|
bnxt_hwrm_ring_grp_alloc(struct bnxt_softc *softc, struct bnxt_grp_info *grp)
|
|
{
|
|
struct hwrm_ring_grp_alloc_input req = {0};
|
|
struct hwrm_ring_grp_alloc_output *resp;
|
|
int rc = 0;
|
|
|
|
if (grp->grp_id != HWRM_NA_SIGNATURE) {
|
|
printf("%s: attempt to re-allocate ring group %04x\n",
|
|
DEVNAME(softc), grp->grp_id);
|
|
return EINVAL;
|
|
}
|
|
|
|
resp = BNXT_DMA_KVA(softc->sc_cmd_resp);
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_RING_GRP_ALLOC);
|
|
req.cr = htole16(grp->cp_ring_id);
|
|
req.rr = htole16(grp->rx_ring_id);
|
|
req.ar = htole16(grp->ag_ring_id);
|
|
req.sc = htole16(grp->stats_ctx);
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc)
|
|
goto fail;
|
|
|
|
grp->grp_id = letoh32(resp->ring_group_id);
|
|
|
|
fail:
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
bnxt_hwrm_ring_grp_free(struct bnxt_softc *softc, struct bnxt_grp_info *grp)
|
|
{
|
|
struct hwrm_ring_grp_free_input req = {0};
|
|
int rc = 0;
|
|
|
|
if (grp->grp_id == HWRM_NA_SIGNATURE) {
|
|
printf("%s: attempt to free ring group %04x\n",
|
|
DEVNAME(softc), grp->grp_id);
|
|
return EINVAL;
|
|
}
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_RING_GRP_FREE);
|
|
req.ring_group_id = htole32(grp->grp_id);
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc == 0)
|
|
grp->grp_id = HWRM_NA_SIGNATURE;
|
|
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
return (rc);
|
|
}
|
|
|
|
/*
|
|
* Ring allocation message to the firmware
|
|
*/
|
|
int
|
|
bnxt_hwrm_ring_alloc(struct bnxt_softc *softc, uint8_t type,
|
|
struct bnxt_ring *ring, uint16_t cmpl_ring_id, uint32_t stat_ctx_id,
|
|
int irq)
|
|
{
|
|
struct hwrm_ring_alloc_input req = {0};
|
|
struct hwrm_ring_alloc_output *resp;
|
|
int rc;
|
|
|
|
if (ring->phys_id != (uint16_t)HWRM_NA_SIGNATURE) {
|
|
printf("%s: attempt to re-allocate ring %04x\n",
|
|
DEVNAME(softc), ring->phys_id);
|
|
return EINVAL;
|
|
}
|
|
|
|
resp = BNXT_DMA_KVA(softc->sc_cmd_resp);
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_RING_ALLOC);
|
|
req.enables = htole32(0);
|
|
req.fbo = htole32(0);
|
|
|
|
if (stat_ctx_id != HWRM_NA_SIGNATURE) {
|
|
req.enables |= htole32(
|
|
HWRM_RING_ALLOC_INPUT_ENABLES_STAT_CTX_ID_VALID);
|
|
req.stat_ctx_id = htole32(stat_ctx_id);
|
|
}
|
|
req.ring_type = type;
|
|
req.page_tbl_addr = htole64(ring->paddr);
|
|
req.length = htole32(ring->ring_size);
|
|
req.logical_id = htole16(ring->id);
|
|
req.cmpl_ring_id = htole16(cmpl_ring_id);
|
|
req.queue_id = htole16(softc->sc_tx_queue_id);
|
|
req.int_mode = (softc->sc_flags & BNXT_FLAG_MSIX) ?
|
|
HWRM_RING_ALLOC_INPUT_INT_MODE_MSIX :
|
|
HWRM_RING_ALLOC_INPUT_INT_MODE_LEGACY;
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc)
|
|
goto fail;
|
|
|
|
ring->phys_id = le16toh(resp->ring_id);
|
|
|
|
fail:
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
bnxt_hwrm_ring_free(struct bnxt_softc *softc, uint8_t type, struct bnxt_ring *ring)
|
|
{
|
|
struct hwrm_ring_free_input req = {0};
|
|
int rc;
|
|
|
|
if (ring->phys_id == (uint16_t)HWRM_NA_SIGNATURE) {
|
|
printf("%s: attempt to deallocate ring %04x\n",
|
|
DEVNAME(softc), ring->phys_id);
|
|
return (EINVAL);
|
|
}
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_RING_FREE);
|
|
req.ring_type = type;
|
|
req.ring_id = htole16(ring->phys_id);
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc)
|
|
goto fail;
|
|
|
|
ring->phys_id = (uint16_t)HWRM_NA_SIGNATURE;
|
|
fail:
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
return (rc);
|
|
}
|
|
|
|
|
|
int
|
|
bnxt_hwrm_stat_ctx_alloc(struct bnxt_softc *softc, struct bnxt_cp_ring *cpr,
|
|
uint64_t paddr)
|
|
{
|
|
struct hwrm_stat_ctx_alloc_input req = {0};
|
|
struct hwrm_stat_ctx_alloc_output *resp;
|
|
int rc = 0;
|
|
|
|
if (cpr->stats_ctx_id != HWRM_NA_SIGNATURE) {
|
|
printf("%s: attempt to re-allocate stats ctx %08x\n",
|
|
DEVNAME(softc), cpr->stats_ctx_id);
|
|
return EINVAL;
|
|
}
|
|
|
|
resp = BNXT_DMA_KVA(softc->sc_cmd_resp);
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_STAT_CTX_ALLOC);
|
|
|
|
req.update_period_ms = htole32(1000);
|
|
req.stats_dma_addr = htole64(paddr);
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc)
|
|
goto fail;
|
|
|
|
cpr->stats_ctx_id = le32toh(resp->stat_ctx_id);
|
|
|
|
fail:
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
bnxt_hwrm_stat_ctx_free(struct bnxt_softc *softc, struct bnxt_cp_ring *cpr)
|
|
{
|
|
struct hwrm_stat_ctx_free_input req = {0};
|
|
int rc = 0;
|
|
|
|
if (cpr->stats_ctx_id == HWRM_NA_SIGNATURE) {
|
|
printf("%s: attempt to free stats ctx %08x\n",
|
|
DEVNAME(softc), cpr->stats_ctx_id);
|
|
return EINVAL;
|
|
}
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_STAT_CTX_FREE);
|
|
req.stat_ctx_id = htole32(cpr->stats_ctx_id);
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
|
|
if (rc == 0)
|
|
cpr->stats_ctx_id = HWRM_NA_SIGNATURE;
|
|
|
|
return (rc);
|
|
}
|
|
|
|
#if 0
|
|
|
|
int
|
|
bnxt_hwrm_port_qstats(struct bnxt_softc *softc)
|
|
{
|
|
struct hwrm_port_qstats_input req = {0};
|
|
int rc = 0;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_PORT_QSTATS);
|
|
|
|
req.port_id = htole16(softc->pf.port_id);
|
|
req.rx_stat_host_addr = htole64(softc->hw_rx_port_stats.idi_paddr);
|
|
req.tx_stat_host_addr = htole64(softc->hw_tx_port_stats.idi_paddr);
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
|
|
return rc;
|
|
}
|
|
|
|
#endif
|
|
|
|
int
|
|
bnxt_hwrm_cfa_l2_set_rx_mask(struct bnxt_softc *softc,
|
|
uint32_t vnic_id, uint32_t rx_mask, uint64_t mc_addr, uint32_t mc_count)
|
|
{
|
|
struct hwrm_cfa_l2_set_rx_mask_input req = {0};
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_CFA_L2_SET_RX_MASK);
|
|
|
|
req.vnic_id = htole32(vnic_id);
|
|
req.mask = htole32(rx_mask);
|
|
req.mc_tbl_addr = htole64(mc_addr);
|
|
req.num_mc_entries = htole32(mc_count);
|
|
return hwrm_send_message(softc, &req, sizeof(req));
|
|
}
|
|
|
|
int
|
|
bnxt_hwrm_set_filter(struct bnxt_softc *softc, struct bnxt_vnic_info *vnic)
|
|
{
|
|
struct hwrm_cfa_l2_filter_alloc_input req = {0};
|
|
struct hwrm_cfa_l2_filter_alloc_output *resp;
|
|
uint32_t enables = 0;
|
|
int rc = 0;
|
|
|
|
if (vnic->filter_id != -1) {
|
|
printf("%s: attempt to re-allocate l2 ctx filter\n",
|
|
DEVNAME(softc));
|
|
return EINVAL;
|
|
}
|
|
|
|
resp = BNXT_DMA_KVA(softc->sc_cmd_resp);
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_CFA_L2_FILTER_ALLOC);
|
|
|
|
req.flags = htole32(HWRM_CFA_L2_FILTER_ALLOC_INPUT_FLAGS_PATH_RX
|
|
| HWRM_CFA_L2_FILTER_ALLOC_INPUT_FLAGS_OUTERMOST);
|
|
enables = HWRM_CFA_L2_FILTER_ALLOC_INPUT_ENABLES_L2_ADDR
|
|
| HWRM_CFA_L2_FILTER_ALLOC_INPUT_ENABLES_L2_ADDR_MASK
|
|
| HWRM_CFA_L2_FILTER_ALLOC_INPUT_ENABLES_DST_ID;
|
|
req.enables = htole32(enables);
|
|
req.dst_id = htole16(vnic->id);
|
|
memcpy(req.l2_addr, softc->sc_ac.ac_enaddr, ETHER_ADDR_LEN);
|
|
memset(&req.l2_addr_mask, 0xff, sizeof(req.l2_addr_mask));
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc)
|
|
goto fail;
|
|
|
|
vnic->filter_id = le64toh(resp->l2_filter_id);
|
|
vnic->flow_id = le64toh(resp->flow_id);
|
|
|
|
fail:
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
return (rc);
|
|
}
|
|
|
|
int
|
|
bnxt_hwrm_free_filter(struct bnxt_softc *softc, struct bnxt_vnic_info *vnic)
|
|
{
|
|
struct hwrm_cfa_l2_filter_free_input req = {0};
|
|
int rc = 0;
|
|
|
|
if (vnic->filter_id == -1) {
|
|
printf("%s: attempt to deallocate filter %llx\n",
|
|
DEVNAME(softc), vnic->filter_id);
|
|
return (EINVAL);
|
|
}
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_CFA_L2_FILTER_FREE);
|
|
req.l2_filter_id = htole64(vnic->filter_id);
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc == 0)
|
|
vnic->filter_id = -1;
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
|
|
return (rc);
|
|
}
|
|
|
|
|
|
int
|
|
bnxt_hwrm_vnic_rss_cfg(struct bnxt_softc *softc, struct bnxt_vnic_info *vnic,
|
|
uint32_t hash_type, daddr_t rss_table, daddr_t rss_key)
|
|
{
|
|
struct hwrm_vnic_rss_cfg_input req = {0};
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_VNIC_RSS_CFG);
|
|
|
|
req.hash_type = htole32(hash_type);
|
|
req.ring_grp_tbl_addr = htole64(rss_table);
|
|
req.hash_key_tbl_addr = htole64(rss_key);
|
|
req.rss_ctx_idx = htole16(vnic->rss_id);
|
|
|
|
return hwrm_send_message(softc, &req, sizeof(req));
|
|
}
|
|
|
|
int
|
|
bnxt_cfg_async_cr(struct bnxt_softc *softc, struct bnxt_cp_ring *cpr)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (1 /* BNXT_PF(softc) */) {
|
|
struct hwrm_func_cfg_input req = {0};
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_FUNC_CFG);
|
|
|
|
req.fid = htole16(0xffff);
|
|
req.enables = htole32(HWRM_FUNC_CFG_INPUT_ENABLES_ASYNC_EVENT_CR);
|
|
req.async_event_cr = htole16(cpr->ring.phys_id);
|
|
|
|
rc = hwrm_send_message(softc, &req, sizeof(req));
|
|
} else {
|
|
struct hwrm_func_vf_cfg_input req = {0};
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_FUNC_VF_CFG);
|
|
|
|
req.enables = htole32(HWRM_FUNC_VF_CFG_INPUT_ENABLES_ASYNC_EVENT_CR);
|
|
req.async_event_cr = htole16(cpr->ring.phys_id);
|
|
|
|
rc = hwrm_send_message(softc, &req, sizeof(req));
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
#if 0
|
|
|
|
void
|
|
bnxt_validate_hw_lro_settings(struct bnxt_softc *softc)
|
|
{
|
|
softc->hw_lro.enable = min(softc->hw_lro.enable, 1);
|
|
|
|
softc->hw_lro.is_mode_gro = min(softc->hw_lro.is_mode_gro, 1);
|
|
|
|
softc->hw_lro.max_agg_segs = min(softc->hw_lro.max_agg_segs,
|
|
HWRM_VNIC_TPA_CFG_INPUT_MAX_AGG_SEGS_MAX);
|
|
|
|
softc->hw_lro.max_aggs = min(softc->hw_lro.max_aggs,
|
|
HWRM_VNIC_TPA_CFG_INPUT_MAX_AGGS_MAX);
|
|
|
|
softc->hw_lro.min_agg_len = min(softc->hw_lro.min_agg_len, BNXT_MAX_MTU);
|
|
}
|
|
|
|
int
|
|
bnxt_hwrm_vnic_tpa_cfg(struct bnxt_softc *softc)
|
|
{
|
|
struct hwrm_vnic_tpa_cfg_input req = {0};
|
|
uint32_t flags;
|
|
|
|
if (softc->vnic_info.id == (uint16_t) HWRM_NA_SIGNATURE) {
|
|
return 0;
|
|
}
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_VNIC_TPA_CFG);
|
|
|
|
if (softc->hw_lro.enable) {
|
|
flags = HWRM_VNIC_TPA_CFG_INPUT_FLAGS_TPA |
|
|
HWRM_VNIC_TPA_CFG_INPUT_FLAGS_ENCAP_TPA |
|
|
HWRM_VNIC_TPA_CFG_INPUT_FLAGS_AGG_WITH_ECN |
|
|
HWRM_VNIC_TPA_CFG_INPUT_FLAGS_AGG_WITH_SAME_GRE_SEQ;
|
|
|
|
if (softc->hw_lro.is_mode_gro)
|
|
flags |= HWRM_VNIC_TPA_CFG_INPUT_FLAGS_GRO;
|
|
else
|
|
flags |= HWRM_VNIC_TPA_CFG_INPUT_FLAGS_RSC_WND_UPDATE;
|
|
|
|
req.flags = htole32(flags);
|
|
|
|
req.enables = htole32(HWRM_VNIC_TPA_CFG_INPUT_ENABLES_MAX_AGG_SEGS |
|
|
HWRM_VNIC_TPA_CFG_INPUT_ENABLES_MAX_AGGS |
|
|
HWRM_VNIC_TPA_CFG_INPUT_ENABLES_MIN_AGG_LEN);
|
|
|
|
req.max_agg_segs = htole16(softc->hw_lro.max_agg_segs);
|
|
req.max_aggs = htole16(softc->hw_lro.max_aggs);
|
|
req.min_agg_len = htole32(softc->hw_lro.min_agg_len);
|
|
}
|
|
|
|
req.vnic_id = htole16(softc->vnic_info.id);
|
|
|
|
return hwrm_send_message(softc, &req, sizeof(req));
|
|
}
|
|
|
|
|
|
int
|
|
bnxt_hwrm_fw_reset(struct bnxt_softc *softc, uint8_t processor,
|
|
uint8_t *selfreset)
|
|
{
|
|
struct hwrm_fw_reset_input req = {0};
|
|
struct hwrm_fw_reset_output *resp =
|
|
(void *)softc->hwrm_cmd_resp.idi_vaddr;
|
|
int rc;
|
|
|
|
MPASS(selfreset);
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_FW_RESET);
|
|
req.embedded_proc_type = processor;
|
|
req.selfrst_status = *selfreset;
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc)
|
|
goto exit;
|
|
*selfreset = resp->selfrst_status;
|
|
|
|
exit:
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
bnxt_hwrm_fw_qstatus(struct bnxt_softc *softc, uint8_t type, uint8_t *selfreset)
|
|
{
|
|
struct hwrm_fw_qstatus_input req = {0};
|
|
struct hwrm_fw_qstatus_output *resp =
|
|
(void *)softc->hwrm_cmd_resp.idi_vaddr;
|
|
int rc;
|
|
|
|
MPASS(selfreset);
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_FW_QSTATUS);
|
|
req.embedded_proc_type = type;
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc)
|
|
goto exit;
|
|
*selfreset = resp->selfrst_status;
|
|
|
|
exit:
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
return rc;
|
|
}
|
|
|
|
#endif
|
|
|
|
int
|
|
bnxt_hwrm_nvm_get_dev_info(struct bnxt_softc *softc, uint16_t *mfg_id,
|
|
uint16_t *device_id, uint32_t *sector_size, uint32_t *nvram_size,
|
|
uint32_t *reserved_size, uint32_t *available_size)
|
|
{
|
|
struct hwrm_nvm_get_dev_info_input req = {0};
|
|
struct hwrm_nvm_get_dev_info_output *resp =
|
|
BNXT_DMA_KVA(softc->sc_cmd_resp);
|
|
int rc;
|
|
uint32_t old_timeo;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_NVM_GET_DEV_INFO);
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
old_timeo = softc->sc_cmd_timeo;
|
|
softc->sc_cmd_timeo = BNXT_NVM_TIMEO;
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
softc->sc_cmd_timeo = old_timeo;
|
|
if (rc)
|
|
goto exit;
|
|
|
|
if (mfg_id)
|
|
*mfg_id = le16toh(resp->manufacturer_id);
|
|
if (device_id)
|
|
*device_id = le16toh(resp->device_id);
|
|
if (sector_size)
|
|
*sector_size = le32toh(resp->sector_size);
|
|
if (nvram_size)
|
|
*nvram_size = le32toh(resp->nvram_size);
|
|
if (reserved_size)
|
|
*reserved_size = le32toh(resp->reserved_size);
|
|
if (available_size)
|
|
*available_size = le32toh(resp->available_size);
|
|
|
|
exit:
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
return rc;
|
|
}
|
|
|
|
#if 0
|
|
|
|
int
|
|
bnxt_hwrm_fw_get_time(struct bnxt_softc *softc, uint16_t *year, uint8_t *month,
|
|
uint8_t *day, uint8_t *hour, uint8_t *minute, uint8_t *second,
|
|
uint16_t *millisecond, uint16_t *zone)
|
|
{
|
|
struct hwrm_fw_get_time_input req = {0};
|
|
struct hwrm_fw_get_time_output *resp =
|
|
(void *)softc->hwrm_cmd_resp.idi_vaddr;
|
|
int rc;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_FW_GET_TIME);
|
|
|
|
BNXT_HWRM_LOCK(softc);
|
|
rc = _hwrm_send_message(softc, &req, sizeof(req));
|
|
if (rc)
|
|
goto exit;
|
|
|
|
if (year)
|
|
*year = le16toh(resp->year);
|
|
if (month)
|
|
*month = resp->month;
|
|
if (day)
|
|
*day = resp->day;
|
|
if (hour)
|
|
*hour = resp->hour;
|
|
if (minute)
|
|
*minute = resp->minute;
|
|
if (second)
|
|
*second = resp->second;
|
|
if (millisecond)
|
|
*millisecond = le16toh(resp->millisecond);
|
|
if (zone)
|
|
*zone = le16toh(resp->zone);
|
|
|
|
exit:
|
|
BNXT_HWRM_UNLOCK(softc);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
bnxt_hwrm_fw_set_time(struct bnxt_softc *softc, uint16_t year, uint8_t month,
|
|
uint8_t day, uint8_t hour, uint8_t minute, uint8_t second,
|
|
uint16_t millisecond, uint16_t zone)
|
|
{
|
|
struct hwrm_fw_set_time_input req = {0};
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_FW_SET_TIME);
|
|
|
|
req.year = htole16(year);
|
|
req.month = month;
|
|
req.day = day;
|
|
req.hour = hour;
|
|
req.minute = minute;
|
|
req.second = second;
|
|
req.millisecond = htole16(millisecond);
|
|
req.zone = htole16(zone);
|
|
return hwrm_send_message(softc, &req, sizeof(req));
|
|
}
|
|
|
|
#endif
|
|
|
|
void
|
|
_bnxt_hwrm_set_async_event_bit(struct hwrm_func_drv_rgtr_input *req, int bit)
|
|
{
|
|
req->async_event_fwd[bit/32] |= (1 << (bit % 32));
|
|
}
|
|
|
|
int bnxt_hwrm_func_rgtr_async_events(struct bnxt_softc *softc)
|
|
{
|
|
struct hwrm_func_drv_rgtr_input req = {0};
|
|
int events[] = {
|
|
HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE,
|
|
HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_UNLOAD,
|
|
HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PORT_CONN_NOT_ALLOWED,
|
|
HWRM_ASYNC_EVENT_CMPL_EVENT_ID_VF_CFG_CHANGE,
|
|
HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CFG_CHANGE
|
|
};
|
|
int i;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_FUNC_DRV_RGTR);
|
|
|
|
req.enables =
|
|
htole32(HWRM_FUNC_DRV_RGTR_INPUT_ENABLES_ASYNC_EVENT_FWD);
|
|
|
|
for (i = 0; i < nitems(events); i++)
|
|
_bnxt_hwrm_set_async_event_bit(&req, events[i]);
|
|
|
|
return hwrm_send_message(softc, &req, sizeof(req));
|
|
}
|
|
|
|
int
|
|
bnxt_get_sffpage(struct bnxt_softc *softc, struct if_sffpage *sff)
|
|
{
|
|
struct hwrm_port_phy_i2c_read_input req;
|
|
struct hwrm_port_phy_i2c_read_output *out;
|
|
int offset;
|
|
|
|
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_PORT_PHY_I2C_READ);
|
|
req.i2c_slave_addr = sff->sff_addr;
|
|
req.page_number = htole16(sff->sff_page);
|
|
|
|
for (offset = 0; offset < 256; offset += sizeof(out->data)) {
|
|
req.page_offset = htole16(offset);
|
|
req.data_length = sizeof(out->data);
|
|
req.enables = htole32(HWRM_PORT_PHY_I2C_READ_REQ_ENABLES_PAGE_OFFSET);
|
|
|
|
if (hwrm_send_message(softc, &req, sizeof(req))) {
|
|
printf("%s: failed to read i2c data\n", DEVNAME(softc));
|
|
return 1;
|
|
}
|
|
|
|
out = (struct hwrm_port_phy_i2c_read_output *)
|
|
BNXT_DMA_KVA(softc->sc_cmd_resp);
|
|
memcpy(sff->sff_data + offset, out->data, sizeof(out->data));
|
|
}
|
|
|
|
return 0;
|
|
}
|