/* $OpenBSD: if_bnxt.c,v 1.17 2018/09/19 10:26:17 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 * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #define __FBSDID(x) #include #include #include #if NBPFILTER > 0 #include #endif #include #include #define BNXT_HWRM_BAR 0x10 #define BNXT_DOORBELL_BAR 0x18 #define BNXT_RX_RING_ID 0 #define BNXT_AG_RING_ID 1 #define BNXT_TX_RING_ID 3 #define BNXT_MAX_QUEUE 8 #define BNXT_MAX_MTU 9000 #define BNXT_AG_BUFFER_SIZE 8192 #define BNXT_CP_PAGES 4 #define BNXT_MAX_TX_SEGS 32 /* a bit much? */ #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 /* 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_cos_queue { uint8_t id; uint8_t profile; }; 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_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; /* rss things */ }; 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_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_max_tc; struct bnxt_cos_queue sc_q_info[BNXT_MAX_QUEUE]; struct bnxt_vnic_info sc_vnic; struct bnxt_dmamem *sc_stats_ctx_mem; struct bnxt_cp_ring sc_cp_ring; struct bnxt_dmamem *sc_cp_ring_mem; /* rx */ struct bnxt_dmamem *sc_rx_ring_mem; /* rx and ag */ struct bnxt_dmamem *sc_rx_mcast; struct bnxt_ring sc_rx_ring; struct bnxt_ring sc_rx_ag_ring; struct bnxt_grp_info sc_ring_group; struct if_rxring sc_rxr[2]; struct bnxt_slot *sc_rx_slots; struct bnxt_slot *sc_rx_ag_slots; int sc_rx_prod; int sc_rx_cons; int sc_rx_ag_prod; int sc_rx_ag_cons; struct timeout sc_rx_refill; /* tx */ struct bnxt_dmamem *sc_tx_ring_mem; struct bnxt_ring sc_tx_ring; struct bnxt_slot *sc_tx_slots; int sc_tx_prod; int sc_tx_cons; int sc_tx_ring_prod; int sc_tx_ring_cons; }; #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_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_softc *); 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_cp_ring *, struct mbuf_list *, int *, int *, struct cmpl_base *); void bnxt_txeof(struct bnxt_softc *, int *, struct cmpl_base *); 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_cfg_async_cr(struct bnxt_softc *); 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 *); /* 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_rss_cfg(struct bnxt_softc *softc, struct bnxt_vnic_info *vnic, uint32_t hash_type); 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 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_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 hwrm_ring_cmpl_ring_cfg_aggint_params_input aggint; struct ifnet *ifp = &sc->sc_ac.ac_if; struct pci_attach_args *pa = aux; pci_intr_handle_t ih; const char *intrstr; u_int memtype; /* enable busmaster? */ 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_msi(pa, &ih) != 0 && */ pci_intr_map(pa, &ih) != 0) { printf(": unable to map interrupt\n"); goto free_resp; } 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, DEVNAME(sc)); if (sc->sc_ih == NULL) { printf(": unable to establish interrupt"); if (intrstr != NULL) printf(" at %s", intrstr); printf("\n"); goto deintr; } printf("%s, address %s\n", intrstr, 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; } sc->sc_cp_ring.stats_ctx_id = HWRM_NA_SIGNATURE; sc->sc_cp_ring.ring.phys_id = (uint16_t)HWRM_NA_SIGNATURE; sc->sc_cp_ring.softc = sc; sc->sc_cp_ring.ring.id = 0; sc->sc_cp_ring.ring.doorbell = sc->sc_cp_ring.ring.id * 0x80; sc->sc_cp_ring.ring.ring_size = (PAGE_SIZE * BNXT_CP_PAGES) / sizeof(struct cmpl_base); sc->sc_cp_ring_mem = bnxt_dmamem_alloc(sc, PAGE_SIZE * BNXT_CP_PAGES); if (sc->sc_cp_ring_mem == NULL) { printf("%s: failed to allocate completion queue memory\n", DEVNAME(sc)); goto deintr; } sc->sc_cp_ring.ring.vaddr = BNXT_DMA_KVA(sc->sc_cp_ring_mem); sc->sc_cp_ring.ring.paddr = BNXT_DMA_DVA(sc->sc_cp_ring_mem); sc->sc_cp_ring.cons = UINT32_MAX; sc->sc_cp_ring.v_bit = 1; bnxt_mark_cpr_invalid(&sc->sc_cp_ring); if (bnxt_hwrm_ring_alloc(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_L2_CMPL, &sc->sc_cp_ring.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) != 0) { printf("%s: failed to set async completion ring\n", DEVNAME(sc)); goto free_cp_mem; } bnxt_write_cp_doorbell(sc, &sc->sc_cp_ring.ring, 1); /* * 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(sc->sc_cp_ring.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); if (hwrm_send_message(sc, &aggint, sizeof(aggint))) 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; /* ? */ /* checksum flags, hwtagging? */ IFQ_SET_MAXLEN(&ifp->if_snd, 1024); /* ? */ ifmedia_init(&sc->sc_media, IFM_IMASK, bnxt_media_change, bnxt_media_status); if_attach(ifp); ether_ifattach(ifp); timeout_set(&sc->sc_rx_refill, bnxt_refill, sc); bnxt_media_autonegotiate(sc); bnxt_hwrm_port_phy_qcfg(sc, NULL); return; free_cp_mem: bnxt_dmamem_free(sc, sc->sc_cp_ring_mem); deintr: 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_hwrm_t, sc->sc_hwrm_h, sc->sc_hwrm_s); sc->sc_hwrm_s = 0; unmap_1: bus_space_unmap(sc->sc_db_t, sc->sc_db_h, sc->sc_db_s); sc->sc_db_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); } free(slots, M_DEVBUF, total * sizeof(*bs)); } void bnxt_up(struct bnxt_softc *sc) { struct ifnet *ifp = &sc->sc_ac.ac_if; struct bnxt_slot *bs; int i; sc->sc_stats_ctx_mem = bnxt_dmamem_alloc(sc, sizeof(struct ctx_hw_stats)); if (sc->sc_stats_ctx_mem == NULL) { printf("%s: failed to allocate stats contexts\n", DEVNAME(sc)); return; } sc->sc_tx_ring_mem = bnxt_dmamem_alloc(sc, PAGE_SIZE); if (sc->sc_tx_ring_mem == NULL) { printf("%s: failed to allocate tx ring\n", DEVNAME(sc)); goto free_stats; } sc->sc_rx_ring_mem = bnxt_dmamem_alloc(sc, PAGE_SIZE * 2); if (sc->sc_rx_ring_mem == NULL) { printf("%s: failed to allocate rx ring\n", DEVNAME(sc)); goto free_tx; } sc->sc_rx_mcast = bnxt_dmamem_alloc(sc, PAGE_SIZE); if (sc->sc_rx_mcast == NULL) { printf("%s: failed to allocate multicast address table\n", DEVNAME(sc)); goto free_rx; } if (bnxt_hwrm_stat_ctx_alloc(sc, &sc->sc_cp_ring, BNXT_DMA_DVA(sc->sc_stats_ctx_mem)) != 0) { printf("%s: failed to set up stats context\n", DEVNAME(sc)); goto free_mc; } sc->sc_tx_ring.phys_id = (uint16_t)HWRM_NA_SIGNATURE; sc->sc_tx_ring.id = BNXT_TX_RING_ID; sc->sc_tx_ring.doorbell = sc->sc_tx_ring.id * 0x80; sc->sc_tx_ring.ring_size = PAGE_SIZE / sizeof(struct tx_bd_short); sc->sc_tx_ring.vaddr = BNXT_DMA_KVA(sc->sc_tx_ring_mem); sc->sc_tx_ring.paddr = BNXT_DMA_DVA(sc->sc_tx_ring_mem); if (bnxt_hwrm_ring_alloc(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_TX, &sc->sc_tx_ring, sc->sc_cp_ring.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, &sc->sc_tx_ring, 0); sc->sc_rx_ring.phys_id = (uint16_t)HWRM_NA_SIGNATURE; sc->sc_rx_ring.id = BNXT_RX_RING_ID; sc->sc_rx_ring.doorbell = sc->sc_rx_ring.id * 0x80; sc->sc_rx_ring.ring_size = PAGE_SIZE / sizeof(struct rx_prod_pkt_bd); sc->sc_rx_ring.vaddr = BNXT_DMA_KVA(sc->sc_rx_ring_mem); sc->sc_rx_ring.paddr = BNXT_DMA_DVA(sc->sc_rx_ring_mem); if (bnxt_hwrm_ring_alloc(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_RX, &sc->sc_rx_ring, sc->sc_cp_ring.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, &sc->sc_rx_ring, 0); sc->sc_rx_ag_ring.phys_id = (uint16_t)HWRM_NA_SIGNATURE; sc->sc_rx_ag_ring.id = BNXT_AG_RING_ID; sc->sc_rx_ag_ring.doorbell = sc->sc_rx_ag_ring.id * 0x80; sc->sc_rx_ag_ring.ring_size = PAGE_SIZE / sizeof(struct rx_prod_pkt_bd); sc->sc_rx_ag_ring.vaddr = BNXT_DMA_KVA(sc->sc_rx_ring_mem) + PAGE_SIZE; sc->sc_rx_ag_ring.paddr = BNXT_DMA_DVA(sc->sc_rx_ring_mem) + PAGE_SIZE; if (bnxt_hwrm_ring_alloc(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_RX, &sc->sc_rx_ag_ring, sc->sc_cp_ring.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, &sc->sc_rx_ag_ring, 0); sc->sc_ring_group.grp_id = HWRM_NA_SIGNATURE; sc->sc_ring_group.stats_ctx = sc->sc_cp_ring.stats_ctx_id; sc->sc_ring_group.rx_ring_id = sc->sc_rx_ring.phys_id; sc->sc_ring_group.ag_ring_id = sc->sc_rx_ag_ring.phys_id; sc->sc_ring_group.cp_ring_id = sc->sc_cp_ring.ring.phys_id; if (bnxt_hwrm_ring_grp_alloc(sc, &sc->sc_ring_group) != 0) { printf("%s: failed to allocate ring group\n", DEVNAME(sc)); goto dealloc_ag; } 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 dealloc_ring_group; } sc->sc_vnic.id = (uint16_t)HWRM_NA_SIGNATURE; sc->sc_vnic.def_ring_grp = sc->sc_ring_group.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; 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; } /* don't configure rss or tpa yet */ sc->sc_rx_slots = mallocarray(sizeof(*bs), sc->sc_rx_ring.ring_size, M_DEVBUF, M_WAITOK | M_ZERO); if (sc->sc_rx_slots == NULL) { printf("%s: failed to allocate rx slots\n", DEVNAME(sc)); goto dealloc_filter; } for (i = 0; i < sc->sc_rx_ring.ring_size; i++) { bs = &sc->sc_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; } } sc->sc_rx_ag_slots = mallocarray(sizeof(*bs), sc->sc_rx_ag_ring.ring_size, M_DEVBUF, M_WAITOK | M_ZERO); if (sc->sc_rx_ag_slots == NULL) { printf("%s: failed to allocate rx ag slots\n", DEVNAME(sc)); goto destroy_rx_slots; } for (i = 0; i < sc->sc_rx_ag_ring.ring_size; i++) { bs = &sc->sc_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; } } sc->sc_tx_slots = mallocarray(sizeof(*bs), sc->sc_tx_ring.ring_size, M_DEVBUF, M_WAITOK | M_ZERO); if (sc->sc_tx_slots == NULL) { printf("%s: failed to allocate tx slots\n", DEVNAME(sc)); goto destroy_rx_ag_slots; } for (i = 0; i < sc->sc_tx_ring.ring_size; i++) { bs = &sc->sc_tx_slots[i]; if (bus_dmamap_create(sc->sc_dmat, BNXT_MAX_MTU, 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; } } bnxt_iff(sc); /* * 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(&sc->sc_rxr[0], 32, sc->sc_rx_ring.ring_size - 1); if_rxr_init(&sc->sc_rxr[1], 32, sc->sc_rx_ag_ring.ring_size - 1); sc->sc_rx_prod = 0; sc->sc_rx_cons = 0; sc->sc_rx_ag_prod = 0; sc->sc_rx_ag_cons = 0; bnxt_rx_fill(sc); SET(ifp->if_flags, IFF_RUNNING); sc->sc_tx_cons = 0; sc->sc_tx_prod = 0; sc->sc_tx_ring_cons = 0; sc->sc_tx_ring_prod = 0; ifq_clr_oactive(&ifp->if_snd); ifq_restart(&ifp->if_snd); return; destroy_tx_slots: bnxt_free_slots(sc, sc->sc_tx_slots, i, sc->sc_tx_ring.ring_size); sc->sc_tx_slots = NULL; i = sc->sc_rx_ag_ring.ring_size; destroy_rx_ag_slots: bnxt_free_slots(sc, sc->sc_rx_ag_slots, i, sc->sc_rx_ag_ring.ring_size); sc->sc_rx_ag_slots = NULL; i = sc->sc_rx_ring.ring_size; destroy_rx_slots: bnxt_free_slots(sc, sc->sc_rx_slots, i, sc->sc_rx_ring.ring_size); sc->sc_rx_slots = NULL; dealloc_filter: bnxt_hwrm_free_filter(sc, &sc->sc_vnic); dealloc_vnic: bnxt_hwrm_vnic_free(sc, &sc->sc_vnic); dealloc_vnic_ctx: bnxt_hwrm_vnic_ctx_free(sc, &sc->sc_vnic.rss_id); dealloc_ring_group: bnxt_hwrm_ring_grp_free(sc, &sc->sc_ring_group); dealloc_ag: bnxt_hwrm_ring_free(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_RX, &sc->sc_rx_ag_ring); dealloc_tx: bnxt_hwrm_ring_free(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_TX, &sc->sc_tx_ring); dealloc_rx: bnxt_hwrm_ring_free(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_RX, &sc->sc_rx_ring); dealloc_stats: bnxt_hwrm_stat_ctx_free(sc, &sc->sc_cp_ring); free_mc: bnxt_dmamem_free(sc, sc->sc_rx_mcast); sc->sc_rx_mcast = NULL; free_rx: bnxt_dmamem_free(sc, sc->sc_rx_ring_mem); sc->sc_rx_ring_mem = NULL; free_tx: bnxt_dmamem_free(sc, sc->sc_tx_ring_mem); sc->sc_tx_ring_mem = 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; CLR(ifp->if_flags, IFF_RUNNING); ifq_clr_oactive(&ifp->if_snd); ifq_barrier(&ifp->if_snd); timeout_del(&sc->sc_rx_refill); /* empty rx ring first i guess */ bnxt_free_slots(sc, sc->sc_tx_slots, sc->sc_tx_ring.ring_size, sc->sc_tx_ring.ring_size); sc->sc_tx_slots = NULL; bnxt_free_slots(sc, sc->sc_rx_ag_slots, sc->sc_rx_ag_ring.ring_size, sc->sc_rx_ag_ring.ring_size); sc->sc_rx_ag_slots = NULL; bnxt_free_slots(sc, sc->sc_rx_slots, sc->sc_rx_ring.ring_size, sc->sc_rx_ring.ring_size); sc->sc_rx_slots = NULL; 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); bnxt_hwrm_ring_grp_free(sc, &sc->sc_ring_group); bnxt_hwrm_stat_ctx_free(sc, &sc->sc_cp_ring); /* 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, &sc->sc_tx_ring); bnxt_hwrm_ring_free(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_RX, &sc->sc_rx_ag_ring); bnxt_hwrm_ring_free(sc, HWRM_RING_ALLOC_INPUT_RING_TYPE_RX, &sc->sc_rx_ring); bnxt_dmamem_free(sc, sc->sc_rx_mcast); sc->sc_rx_mcast = NULL; bnxt_dmamem_free(sc, sc->sc_rx_ring_mem); sc->sc_rx_ring_mem = NULL; bnxt_dmamem_free(sc, sc->sc_tx_ring_mem); sc->sc_tx_ring_mem = 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_mcast); 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_mcast), 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; 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[2]; memset(&ifr, 0, sizeof(ifr)); ifr[0].ifr_size = MCLBYTES; ifr[0].ifr_info = sc->sc_rxr[0]; ifr[1].ifr_size = BNXT_AG_BUFFER_SIZE; ifr[1].ifr_info = sc->sc_rxr[1]; return (if_rxr_info_ioctl(ifri, nitems(ifr), ifr)); } 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 bnxt_softc *sc = ifp->if_softc; struct bnxt_slot *bs; bus_dmamap_t map; struct mbuf *m; u_int idx, free, used, laststart; uint16_t txflags; int i; txring = (struct tx_bd_short *)BNXT_DMA_KVA(sc->sc_tx_ring_mem); idx = sc->sc_tx_ring_prod; free = sc->sc_tx_ring_cons; if (free <= idx) free += sc->sc_tx_ring.ring_size; free -= idx; used = 0; for (;;) { if (used + BNXT_MAX_TX_SEGS > free) { ifq_set_oactive(ifq); break; } m = ifq_dequeue(ifq); if (m == NULL) break; bs = &sc->sc_tx_slots[sc->sc_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 += map->dm_nsegs; if (map->dm_mapsize < 512) txflags = TX_BD_SHORT_FLAGS_LHINT_LT512; else if (map->dm_mapsize < 1024) txflags = TX_BD_SHORT_FLAGS_LHINT_LT1K; else if (map->dm_mapsize < 2048) txflags = TX_BD_SHORT_FLAGS_LHINT_LT2K; else txflags = TX_BD_SHORT_FLAGS_LHINT_GTE2K; txflags |= TX_BD_SHORT_TYPE_TX_BD_SHORT | TX_BD_SHORT_FLAGS_NO_CMPL | (map->dm_nsegs << TX_BD_SHORT_FLAGS_BD_CNT_SFT); laststart = idx; for (i = 0; i < map->dm_nsegs; i++) { txring[idx].flags_type = htole16(txflags); if (i == map->dm_nsegs - 1) txring[idx].flags_type |= TX_BD_SHORT_FLAGS_PACKET_END; txflags = TX_BD_SHORT_TYPE_TX_BD_SHORT; txring[idx].len = htole16(bs->bs_map->dm_segs[i].ds_len); txring[idx].opaque = sc->sc_tx_prod; txring[idx].addr = htole64(bs->bs_map->dm_segs[i].ds_addr); idx++; if (idx == sc->sc_tx_ring.ring_size) idx = 0; } if (++sc->sc_tx_prod >= sc->sc_tx_ring.ring_size) sc->sc_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, &sc->sc_tx_ring, idx); sc->sc_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_intr(void *xsc) { struct bnxt_softc *sc = (struct bnxt_softc *)xsc; struct ifnet *ifp = &sc->sc_ac.ac_if; struct bnxt_cp_ring *cpr = &sc->sc_cp_ring; 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: rollback = bnxt_rx(sc, cpr, &ml, &rxfree, &agfree, cmpl); break; case CMPL_BASE_TYPE_TX_L2: bnxt_txeof(sc, &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) { sc->sc_rx_cons += rxfree; if (sc->sc_rx_cons >= sc->sc_rx_ring.ring_size) sc->sc_rx_cons -= sc->sc_rx_ring.ring_size; sc->sc_rx_ag_cons += agfree; if (sc->sc_rx_ag_cons >= sc->sc_rx_ag_ring.ring_size) sc->sc_rx_ag_cons -= sc->sc_rx_ag_ring.ring_size; if_rxr_put(&sc->sc_rxr[0], rxfree); if_rxr_put(&sc->sc_rxr[1], agfree); bnxt_rx_fill(sc); if ((sc->sc_rx_cons == sc->sc_rx_prod) || (sc->sc_rx_ag_cons == sc->sc_rx_ag_prod)) timeout_add(&sc->sc_rx_refill, 0); if_input(&sc->sc_ac.ac_if, &ml); } if (txfree != 0) { if (ifq_is_oactive(&ifp->if_snd)) ifq_restart(&ifp->if_snd); } 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 = MCLGETI(NULL, M_DONTWAIT, NULL, 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_softc *sc) { u_int slots; int rv = 0; slots = if_rxr_get(&sc->sc_rxr[0], sc->sc_rx_ring.ring_size); if (slots > 0) { slots = bnxt_rx_fill_slots(sc, &sc->sc_rx_ring, BNXT_DMA_KVA(sc->sc_rx_ring_mem), sc->sc_rx_slots, &sc->sc_rx_prod, MCLBYTES, RX_PROD_PKT_BD_TYPE_RX_PROD_PKT, slots); if_rxr_put(&sc->sc_rxr[0], slots); } else rv = 1; slots = if_rxr_get(&sc->sc_rxr[1], sc->sc_rx_ag_ring.ring_size); if (slots > 0) { slots = bnxt_rx_fill_slots(sc, &sc->sc_rx_ag_ring, BNXT_DMA_KVA(sc->sc_rx_ring_mem) + PAGE_SIZE, sc->sc_rx_ag_slots, &sc->sc_rx_ag_prod, BNXT_AG_BUFFER_SIZE, RX_PROD_AGG_BD_TYPE_RX_PROD_AGG, slots); if_rxr_put(&sc->sc_rxr[1], slots); } else rv = 1; return (rv); } void bnxt_refill(void *xsc) { struct bnxt_softc *sc = xsc; bnxt_rx_fill(sc); if (sc->sc_rx_cons == sc->sc_rx_prod) timeout_add(&sc->sc_rx_refill, 1); } int bnxt_rx(struct bnxt_softc *sc, 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 *rx = (struct rx_pkt_cmpl *)cmpl; struct rx_pkt_cmpl_hi *rxhi; struct rx_abuf_cmpl *ag; /* 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 ((rx->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 = &sc->sc_rx_slots[rx->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(rx->len); (*slots)++; if (ag != NULL) { bs = &sc->sc_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, 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 = sc->sc_tx_ring_cons; last = sc->sc_tx_cons; do { bs = &sc->sc_tx_slots[sc->sc_tx_cons]; map = bs->bs_map; segs = map->dm_nsegs; 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 >= sc->sc_tx_ring.ring_size) idx -= sc->sc_tx_ring.ring_size; last = sc->sc_tx_cons; if (++sc->sc_tx_cons >= sc->sc_tx_ring.ring_size) sc->sc_tx_cons = 0; } while (last != txcmpl->opaque); sc->sc_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 i, rc = 0; uint8_t *qptr; 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_max_tc = resp->max_configurable_queues; if (softc->sc_max_tc > BNXT_MAX_QUEUE) softc->sc_max_tc = BNXT_MAX_QUEUE; qptr = &resp->queue_id0; for (i = 0; i < softc->sc_max_tc; i++) { softc->sc_q_info[i].id = *qptr++; softc->sc_q_info[i].profile = *qptr++; } 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[] = ""; const char naver[] = ""; #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_q_info[0].id); req.int_mode = 0; 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); 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); } #if 0 int bnxt_hwrm_rss_cfg(struct bnxt_softc *softc, struct bnxt_vnic_info *vnic, uint32_t hash_type) { 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(vnic->rss_grp_tbl.idi_paddr); req.hash_key_tbl_addr = htole64(vnic->rss_hash_key_tbl.idi_paddr); req.rss_ctx_idx = htole16(vnic->rss_id); return hwrm_send_message(softc, &req, sizeof(req)); } #endif int bnxt_cfg_async_cr(struct bnxt_softc *softc) { 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(softc->sc_cp_ring.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(softc->sc_cp_ring.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)); }