/*- * Copyright (c) 2007-2011 Broadcom Corporation. All rights reserved. * * Gary Zambrano * David Christensen * * 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. * 3. Neither the name of Broadcom Corporation nor the name of its contributors * may be used to endorse or promote products derived from this software * without specific prior written consent. * * 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. */ #include __FBSDID("$FreeBSD$"); /* * The following controllers are supported by this driver: * BCM57710 A1+ * BCM57711 A0+ * BCM57711E A0+ * * The following controllers are not supported by this driver: * BCM57710 A0 (pre-production) * * External PHY References: * ------------------------ * BCM8073 - Dual Port 10GBase-KR Ethernet PHY * BCM8705 - 10Gb Ethernet Serial Transceiver * BCM8706 - 10Gb Ethernet LRM PHY * BCM8726 - Dual Port 10Gb Ethernet LRM PHY * BCM8727 - Dual Port 10Gb Ethernet LRM PHY * BCM8481 - Single Port 10GBase-T Ethernet PHY * BCM84823 - Dual Port 10GBase-T Ethernet PHY * SFX7101 - Solarflare 10GBase-T Ethernet PHY * */ #include "opt_bxe.h" #include "bxe_include.h" #include "if_bxe.h" #include "bxe_init.h" #include "hw_dump_reg_st.h" #include "dump_e1.h" #include "dump_e1h.h" #include "bxe_self_test.h" /* BXE Debug Options */ #ifdef BXE_DEBUG uint32_t bxe_debug = BXE_WARN; /* 0 = Never */ /* 1 = 1 in 2,147,483,648 */ /* 256 = 1 in 8,388,608 */ /* 2048 = 1 in 1,048,576 */ /* 65536 = 1 in 32,768 */ /* 1048576 = 1 in 2,048 */ /* 268435456 = 1 in 8 */ /* 536870912 = 1 in 4 */ /* 1073741824 = 1 in 2 */ /* Controls how often to simulate an mbuf allocation failure. */ int bxe_debug_mbuf_allocation_failure = 0; /* Controls how often to simulate a DMA mapping failure. */ int bxe_debug_dma_map_addr_failure = 0; /* Controls how often to simulate a bootcode failure. */ int bxe_debug_bootcode_running_failure = 0; #endif #define MDIO_INDIRECT_REG_ADDR 0x1f #define MDIO_SET_REG_BANK(sc, reg_bank) \ bxe_mdio22_write(sc, MDIO_INDIRECT_REG_ADDR, reg_bank) #define MDIO_ACCESS_TIMEOUT 1000 #define BMAC_CONTROL_RX_ENABLE 2 /* BXE Build Time Options */ /* #define BXE_NVRAM_WRITE 1 */ #define BXE_USE_DMAE 1 /* * PCI Device ID Table * Used by bxe_probe() to identify the devices supported by this driver. */ #define BXE_DEVDESC_MAX 64 static struct bxe_type bxe_devs[] = { /* BCM57710 Controllers and OEM boards. */ { BRCM_VENDORID, BRCM_DEVICEID_BCM57710, PCI_ANY_ID, PCI_ANY_ID, "Broadcom NetXtreme II BCM57710 10GbE" }, /* BCM57711 Controllers and OEM boards. */ { BRCM_VENDORID, BRCM_DEVICEID_BCM57711, PCI_ANY_ID, PCI_ANY_ID, "Broadcom NetXtreme II BCM57711 10GbE" }, /* BCM57711E Controllers and OEM boards. */ { BRCM_VENDORID, BRCM_DEVICEID_BCM57711E, PCI_ANY_ID, PCI_ANY_ID, "Broadcom NetXtreme II BCM57711E 10GbE" }, {0, 0, 0, 0, NULL} }; /* * FreeBSD device entry points. */ static int bxe_probe(device_t); static int bxe_attach(device_t); static int bxe_detach(device_t); static int bxe_shutdown(device_t); /* * Driver local functions. */ static void bxe_tunables_set(struct bxe_softc *); static void bxe_print_adapter_info(struct bxe_softc *); static void bxe_probe_pci_caps(struct bxe_softc *); static void bxe_link_settings_supported(struct bxe_softc *, uint32_t); static void bxe_link_settings_requested(struct bxe_softc *); static int bxe_hwinfo_function_get(struct bxe_softc *); static int bxe_hwinfo_port_get(struct bxe_softc *); static int bxe_hwinfo_common_get(struct bxe_softc *); static void bxe_undi_unload(struct bxe_softc *); static int bxe_setup_leading(struct bxe_softc *); static int bxe_stop_leading(struct bxe_softc *); static int bxe_setup_multi(struct bxe_softc *, int); static int bxe_stop_multi(struct bxe_softc *, int); static int bxe_stop_locked(struct bxe_softc *, int); static int bxe_alloc_buf_rings(struct bxe_softc *); static void bxe_free_buf_rings(struct bxe_softc *); static void bxe_init_locked(struct bxe_softc *, int); static int bxe_wait_ramrod(struct bxe_softc *, int, int, int *, int); static void bxe_init_str_wr(struct bxe_softc *, uint32_t, const uint32_t *, uint32_t); static void bxe_init_ind_wr(struct bxe_softc *, uint32_t, const uint32_t *, uint16_t); static void bxe_init_wr_64(struct bxe_softc *, uint32_t, const uint32_t *, uint32_t); static void bxe_write_big_buf(struct bxe_softc *, uint32_t, uint32_t); static void bxe_init_fill(struct bxe_softc *, uint32_t, int, uint32_t); static void bxe_init_block(struct bxe_softc *, uint32_t, uint32_t); static void bxe_init(void *); static void bxe_release_resources(struct bxe_softc *); static void bxe_reg_wr_ind(struct bxe_softc *, uint32_t, uint32_t); static uint32_t bxe_reg_rd_ind(struct bxe_softc *, uint32_t); static void bxe_post_dmae(struct bxe_softc *, struct dmae_command *, int); static void bxe_wb_wr(struct bxe_softc *, int, uint32_t, uint32_t); static __inline uint32_t bxe_reg_poll(struct bxe_softc *, uint32_t, uint32_t, int, int); static int bxe_mc_assert(struct bxe_softc *); static void bxe_panic_dump(struct bxe_softc *); static void bxe_int_enable(struct bxe_softc *); static void bxe_int_disable(struct bxe_softc *); static int bxe_nvram_acquire_lock(struct bxe_softc *); static int bxe_nvram_release_lock(struct bxe_softc *); static void bxe_nvram_enable_access(struct bxe_softc *); static void bxe_nvram_disable_access(struct bxe_softc *); static int bxe_nvram_read_dword (struct bxe_softc *, uint32_t, uint32_t *, uint32_t); static int bxe_nvram_read(struct bxe_softc *, uint32_t, uint8_t *, int); #ifdef BXE_NVRAM_WRITE_SUPPORT static int bxe_nvram_write_dword(struct bxe_softc *, uint32_t, uint32_t, uint32_t); static int bxe_nvram_write1(struct bxe_softc *, uint32_t, uint8_t *, int); static int bxe_nvram_write(struct bxe_softc *, uint32_t, uint8_t *, int); #endif static int bxe_nvram_test(struct bxe_softc *); static __inline void bxe_ack_sb(struct bxe_softc *, uint8_t, uint8_t, uint16_t, uint8_t, uint8_t); static __inline uint16_t bxe_update_fpsb_idx(struct bxe_fastpath *); static uint16_t bxe_ack_int(struct bxe_softc *); static void bxe_sp_event(struct bxe_fastpath *, union eth_rx_cqe *); static int bxe_acquire_hw_lock(struct bxe_softc *, uint32_t); static int bxe_release_hw_lock(struct bxe_softc *, uint32_t); static void bxe_acquire_phy_lock(struct bxe_softc *); static void bxe_release_phy_lock(struct bxe_softc *); static void bxe_pmf_update(struct bxe_softc *); static void bxe_init_port_minmax(struct bxe_softc *); static void bxe_link_attn(struct bxe_softc *); static int bxe_sp_post(struct bxe_softc *, int, int, uint32_t, uint32_t, int); static int bxe_acquire_alr(struct bxe_softc *); static void bxe_release_alr(struct bxe_softc *); static uint16_t bxe_update_dsb_idx(struct bxe_softc *); static void bxe_attn_int_asserted(struct bxe_softc *, uint32_t); static __inline void bxe_attn_int_deasserted0(struct bxe_softc *, uint32_t); static __inline void bxe_attn_int_deasserted1(struct bxe_softc *, uint32_t); static __inline void bxe_attn_int_deasserted2(struct bxe_softc *, uint32_t); static __inline void bxe_attn_int_deasserted3(struct bxe_softc *, uint32_t); static void bxe_attn_int_deasserted(struct bxe_softc *, uint32_t); static void bxe_attn_int(struct bxe_softc *); static void bxe_stats_storm_post(struct bxe_softc *); static void bxe_stats_init(struct bxe_softc *); static void bxe_stats_hw_post(struct bxe_softc *); static int bxe_stats_comp(struct bxe_softc *); static void bxe_stats_pmf_update(struct bxe_softc *); static void bxe_stats_port_base_init(struct bxe_softc *); static void bxe_stats_port_init(struct bxe_softc *); static void bxe_stats_func_base_init(struct bxe_softc *); static void bxe_stats_func_init(struct bxe_softc *); static void bxe_stats_start(struct bxe_softc *); static void bxe_stats_pmf_start(struct bxe_softc *); static void bxe_stats_restart(struct bxe_softc *); static void bxe_stats_bmac_update(struct bxe_softc *); static void bxe_stats_emac_update(struct bxe_softc *); static int bxe_stats_hw_update(struct bxe_softc *); static int bxe_stats_storm_update(struct bxe_softc *); static void bxe_stats_func_base_update(struct bxe_softc *); static void bxe_stats_update(struct bxe_softc *); static void bxe_stats_port_stop(struct bxe_softc *); static void bxe_stats_stop(struct bxe_softc *); static void bxe_stats_do_nothing(struct bxe_softc *); static void bxe_stats_handle(struct bxe_softc *, enum bxe_stats_event); static int bxe_tx_encap(struct bxe_fastpath *, struct mbuf **); static void bxe_tx_start(struct ifnet *); static void bxe_tx_start_locked(struct ifnet *, struct bxe_fastpath *); static int bxe_tx_mq_start(struct ifnet *, struct mbuf *); static int bxe_tx_mq_start_locked(struct ifnet *, struct bxe_fastpath *, struct mbuf *); static void bxe_mq_flush(struct ifnet *ifp); static int bxe_ioctl(struct ifnet *, u_long, caddr_t); static __inline int bxe_has_rx_work(struct bxe_fastpath *); static __inline int bxe_has_tx_work(struct bxe_fastpath *); static void bxe_intr_legacy(void *); static void bxe_task_sp(void *, int); static void bxe_intr_sp(void *); static void bxe_task_fp(void *, int); static void bxe_intr_fp(void *); static void bxe_zero_sb(struct bxe_softc *, int); static void bxe_init_sb(struct bxe_softc *, struct host_status_block *, bus_addr_t, int); static void bxe_zero_def_sb(struct bxe_softc *); static void bxe_init_def_sb(struct bxe_softc *, struct host_def_status_block *, bus_addr_t, int); static void bxe_update_coalesce(struct bxe_softc *); static __inline void bxe_update_rx_prod(struct bxe_softc *, struct bxe_fastpath *, uint16_t, uint16_t, uint16_t); static void bxe_clear_sge_mask_next_elems(struct bxe_fastpath *); static __inline void bxe_init_sge_ring_bit_mask(struct bxe_fastpath *); static int bxe_alloc_tpa_mbuf(struct bxe_fastpath *, int); static int bxe_fill_tpa_pool(struct bxe_fastpath *); static void bxe_free_tpa_pool(struct bxe_fastpath *); static int bxe_alloc_rx_sge_mbuf(struct bxe_fastpath *, uint16_t); static int bxe_fill_sg_chain(struct bxe_fastpath *); static void bxe_free_sg_chain(struct bxe_fastpath *); static int bxe_alloc_rx_bd_mbuf(struct bxe_fastpath *, uint16_t); static int bxe_fill_rx_bd_chain(struct bxe_fastpath *); static void bxe_free_rx_bd_chain(struct bxe_fastpath *); static void bxe_mutexes_alloc(struct bxe_softc *); static void bxe_mutexes_free(struct bxe_softc *); static void bxe_clear_rx_chains(struct bxe_softc *); static int bxe_init_rx_chains(struct bxe_softc *); static void bxe_clear_tx_chains(struct bxe_softc *); static void bxe_init_tx_chains(struct bxe_softc *); static void bxe_init_sp_ring(struct bxe_softc *); static void bxe_init_context(struct bxe_softc *); static void bxe_init_ind_table(struct bxe_softc *); static void bxe_set_client_config(struct bxe_softc *); static void bxe_set_storm_rx_mode(struct bxe_softc *); static void bxe_init_internal_common(struct bxe_softc *); static void bxe_init_internal_port(struct bxe_softc *); static void bxe_init_internal_func(struct bxe_softc *); static void bxe_init_internal(struct bxe_softc *, uint32_t); static int bxe_init_nic(struct bxe_softc *, uint32_t); static void bxe_lb_pckt(struct bxe_softc *); static int bxe_int_mem_test(struct bxe_softc *); static void bxe_enable_blocks_attention (struct bxe_softc *); static void bxe_init_pxp(struct bxe_softc *); static int bxe_init_common(struct bxe_softc *); static int bxe_init_port(struct bxe_softc *); static void bxe_ilt_wr(struct bxe_softc *, uint32_t, bus_addr_t); static int bxe_init_func(struct bxe_softc *); static int bxe_init_hw(struct bxe_softc *, uint32_t); static int bxe_fw_command(struct bxe_softc *, uint32_t); static void bxe_host_structures_free(struct bxe_softc *); static void bxe_dma_map_addr(void *, bus_dma_segment_t *, int, int); static int bxe_host_structures_alloc(device_t); static void bxe_set_mac_addr_e1(struct bxe_softc *, int); static void bxe_set_mac_addr_e1h(struct bxe_softc *, int); static void bxe_set_rx_mode(struct bxe_softc *); static void bxe_reset_func(struct bxe_softc *); static void bxe_reset_port(struct bxe_softc *); static void bxe_reset_common(struct bxe_softc *); static void bxe_reset_chip(struct bxe_softc *, uint32_t); static int bxe_ifmedia_upd(struct ifnet *); static void bxe_ifmedia_status(struct ifnet *, struct ifmediareq *); static __inline void bxe_update_last_max_sge(struct bxe_fastpath *, uint16_t); static void bxe_update_sge_prod(struct bxe_fastpath *, struct eth_fast_path_rx_cqe *); static void bxe_tpa_start(struct bxe_fastpath *, uint16_t, uint16_t, uint16_t); static int bxe_fill_frag_mbuf(struct bxe_softc *, struct bxe_fastpath *, struct mbuf *, struct eth_fast_path_rx_cqe *, uint16_t); static void bxe_tpa_stop(struct bxe_softc *, struct bxe_fastpath *, uint16_t, int, int, union eth_rx_cqe *, uint16_t); static void bxe_rxeof(struct bxe_fastpath *); static void bxe_txeof(struct bxe_fastpath *); static int bxe_watchdog(struct bxe_fastpath *fp); static void bxe_tick(void *); static void bxe_add_sysctls(struct bxe_softc *); static void bxe_write_dmae_phys_len(struct bxe_softc *, bus_addr_t, uint32_t, uint32_t); void bxe_write_dmae(struct bxe_softc *, bus_addr_t, uint32_t, uint32_t); void bxe_read_dmae(struct bxe_softc *, uint32_t, uint32_t); int bxe_set_gpio(struct bxe_softc *, int, uint32_t, uint8_t); int bxe_get_gpio(struct bxe_softc *, int, uint8_t); int bxe_set_spio(struct bxe_softc *, int, uint32_t); int bxe_set_gpio_int(struct bxe_softc *, int, uint32_t, uint8_t); /* * BXE Debug Data Structure Dump Routines */ #ifdef BXE_DEBUG static int bxe_sysctl_driver_state(SYSCTL_HANDLER_ARGS); static int bxe_sysctl_hw_state(SYSCTL_HANDLER_ARGS); static int bxe_sysctl_dump_fw(SYSCTL_HANDLER_ARGS); static int bxe_sysctl_dump_rx_cq_chain(SYSCTL_HANDLER_ARGS); static int bxe_sysctl_dump_rx_bd_chain(SYSCTL_HANDLER_ARGS); static int bxe_sysctl_dump_tx_chain(SYSCTL_HANDLER_ARGS); static int bxe_sysctl_reg_read(SYSCTL_HANDLER_ARGS); static int bxe_sysctl_breakpoint(SYSCTL_HANDLER_ARGS); static __noinline void bxe_validate_rx_packet(struct bxe_fastpath *, uint16_t, union eth_rx_cqe *, struct mbuf *); static void bxe_grcdump(struct bxe_softc *, int); static __noinline void bxe_dump_enet(struct bxe_softc *,struct mbuf *); static __noinline void bxe_dump_mbuf (struct bxe_softc *, struct mbuf *); static __noinline void bxe_dump_tx_mbuf_chain(struct bxe_softc *, int, int); static __noinline void bxe_dump_rx_mbuf_chain(struct bxe_softc *, int, int); static __noinline void bxe_dump_tx_parsing_bd(struct bxe_fastpath *,int, struct eth_tx_parse_bd *); static __noinline void bxe_dump_txbd(struct bxe_fastpath *, int, union eth_tx_bd_types *); static __noinline void bxe_dump_rxbd(struct bxe_fastpath *, int, struct eth_rx_bd *); static __noinline void bxe_dump_cqe(struct bxe_fastpath *, int, union eth_rx_cqe *); static __noinline void bxe_dump_tx_chain(struct bxe_fastpath *, int, int); static __noinline void bxe_dump_rx_cq_chain(struct bxe_fastpath *, int, int); static __noinline void bxe_dump_rx_bd_chain(struct bxe_fastpath *, int, int); static __noinline void bxe_dump_status_block(struct bxe_softc *); static __noinline void bxe_dump_stats_block(struct bxe_softc *); static __noinline void bxe_dump_fp_state(struct bxe_fastpath *); static __noinline void bxe_dump_port_state_locked(struct bxe_softc *); static __noinline void bxe_dump_link_vars_state_locked(struct bxe_softc *); static __noinline void bxe_dump_link_params_state_locked(struct bxe_softc *); static __noinline void bxe_dump_driver_state(struct bxe_softc *); static __noinline void bxe_dump_hw_state(struct bxe_softc *); static __noinline void bxe_dump_fw(struct bxe_softc *); static void bxe_decode_mb_msgs(struct bxe_softc *, uint32_t, uint32_t); static void bxe_decode_ramrod_cmd(struct bxe_softc *, int); static void bxe_breakpoint(struct bxe_softc *); #endif #define BXE_DRIVER_VERSION "1.5.52" static void bxe_init_e1_firmware(struct bxe_softc *sc); static void bxe_init_e1h_firmware(struct bxe_softc *sc); /* * FreeBSD device dispatch table. */ static device_method_t bxe_methods[] = { /* Device interface (device_if.h) */ DEVMETHOD(device_probe, bxe_probe), DEVMETHOD(device_attach, bxe_attach), DEVMETHOD(device_detach, bxe_detach), DEVMETHOD(device_shutdown, bxe_shutdown), DEVMETHOD_END }; static driver_t bxe_driver = { "bxe", bxe_methods, sizeof(struct bxe_softc) }; static devclass_t bxe_devclass; MODULE_DEPEND(bxe, pci, 1, 1, 1); MODULE_DEPEND(bxe, ether, 1, 1, 1); DRIVER_MODULE(bxe, pci, bxe_driver, bxe_devclass, 0, 0); /* * Tunable device values */ static SYSCTL_NODE(_hw, OID_AUTO, bxe, CTLFLAG_RD, 0, "bxe driver parameters"); /* Allowable values are TRUE (1) or FALSE (0). */ static int bxe_dcc_enable = FALSE; TUNABLE_INT("hw.bxe.dcc_enable", &bxe_dcc_enable); SYSCTL_UINT(_hw_bxe, OID_AUTO, dcc_enable, CTLFLAG_RDTUN, &bxe_dcc_enable, 0, "dcc Enable/Disable"); /* Allowable values are TRUE (1) or FALSE (0). */ static int bxe_tso_enable = TRUE; TUNABLE_INT("hw.bxe.tso_enable", &bxe_tso_enable); SYSCTL_UINT(_hw_bxe, OID_AUTO, tso_enable, CTLFLAG_RDTUN, &bxe_tso_enable, 0, "TSO Enable/Disable"); /* Allowable values are 0 (IRQ), 1 (MSI/IRQ), and 2 (MSI-X/MSI/IRQ). */ static int bxe_int_mode = 2; TUNABLE_INT("hw.bxe.int_mode", &bxe_int_mode); SYSCTL_UINT(_hw_bxe, OID_AUTO, int_mode, CTLFLAG_RDTUN, &bxe_int_mode, 0, "Interrupt (MSI-X|MSI|INTx) mode"); /* * Specifies the number of queues that will be used when a multi-queue * RSS mode is selected using bxe_multi_mode below. * * Allowable values are 0 (Auto) or 1 to MAX_CONTEXT (fixed queue number). */ static int bxe_queue_count = 0; TUNABLE_INT("hw.bxe.queue_count", &bxe_queue_count); SYSCTL_UINT(_hw_bxe, OID_AUTO, queue_count, CTLFLAG_RDTUN, &bxe_queue_count, 0, "Multi-Queue queue count"); /* * ETH_RSS_MODE_DISABLED (0) * Disables all multi-queue/packet sorting algorithms. All * received frames are routed to a single receive queue. * * ETH_RSS_MODE_REGULAR (1) * The default mode which assigns incoming frames to receive * queues according to RSS (i.e a 2-tuple match on the source/ * destination IP address or a 4-tuple match on the source/ * destination IP address and the source/destination TCP port). * */ static int bxe_multi_mode = ETH_RSS_MODE_REGULAR; TUNABLE_INT("hw.bxe.multi_mode", &bxe_multi_mode); SYSCTL_UINT(_hw_bxe, OID_AUTO, multi_mode, CTLFLAG_RDTUN, &bxe_multi_mode, 0, "Multi-Queue Mode"); /* * Host interrupt coalescing is controller by these values. * The first frame always causes an interrupt but subsequent * frames are coalesced until the RX/TX ticks timer value * expires and another interrupt occurs. (Ticks are measured * in microseconds.) */ static uint32_t bxe_rx_ticks = 25; TUNABLE_INT("hw.bxe.rx_ticks", &bxe_rx_ticks); SYSCTL_UINT(_hw_bxe, OID_AUTO, rx_ticks, CTLFLAG_RDTUN, &bxe_rx_ticks, 0, "Receive ticks"); static uint32_t bxe_tx_ticks = 50; TUNABLE_INT("hw.bxe.tx_ticks", &bxe_tx_ticks); SYSCTL_UINT(_hw_bxe, OID_AUTO, tx_ticks, CTLFLAG_RDTUN, &bxe_tx_ticks, 0, "Transmit ticks"); /* * Allows the PCIe maximum read request size value to be manually * set during initialization rather than automatically determined * by the driver. * * Allowable values are: * -1 (Auto), 0 (128B), 1 (256B), 2 (512B), 3 (1KB) */ static int bxe_mrrs = -1; TUNABLE_INT("hw.bxe.mrrs", &bxe_mrrs); SYSCTL_UINT(_hw_bxe, OID_AUTO, mrrs, CTLFLAG_RDTUN, &bxe_mrrs, 0, "PCIe maximum read request size."); #if 0 /* * Allows setting the maximum number of received frames to process * during an interrupt. * * Allowable values are: * -1 (Unlimited), 0 (None), otherwise specifies the number of RX frames. */ static int bxe_rx_limit = -1; TUNABLE_INT("hw.bxe.rx_limit", &bxe_rx_limit); SYSCTL_UINT(_hw_bxe, OID_AUTO, rx_limit, CTLFLAG_RDTUN, &bxe_rx_limit, 0, "Maximum received frames processed during an interrupt."); /* * Allows setting the maximum number of transmit frames to process * during an interrupt. * * Allowable values are: * -1 (Unlimited), 0 (None), otherwise specifies the number of TX frames. */ static int bxe_tx_limit = -1; TUNABLE_INT("hw.bxe.tx_limit", &bxe_tx_limit); SYSCTL_UINT(_hw_bxe, OID_AUTO, tx_limit, CTLFLAG_RDTUN, &bxe_tx_limit, 0, "Maximum transmit frames processed during an interrupt."); #endif /* * Global variables */ /* 0 is common, 1 is port 0, 2 is port 1. */ static int load_count[3]; /* Tracks whether MCP firmware is running. */ static int nomcp; #ifdef BXE_DEBUG /* * A debug version of the 32 bit OS register write function to * capture/display values written to the controller. * * Returns: * None. */ void bxe_reg_write32(struct bxe_softc *sc, bus_size_t offset, uint32_t val) { if ((offset % 4) != 0) { DBPRINT(sc, BXE_WARN, "%s(): Warning! Unaligned write to 0x%jX!\n", __FUNCTION__, (uintmax_t)offset); } DBPRINT(sc, BXE_INSANE_REGS, "%s(): offset = 0x%jX, val = 0x%08X\n", __FUNCTION__, (uintmax_t)offset, val); bus_space_write_4(sc->bxe_btag, sc->bxe_bhandle, offset, val); } /* * A debug version of the 16 bit OS register write function to * capture/display values written to the controller. * * Returns: * None. */ static void bxe_reg_write16(struct bxe_softc *sc, bus_size_t offset, uint16_t val) { if ((offset % 2) != 0) { DBPRINT(sc, BXE_WARN, "%s(): Warning! Unaligned write to 0x%jX!\n", __FUNCTION__, (uintmax_t)offset); } DBPRINT(sc, BXE_INSANE_REGS, "%s(): offset = 0x%jX, val = 0x%04X\n", __FUNCTION__, (uintmax_t)offset, val); bus_space_write_2(sc->bxe_btag, sc->bxe_bhandle, offset, val); } /* * A debug version of the 8 bit OS register write function to * capture/display values written to the controller. * * Returns: * None. */ static void bxe_reg_write8(struct bxe_softc *sc, bus_size_t offset, uint8_t val) { DBPRINT(sc, BXE_INSANE_REGS, "%s(): offset = 0x%jX, val = 0x%02X\n", __FUNCTION__, (uintmax_t)offset, val); bus_space_write_1(sc->bxe_btag, sc->bxe_bhandle, offset, val); } /* * A debug version of the 32 bit OS register read function to * capture/display values read from the controller. * * Returns: * 32bit value read. */ uint32_t bxe_reg_read32(struct bxe_softc *sc, bus_size_t offset) { uint32_t val; if ((offset % 4) != 0) { DBPRINT(sc, BXE_WARN, "%s(): Warning! Unaligned read from 0x%jX!\n", __FUNCTION__, (uintmax_t)offset); } val = bus_space_read_4(sc->bxe_btag, sc->bxe_bhandle, offset); DBPRINT(sc, BXE_INSANE_REGS, "%s(): offset = 0x%jX, val = 0x%08X\n", __FUNCTION__, (uintmax_t)offset, val); return (val); } /* * A debug version of the 16 bit OS register read function to * capture/display values read from the controller. * * Returns: * 16bit value read. */ static uint16_t bxe_reg_read16(struct bxe_softc *sc, bus_size_t offset) { uint16_t val; if ((offset % 2) != 0) { DBPRINT(sc, BXE_WARN, "%s(): Warning! Unaligned read from 0x%jX!\n", __FUNCTION__, (uintmax_t)offset); } val = bus_space_read_2(sc->bxe_btag, sc->bxe_bhandle, offset); DBPRINT(sc, BXE_INSANE_REGS, "%s(): offset = 0x%jX, val = 0x%08X\n", __FUNCTION__, (uintmax_t)offset, val); return (val); } /* * A debug version of the 8 bit OS register write function to * capture/display values written to the controller. * * Returns: * 8bit value read. */ static uint8_t bxe_reg_read8(struct bxe_softc *sc, bus_size_t offset) { uint8_t val = bus_space_read_1(sc->bxe_btag, sc->bxe_bhandle, offset); DBPRINT(sc, BXE_INSANE_REGS, "%s(): offset = 0x%jX, val = 0x%02X\n", __FUNCTION__, (uintmax_t)offset, val); return (val); } #endif static void bxe_read_mf_cfg(struct bxe_softc *sc) { int func, vn; for (vn = VN_0; vn < E1HVN_MAX; vn++) { func = 2 * vn + BP_PORT(sc); sc->mf_config[vn] = SHMEM_RD(sc,mf_cfg.func_mf_config[func].config); } } static void bxe_e1h_disable(struct bxe_softc *sc) { int port; port = BP_PORT(sc); REG_WR(sc, NIG_REG_LLH0_FUNC_EN + port * 8, 0); sc->bxe_ifp->if_drv_flags = 0; } static void bxe_e1h_enable(struct bxe_softc *sc) { int port; port = BP_PORT(sc); REG_WR(sc, NIG_REG_LLH0_FUNC_EN + port * 8, 1); sc->bxe_ifp->if_drv_flags = IFF_DRV_RUNNING; } /* * Calculates the sum of vn_min_rates. * It's needed for further normalizing of the min_rates. * Returns: * sum of vn_min_rates. * or * 0 - if all the min_rates are 0. In the later case fainess * algorithm should be deactivated. If not all min_rates are * zero then those that are zeroes will be set to 1. */ static void bxe_calc_vn_wsum(struct bxe_softc *sc) { uint32_t vn_cfg, vn_min_rate; int all_zero, vn; DBENTER(BXE_VERBOSE_LOAD); all_zero = 1; sc->vn_wsum = 0; for (vn = VN_0; vn < E1HVN_MAX; vn++) { vn_cfg = sc->mf_config[vn]; vn_min_rate = ((vn_cfg & FUNC_MF_CFG_MIN_BW_MASK) >> FUNC_MF_CFG_MIN_BW_SHIFT) * 100; /* Skip hidden vns */ if (vn_cfg & FUNC_MF_CFG_FUNC_HIDE) continue; /* If min rate is zero - set it to 1. */ if (!vn_min_rate) vn_min_rate = DEF_MIN_RATE; else all_zero = 0; sc->vn_wsum += vn_min_rate; } /* ... only if all min rates are zeros - disable fairness */ if (all_zero) sc->cmng.flags.cmng_enables &= ~CMNG_FLAGS_PER_PORT_FAIRNESS_VN; else sc->cmng.flags.cmng_enables |= CMNG_FLAGS_PER_PORT_FAIRNESS_VN; DBEXIT(BXE_VERBOSE_LOAD); } /* * * Returns: * None. */ static void bxe_init_vn_minmax(struct bxe_softc *sc, int vn) { struct rate_shaping_vars_per_vn m_rs_vn; struct fairness_vars_per_vn m_fair_vn; uint32_t vn_cfg; uint16_t vn_min_rate, vn_max_rate; int func, i; vn_cfg = sc->mf_config[vn]; func = 2 * vn + BP_PORT(sc); DBENTER(BXE_VERBOSE_LOAD); /* If function is hidden - set min and max to zeroes. */ if (vn_cfg & FUNC_MF_CFG_FUNC_HIDE) { vn_min_rate = 0; vn_max_rate = 0; } else { vn_min_rate = ((vn_cfg & FUNC_MF_CFG_MIN_BW_MASK) >> FUNC_MF_CFG_MIN_BW_SHIFT) * 100; /* * If fairness is enabled (i.e. not all min rates are zero), * and if the current min rate is zero, set it to 1. * This is a requirement of the algorithm. */ if (sc->vn_wsum && (vn_min_rate == 0)) vn_min_rate = DEF_MIN_RATE; vn_max_rate = ((vn_cfg & FUNC_MF_CFG_MAX_BW_MASK) >> FUNC_MF_CFG_MAX_BW_SHIFT) * 100; if (vn_max_rate == 0) return; } DBPRINT(sc, BXE_INFO_LOAD, "%s(): func %d: vn_min_rate = %d, vn_max_rate = %d, wsum = %d.\n", __FUNCTION__, func, vn_min_rate, vn_max_rate, sc->vn_wsum); memset(&m_rs_vn, 0, sizeof(struct rate_shaping_vars_per_vn)); memset(&m_fair_vn, 0, sizeof(struct fairness_vars_per_vn)); /* Global VNIC counter - maximal Mbps for this VNIC. */ m_rs_vn.vn_counter.rate = vn_max_rate; /* Quota - number of bytes transmitted in this period. */ m_rs_vn.vn_counter.quota = (vn_max_rate * RS_PERIODIC_TIMEOUT_USEC) / 8; if (sc->vn_wsum) { /* * Credit for each period of the fairness algorithm. The * number of bytes in T_FAIR (the VNIC shares the port rate). * vn_wsum should not be larger than 10000, thus * T_FAIR_COEF / (8 * vn_wsum) will always be grater than zero. */ m_fair_vn.vn_credit_delta = max((uint32_t)(vn_min_rate * (T_FAIR_COEF / (8 * sc->vn_wsum))), (uint32_t)(sc->cmng.fair_vars.fair_threshold * 2)); } func = BP_FUNC(sc); /* Store it to internal memory */ for (i = 0; i < sizeof(struct rate_shaping_vars_per_vn) / 4; i++) REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_RATE_SHAPING_PER_VN_VARS_OFFSET(func) + (i * 4), ((uint32_t *)(&m_rs_vn))[i]); for (i = 0; i < sizeof(struct fairness_vars_per_vn) / 4; i++) REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_FAIRNESS_PER_VN_VARS_OFFSET(func) + (i * 4), ((uint32_t *)(&m_fair_vn))[i]); DBEXIT(BXE_VERBOSE_LOAD); } static void bxe_congestionmgmt(struct bxe_softc *sc, uint8_t readshm) { int vn; DBENTER(BXE_VERBOSE_LOAD); /* Read mf conf from shmem. */ if (readshm) bxe_read_mf_cfg(sc); /* Init rate shaping and fairness contexts */ bxe_init_port_minmax(sc); /* vn_weight_sum and enable fairness if not 0 */ bxe_calc_vn_wsum(sc); /* calculate and set min-max rate for each vn */ for (vn = 0; vn < E1HVN_MAX; vn++) bxe_init_vn_minmax(sc, vn); /* Always enable rate shaping and fairness. */ sc->cmng.flags.cmng_enables |= CMNG_FLAGS_PER_PORT_RATE_SHAPING_VN; DBPRINT(sc, BXE_VERBOSE_LOAD, "%s(): Rate shaping set\n", __FUNCTION__); if (!sc->vn_wsum) DBPRINT(sc, BXE_INFO_LOAD, "%s(): All MIN values " "are zeroes, fairness is disabled\n", __FUNCTION__); DBEXIT(BXE_VERBOSE_LOAD); } static void bxe_dcc_event(struct bxe_softc *sc, uint32_t dcc_event) { int i, port; DBENTER(BXE_VERBOSE_LOAD); if (dcc_event & DRV_STATUS_DCC_DISABLE_ENABLE_PF) { if (sc->mf_config[BP_E1HVN(sc)] & FUNC_MF_CFG_FUNC_DISABLED) { DBPRINT(sc, BXE_INFO_LOAD, "%s(): mf_cfg function " "disabled\n", __FUNCTION__); sc->state = BXE_STATE_DISABLED; bxe_e1h_disable(sc); } else { DBPRINT(sc, BXE_INFO_LOAD, "%s(): mf_cfg function " "enabled\n", __FUNCTION__); sc->state = BXE_STATE_OPEN; bxe_e1h_enable(sc); } dcc_event &= ~DRV_STATUS_DCC_DISABLE_ENABLE_PF; } if (dcc_event & DRV_STATUS_DCC_BANDWIDTH_ALLOCATION) { port = BP_PORT(sc); bxe_congestionmgmt(sc, TRUE); for (i = 0; i < sizeof(struct cmng_struct_per_port) / 4; i++) REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_CMNG_PER_PORT_VARS_OFFSET(port) + i*4, ((uint32_t *)(&sc->cmng))[i]); dcc_event &= ~DRV_STATUS_DCC_BANDWIDTH_ALLOCATION; } /* Report results to MCP */ if (dcc_event) bxe_fw_command(sc, DRV_MSG_CODE_DCC_FAILURE); else bxe_fw_command(sc, DRV_MSG_CODE_DCC_OK); DBEXIT(BXE_VERBOSE_LOAD); } /* * Device probe function. * * Compares the device to the driver's list of supported devices and * reports back to the OS whether this is the right driver for the device. * * Returns: * BUS_PROBE_DEFAULT on success, positive value on failure. */ static int bxe_probe(device_t dev) { struct bxe_softc *sc; struct bxe_type *t; char *descbuf; uint16_t did, sdid, svid, vid; sc = device_get_softc(dev); sc->dev = dev; t = bxe_devs; /* Get the data for the device to be probed. */ vid = pci_get_vendor(dev); did = pci_get_device(dev); svid = pci_get_subvendor(dev); sdid = pci_get_subdevice(dev); DBPRINT(sc, BXE_VERBOSE_LOAD, "%s(); VID = 0x%04X, DID = 0x%04X, SVID = 0x%04X, " "SDID = 0x%04X\n", __FUNCTION__, vid, did, svid, sdid); /* Look through the list of known devices for a match. */ while (t->bxe_name != NULL) { if ((vid == t->bxe_vid) && (did == t->bxe_did) && ((svid == t->bxe_svid) || (t->bxe_svid == PCI_ANY_ID)) && ((sdid == t->bxe_sdid) || (t->bxe_sdid == PCI_ANY_ID))) { descbuf = malloc(BXE_DEVDESC_MAX, M_TEMP, M_NOWAIT); if (descbuf == NULL) return (ENOMEM); /* Print out the device identity. */ snprintf(descbuf, BXE_DEVDESC_MAX, "%s (%c%d) BXE v:%s\n", t->bxe_name, (((pci_read_config(dev, PCIR_REVID, 4) & 0xf0) >> 4) + 'A'), (pci_read_config(dev, PCIR_REVID, 4) & 0xf), BXE_DRIVER_VERSION); device_set_desc_copy(dev, descbuf); free(descbuf, M_TEMP); return (BUS_PROBE_DEFAULT); } t++; } return (ENXIO); } /* * Prints useful adapter info. * * Returns: * None. */ /* ToDo: Create a sysctl for this info. */ static void bxe_print_adapter_info(struct bxe_softc *sc) { int i = 0; DBENTER(BXE_EXTREME_LOAD); /* Hardware chip info. */ BXE_PRINTF("ASIC (0x%08X); ", sc->common.chip_id); printf("Rev (%c%d); ", (CHIP_REV(sc) >> 12) + 'A', (CHIP_METAL(sc) >> 4)); /* Bus info. */ printf("Bus (PCIe x%d, ", sc->pcie_link_width); switch (sc->pcie_link_speed) { case 1: printf("2.5Gbps"); break; case 2: printf("5Gbps"); break; default: printf("Unknown link speed"); } /* Device features. */ printf("); Flags ("); /* Miscellaneous flags. */ if (sc->msi_count > 0) printf("MSI"); if (sc->msix_count > 0) { if (i > 0) printf("|"); printf("MSI-X"); i++; } if (TPA_ENABLED(sc)) { if (i > 0) printf("|"); printf("TPA"); i++; } printf("); Queues ("); switch (sc->multi_mode) { case ETH_RSS_MODE_DISABLED: printf("None"); break; case ETH_RSS_MODE_REGULAR: printf("RSS:%d", sc->num_queues); break; default: printf("Unknown"); break; } printf("); BD's (RX:%d,TX:%d", (int) USABLE_RX_BD, (int) USABLE_TX_BD); /* Firmware versions and device features. */ printf("); Firmware (%d.%d.%d); Bootcode (%d.%d.%d)\n", BCM_5710_FW_MAJOR_VERSION, BCM_5710_FW_MINOR_VERSION, BCM_5710_FW_REVISION_VERSION, (int)((sc->common.bc_ver & 0xff0000) >> 16), (int)((sc->common.bc_ver & 0x00ff00) >> 8), (int)((sc->common.bc_ver & 0x0000ff))); DBEXIT(BXE_EXTREME_LOAD); } /* * Release any interrupts allocated by the driver. * * Returns: * None */ static void bxe_interrupt_free(struct bxe_softc *sc) { device_t dev; int i; DBENTER(BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); dev = sc->dev; if (sc->msix_count > 0) { /* Free MSI-X resources. */ for (i = 0; i < sc->msix_count; i++) { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR), "%s(): Releasing MSI-X[%d] " "vector.\n", __FUNCTION__, i); if (sc->bxe_msix_res[i] && sc->bxe_msix_rid[i]) bus_release_resource(dev, SYS_RES_IRQ, sc->bxe_msix_rid[i], sc->bxe_msix_res[i]); } pci_release_msi(dev); } else if (sc->msi_count > 0) { /* Free MSI resources. */ for (i = 0; i < sc->msi_count; i++) { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR), "%s(): Releasing MSI[%d] " "vector.\n", __FUNCTION__, i); if (sc->bxe_msi_res[i] && sc->bxe_msi_rid[i]) bus_release_resource(dev, SYS_RES_IRQ, sc->bxe_msi_rid[i], sc->bxe_msi_res[i]); } pci_release_msi(dev); } else { /* Free legacy interrupt resources. */ DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR), "%s(): Releasing legacy interrupt.\n", __FUNCTION__); if (sc->bxe_irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, sc->bxe_irq_rid, sc->bxe_irq_res); } DBEXIT(BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } /* * This function determines and allocates the appropriate * interrupt based on system capabilites and user request. * * The user may force a particular interrupt mode, specify * the number of receive queues, specify the method for * distribuitng received frames to receive queues, or use * the default settings which will automatically select the * best supported combination. In addition, the OS may or * may not support certain combinations of these settings. * This routine attempts to reconcile the settings requested * by the user with the capabilites available from the system * to select the optimal combination of features. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_interrupt_alloc(struct bxe_softc *sc) { device_t dev; int error, i, rid, rc; int msi_count, msi_required, msi_allocated; int msix_count, msix_required, msix_allocated; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR); rc = 0; dev = sc->dev; msi_count = msi_required = msi_allocated = 0; msix_count = msix_required = msix_allocated = 0; /* Get the number of available MSI/MSI-X interrupts from the OS. */ if (sc->int_mode > 0) { if (sc->bxe_cap_flags & BXE_MSIX_CAPABLE_FLAG) msix_count = pci_msix_count(dev); if (sc->bxe_cap_flags & BXE_MSI_CAPABLE_FLAG) msi_count = pci_msi_count(dev); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): %d MSI and %d MSI-X vectors available.\n", __FUNCTION__, msi_count, msix_count); } /* Try allocating MSI-X interrupt resources. */ if ((sc->bxe_cap_flags & BXE_MSIX_CAPABLE_FLAG) && (sc->int_mode > 1) && (msix_count > 0) && (msix_count >= sc->num_queues)) { /* Ask for the necessary number of MSI-X vectors. */ if (sc->num_queues == 1) msix_allocated = msix_required = 2; else msix_allocated = msix_required = sc->num_queues + 1; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Requesting %d MSI-X vectors.\n", __FUNCTION__, msix_required); /* BSD resource identifier */ rid = 1; error = pci_alloc_msix(dev, &msix_allocated); if (error == 0) { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Required/Allocated (%d/%d) MSI-X vector(s).\n", __FUNCTION__, msix_required, msix_allocated); /* Make sure we got all the interrupts we asked for. */ if (msix_allocated >= msix_required) { sc->msix_count = msix_required; msi_count = 0; /* Allocate the MSI-X vectors. */ for (i = 0; i < msix_required; i++) { sc->bxe_msix_rid[i] = rid + i + BP_L_ID(sc); sc->bxe_msix_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->bxe_msix_rid[i], RF_ACTIVE); /* Report any IRQ allocation errors. */ if (sc->bxe_msix_res[i] == NULL) { BXE_PRINTF( "%s(%d): Failed to map MSI-X[%d] vector!\n", __FILE__, __LINE__, (3)); rc = ENXIO; goto bxe_interrupt_alloc_exit; } } } else { DBPRINT(sc, BXE_WARN, "%s(): MSI-X allocation failed!\n", __FUNCTION__); /* Release any resources acquired. */ pci_release_msi(dev); sc->msix_count = msix_count = 0; /* We'll try MSI next. */ sc->int_mode = 1; } } } /* Try allocating MSI vector resources. */ if ((sc->bxe_cap_flags & BXE_MSI_CAPABLE_FLAG) && (sc->int_mode > 0) && (msi_count > 0) && (msi_count >= sc->num_queues)) { /* Ask for the necessary number of MSI vectors. */ if (sc->num_queues == 1) msi_required = msi_allocated = 1; else msi_required = msi_allocated = BXE_MSI_VECTOR_COUNT; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Requesting %d MSI vectors.\n", __FUNCTION__, msi_required); rid = 1; error = pci_alloc_msi(dev, &msi_allocated); if (error == 0) { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Required/Allocated (%d/%d) MSI vector(s).\n", __FUNCTION__, msi_required, msi_allocated); /* * Make sure we got all the vectors we asked for. * XXX * FreeBSD always gives 8 even if we ask for less. */ if (msi_required >= msi_allocated) { sc->msi_count = msi_required; /* Allocate the MSI vectors. */ for (i = 0; i < msi_required; i++) { sc->bxe_msi_rid[i] = i + rid; sc->bxe_msi_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->bxe_msi_rid[i], RF_ACTIVE); /* Report any IRQ allocation errors. */ if (sc->bxe_msi_res[i] == NULL) { BXE_PRINTF( "%s(%d): Failed to map MSI vector (%d)!\n", __FILE__, __LINE__, (i)); rc = ENXIO; goto bxe_interrupt_alloc_exit; } } } } else { DBPRINT(sc, BXE_WARN, "%s(): MSI allocation failed!\n", __FUNCTION__); /* Release any resources acquired. */ pci_release_msi(dev); sc->msi_count = msi_count = 0; /* We'll try INTx next. */ sc->int_mode = 0; } } /* Try allocating INTx resources. */ if (sc->int_mode == 0) { sc->num_queues = 1; sc->multi_mode = ETH_RSS_MODE_DISABLED; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Requesting legacy INTx interrupt.\n", __FUNCTION__); rid = 0; sc->bxe_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); /* Report any IRQ allocation errors. */ if (sc->bxe_irq_res == NULL) { BXE_PRINTF("%s(%d): PCI map interrupt failed!\n", __FILE__, __LINE__); rc = ENXIO; goto bxe_interrupt_alloc_exit; } sc->bxe_irq_rid = rid; } DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Actual: int_mode = %d, multi_mode = %d, num_queues = %d\n", __FUNCTION__, sc->int_mode, sc->multi_mode, sc->num_queues); bxe_interrupt_alloc_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR); return (rc); } /* * This function releases taskqueues. * * Returns: * None */ static void bxe_interrupt_detach(struct bxe_softc *sc) { #ifdef BXE_TASK struct bxe_fastpath *fp; #endif device_t dev; int i; DBENTER(BXE_VERBOSE_UNLOAD); dev = sc->dev; #ifdef BXE_TASK /* Free the OS taskqueue resources. */ for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; if (fp->tq != NULL) { taskqueue_drain(fp->tq, &fp->task); taskqueue_free(fp->tq); } } if (sc->tq != NULL) { taskqueue_drain(sc->tq, &sc->task); taskqueue_free(sc->tq); } #endif /* Release interrupt resources. */ if (sc->msix_count > 0) { for (i = 0; i < sc->msix_count; i++) { if (sc->bxe_msix_tag[i] && sc->bxe_msix_res[i]) bus_teardown_intr(dev, sc->bxe_msix_res[i], sc->bxe_msix_tag[i]); } } else if (sc->msi_count > 0) { for (i = 0; i < sc->msi_count; i++) { if (sc->bxe_msi_tag[i] && sc->bxe_msi_res[i]) bus_teardown_intr(dev, sc->bxe_msi_res[i], sc->bxe_msi_tag[i]); } } else { if (sc->bxe_irq_tag != NULL) bus_teardown_intr(dev, sc->bxe_irq_res, sc->bxe_irq_tag); } DBEXIT(BXE_VERBOSE_UNLOAD); } /* * This function enables interrupts and attachs to the ISR. * * When using multiple MSI/MSI-X vectors the first vector * is used for slowpath operations while all remaining * vectors are used for fastpath operations. If only a * single MSI/MSI-X vector is used (SINGLE_ISR) then the * ISR must look for both slowpath and fastpath completions. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_interrupt_attach(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i, rc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR); rc = 0; #ifdef BXE_TASK /* Setup the slowpath deferred task queue. */ TASK_INIT(&sc->task, 0, bxe_task_sp, sc); sc->tq = taskqueue_create_fast("bxe_spq", M_NOWAIT, taskqueue_thread_enqueue, &sc->tq); taskqueue_start_threads(&sc->tq, 1, PI_NET, "%s spq", device_get_nameunit(sc->dev)); #endif /* Setup interrupt handlers. */ if (sc->msix_count > 0) { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Enabling slowpath MSI-X[0] vector.\n",__FUNCTION__); /* * Setup the interrupt handler. Note that we pass the * driver instance to the interrupt handler for the * slowpath. */ rc = bus_setup_intr(sc->dev, sc->bxe_msix_res[0], INTR_TYPE_NET | INTR_MPSAFE, NULL, bxe_intr_sp, sc, &sc->bxe_msix_tag[0]); if (rc) { BXE_PRINTF( "%s(%d): Failed to allocate MSI-X[0] vector!\n", __FILE__, __LINE__); goto bxe_interrupt_attach_exit; } #if __FreeBSD_version >= 800504 bus_describe_intr(sc->dev, sc->bxe_msix_res[0], sc->bxe_msix_tag[0], "sp"); #endif /* Now initialize the fastpath vectors. */ for (i = 0; i < (sc->num_queues); i++) { fp = &sc->fp[i]; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Enabling MSI-X[%d] vector.\n", __FUNCTION__, i + 1); /* * Setup the interrupt handler. Note that we pass the * fastpath context to the interrupt handler in this * case. Also the first msix_res was used by the sp. */ rc = bus_setup_intr(sc->dev, sc->bxe_msix_res[i + 1], INTR_TYPE_NET | INTR_MPSAFE, NULL, bxe_intr_fp, fp, &sc->bxe_msix_tag[i + 1]); if (rc) { BXE_PRINTF( "%s(%d): Failed to allocate MSI-X[%d] vector!\n", __FILE__, __LINE__, (i + 1)); goto bxe_interrupt_attach_exit; } #if __FreeBSD_version >= 800504 bus_describe_intr(sc->dev, sc->bxe_msix_res[i + 1], sc->bxe_msix_tag[i + 1], "fp[%02d]", i); #endif /* Bind the fastpath instance to a CPU. */ if (sc->num_queues > 1) { bus_bind_intr(sc->dev, sc->bxe_msix_res[i + 1], i); } #ifdef BXE_TASK TASK_INIT(&fp->task, 0, bxe_task_fp, fp); fp->tq = taskqueue_create_fast("bxe_fpq", M_NOWAIT, taskqueue_thread_enqueue, &fp->tq); taskqueue_start_threads(&fp->tq, 1, PI_NET, "%s fpq", device_get_nameunit(sc->dev)); #endif fp->state = BXE_FP_STATE_IRQ; } } else if (sc->msi_count > 0) { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Enabling slowpath MSI[0] vector.\n", __FUNCTION__); /* * Setup the interrupt handler. Note that we pass the driver * instance to the interrupt handler for the slowpath. */ rc = bus_setup_intr(sc->dev,sc->bxe_msi_res[0], INTR_TYPE_NET | INTR_MPSAFE, NULL, bxe_intr_sp, sc, &sc->bxe_msi_tag[0]); if (rc) { BXE_PRINTF( "%s(%d): Failed to allocate MSI[0] vector!\n", __FILE__, __LINE__); goto bxe_interrupt_attach_exit; } #if __FreeBSD_version >= 800504 bus_describe_intr(sc->dev, sc->bxe_msi_res[0], sc->bxe_msi_tag[0], "sp"); #endif /* Now initialize the fastpath vectors. */ for (i = 0; i < (sc->num_queues); i++) { fp = &sc->fp[i]; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Enabling MSI[%d] vector.\n", __FUNCTION__, i + 1); /* * Setup the interrupt handler. Note that we pass the * fastpath context to the interrupt handler in this * case. */ rc = bus_setup_intr(sc->dev, sc->bxe_msi_res[i + 1], INTR_TYPE_NET | INTR_MPSAFE, NULL, bxe_intr_fp, fp, &sc->bxe_msi_tag[i + 1]); if (rc) { BXE_PRINTF( "%s(%d): Failed to allocate MSI[%d] vector!\n", __FILE__, __LINE__, (i + 1)); goto bxe_interrupt_attach_exit; } #if __FreeBSD_version >= 800504 bus_describe_intr(sc->dev, sc->bxe_msi_res[i + 1], sc->bxe_msi_tag[i + 1], "fp[%02d]", i); #endif #ifdef BXE_TASK TASK_INIT(&fp->task, 0, bxe_task_fp, fp); fp->tq = taskqueue_create_fast("bxe_fpq", M_NOWAIT, taskqueue_thread_enqueue, &fp->tq); taskqueue_start_threads(&fp->tq, 1, PI_NET, "%s fpq", device_get_nameunit(sc->dev)); #endif } } else { #ifdef BXE_TASK fp = &sc->fp[0]; #endif DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Enabling INTx interrupts.\n", __FUNCTION__); /* * Setup the interrupt handler. Note that we pass the * driver instance to the interrupt handler which * will handle both the slowpath and fastpath. */ rc = bus_setup_intr(sc->dev,sc->bxe_irq_res, INTR_TYPE_NET | INTR_MPSAFE, NULL, bxe_intr_legacy, sc, &sc->bxe_irq_tag); if (rc) { BXE_PRINTF("%s(%d): Failed to allocate interrupt!\n", __FILE__, __LINE__); goto bxe_interrupt_attach_exit; } #ifdef BXE_TASK TASK_INIT(&fp->task, 0, bxe_task_fp, fp); fp->tq = taskqueue_create_fast("bxe_fpq", M_NOWAIT, taskqueue_thread_enqueue, &fp->tq); taskqueue_start_threads(&fp->tq, 1, PI_NET, "%s fpq", device_get_nameunit(sc->dev)); #endif } bxe_interrupt_attach_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR); return (rc); } /* * PCI Capabilities Probe Function. * * Walks the PCI capabiites list for the device to find what features are * supported. These capabilites may be enabled/disabled by firmware so it's * best to walk the list rather than hard code any values. * * Returns: * None. */ static void bxe_probe_pci_caps(struct bxe_softc *sc) { device_t dev; uint32_t reg; uint16_t link_status; dev = sc->dev; DBENTER(BXE_EXTREME_LOAD); /* Check if PCI Power Management capability is enabled. */ if (pci_find_cap(dev, PCIY_PMG, ®) == 0) { if (reg != 0) { DBPRINT(sc, BXE_EXTREME_LOAD, "%s(): Found PM capability at 0x%04X\n", __FUNCTION__, reg); sc->pm_cap = reg; } } /* Check if PCIe capability is enabled. */ if (pci_find_cap(dev, PCIY_EXPRESS, ®) == 0) { if (reg != 0) { link_status = pci_read_config(dev, reg + 0x12, 2); DBPRINT(sc, BXE_EXTREME_LOAD, "%s(): Found PCIe capability at 0x%04X\n", __FUNCTION__, reg); /* Handle PCIe 2.0 workarounds for the 57710. */ if (CHIP_IS_E1(sc)) { /* Workaround for 57710 errata E4_57710_27462. */ sc->pcie_link_speed = (REG_RD(sc, 0x3d04) & (1 << 24)) ? 2 : 1; /* Workaround for 57710 errata E4_57710_27488. */ sc->pcie_link_width = (link_status >> 4) & 0x3f; if (sc->pcie_link_speed > 1) sc->pcie_link_width = ((link_status >> 4) & 0x3f) >> 1; } else { sc->pcie_link_speed = link_status & 0xf; sc->pcie_link_width = (link_status >> 4) & 0x3f; } sc->bxe_cap_flags |= BXE_PCIE_CAPABLE_FLAG; sc->pcie_cap = reg; } } /* Check if MSI capability is enabled. */ if (pci_find_cap(dev, PCIY_MSI, ®) == 0) { if (reg != 0) { DBPRINT(sc, BXE_EXTREME_LOAD, "%s(): Found MSI capability at 0x%04X\n", __FUNCTION__, reg); sc->bxe_cap_flags |= BXE_MSI_CAPABLE_FLAG; } } /* Check if MSI-X capability is enabled. */ if (pci_find_cap(dev, PCIY_MSIX, ®) == 0) { if (reg != 0) { DBPRINT(sc, BXE_EXTREME_LOAD, "%s(): Found MSI-X capability at 0x%04X\n", __FUNCTION__, reg); sc->bxe_cap_flags |= BXE_MSIX_CAPABLE_FLAG; } } DBEXIT(BXE_EXTREME_LOAD); } /* * Setup firmware pointers for BCM57710. * * Returns: * None */ static void bxe_init_e1_firmware(struct bxe_softc *sc) { INIT_OPS(sc) = (struct raw_op *)init_ops_e1; INIT_DATA(sc) = (const uint32_t *)init_data_e1; INIT_OPS_OFFSETS(sc) = (const uint16_t *)init_ops_offsets_e1; INIT_TSEM_INT_TABLE_DATA(sc) = tsem_int_table_data_e1; INIT_TSEM_PRAM_DATA(sc) = tsem_pram_data_e1; INIT_USEM_INT_TABLE_DATA(sc) = usem_int_table_data_e1; INIT_USEM_PRAM_DATA(sc) = usem_pram_data_e1; INIT_XSEM_INT_TABLE_DATA(sc) = xsem_int_table_data_e1; INIT_XSEM_PRAM_DATA(sc) = xsem_pram_data_e1; INIT_CSEM_INT_TABLE_DATA(sc) = csem_int_table_data_e1; INIT_CSEM_PRAM_DATA(sc) = csem_pram_data_e1; } /* * Setup firmware pointers for BCM57711. * * Returns: * None */ static void bxe_init_e1h_firmware(struct bxe_softc *sc) { INIT_OPS(sc) = (struct raw_op *)init_ops_e1h; INIT_DATA(sc) = (const uint32_t *)init_data_e1h; INIT_OPS_OFFSETS(sc) = (const uint16_t *)init_ops_offsets_e1h; INIT_TSEM_INT_TABLE_DATA(sc) = tsem_int_table_data_e1h; INIT_TSEM_PRAM_DATA(sc) = tsem_pram_data_e1h; INIT_USEM_INT_TABLE_DATA(sc) = usem_int_table_data_e1h; INIT_USEM_PRAM_DATA(sc) = usem_pram_data_e1h; INIT_XSEM_INT_TABLE_DATA(sc) = xsem_int_table_data_e1h; INIT_XSEM_PRAM_DATA(sc) = xsem_pram_data_e1h; INIT_CSEM_INT_TABLE_DATA(sc) = csem_int_table_data_e1h; INIT_CSEM_PRAM_DATA(sc) = csem_pram_data_e1h; } /* * Sets up pointers for loading controller firmware. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_init_firmware(struct bxe_softc *sc) { int rc; rc = 0; if (CHIP_IS_E1(sc)) bxe_init_e1_firmware(sc); else if (CHIP_IS_E1H(sc)) bxe_init_e1h_firmware(sc); else { BXE_PRINTF("%s(%d): No firmware to support chip revision!\n", __FILE__, __LINE__); rc = ENXIO; } return (rc); } static void bxe_tunables_set(struct bxe_softc *sc) { /* * Get our starting point for interrupt mode/number of queues. * We will progressively step down from MSI-X to MSI to INTx * and reduce the number of receive queues as necessary to * match the system capabilities. */ sc->multi_mode = bxe_multi_mode; sc->int_mode = bxe_int_mode; sc->tso_enable = bxe_tso_enable; /* * Verify the Priority -> Receive Queue mappings. */ if (sc->int_mode > 0) { /* Multi-queue modes require MSI/MSI-X. */ switch (sc->multi_mode) { case ETH_RSS_MODE_DISABLED: /* No multi-queue mode requested. */ sc->num_queues = 1; break; case ETH_RSS_MODE_REGULAR: if (sc->int_mode > 1) { /* * Assume we can use MSI-X * (max of 16 receive queues). */ sc->num_queues = min((bxe_queue_count ? bxe_queue_count : mp_ncpus), MAX_CONTEXT); } else { /* * Assume we can use MSI * (max of 7 receive queues). */ sc->num_queues = min((bxe_queue_count ? bxe_queue_count : mp_ncpus), BXE_MSI_VECTOR_COUNT - 1); } break; default: BXE_PRINTF( "%s(%d): Unsupported multi_mode parameter (%d), " "disabling multi-queue support!\n", __FILE__, __LINE__, sc->multi_mode); sc->multi_mode = ETH_RSS_MODE_DISABLED; sc->num_queues = 1; break; } } else { /* User has forced INTx mode. */ sc->multi_mode = ETH_RSS_MODE_DISABLED; sc->num_queues = 1; } DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Requested: int_mode = %d, multi_mode = %d num_queues = %d\n", __FUNCTION__, sc->int_mode, sc->multi_mode, sc->num_queues); sc->stats_enable = TRUE; /* Select the host coalescing tick count values (limit values). */ if (bxe_tx_ticks > 100) { BXE_PRINTF("%s(%d): bxe_tx_ticks too large " "(%d), setting default value of 50.\n", __FILE__, __LINE__, bxe_tx_ticks); sc->tx_ticks = 50; } else sc->tx_ticks = bxe_tx_ticks; if (bxe_rx_ticks > 100) { BXE_PRINTF("%s(%d): bxe_rx_ticks too large " "(%d), setting default value of 25.\n", __FILE__, __LINE__, bxe_rx_ticks); sc->rx_ticks = 25; } else sc->rx_ticks = bxe_rx_ticks; /* Select the PCIe maximum read request size (MRRS). */ if (bxe_mrrs > 3) sc->mrrs = 3; else sc->mrrs = bxe_mrrs; /* Check for DCC support. */ if (bxe_dcc_enable == FALSE) sc->dcc_enable = FALSE; else sc->dcc_enable = TRUE; } /* * Allocates PCI resources from OS. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_pci_resources_alloc(struct bxe_softc *sc) { int rid, rc = 0; DBENTER(BXE_VERBOSE_LOAD); /* * Allocate PCI memory resources for BAR0. * This includes device registers and internal * processor memory. */ rid = PCIR_BAR(0); sc->bxe_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->bxe_res == NULL) { BXE_PRINTF("%s(%d):PCI BAR0 memory allocation failed\n", __FILE__, __LINE__); rc = ENXIO; goto bxe_pci_resources_alloc_exit; } /* Get OS resource handles for BAR0 memory. */ sc->bxe_btag = rman_get_bustag(sc->bxe_res); sc->bxe_bhandle = rman_get_bushandle(sc->bxe_res); sc->bxe_vhandle = (vm_offset_t) rman_get_virtual(sc->bxe_res); /* * Allocate PCI memory resources for BAR2. * Doorbell (DB) memory. */ rid = PCIR_BAR(2); sc->bxe_db_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->bxe_db_res == NULL) { BXE_PRINTF("%s(%d): PCI BAR2 memory allocation failed\n", __FILE__, __LINE__); rc = ENXIO; goto bxe_pci_resources_alloc_exit; } /* Get OS resource handles for BAR2 memory. */ sc->bxe_db_btag = rman_get_bustag(sc->bxe_db_res); sc->bxe_db_bhandle = rman_get_bushandle(sc->bxe_db_res); sc->bxe_db_vhandle = (vm_offset_t) rman_get_virtual(sc->bxe_db_res); bxe_pci_resources_alloc_exit: DBEXIT(BXE_VERBOSE_LOAD); return (rc); } /* * Frees PCI resources allocated in bxe_pci_resources_alloc(). * * Returns: * None */ static void bxe_pci_resources_free(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_UNLOAD); /* Release the PCIe BAR0 mapped memory. */ if (sc->bxe_res != NULL) { bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(0), sc->bxe_res); } /* Release the PCIe BAR2 (doorbell) mapped memory. */ if (sc->bxe_db_res != NULL) { bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2), sc->bxe_db_res); } DBENTER(BXE_VERBOSE_UNLOAD); } /* * Determines the media reported to the OS by examining * the installed PHY type. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_media_detect(struct bxe_softc *sc) { int rc; rc = 0; /* Identify supported media based on the PHY type. */ switch (XGXS_EXT_PHY_TYPE(sc->link_params.ext_phy_config)) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: DBPRINT(sc, BXE_INFO_LOAD, "%s(): Found 10GBase-CX4 media.\n", __FUNCTION__); sc->media = IFM_10G_CX4; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: /* Technically 10GBase-KR but report as 10GBase-SR*/ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727_NOC: DBPRINT(sc, BXE_INFO_LOAD, "%s(): Found 10GBase-SR media.\n", __FUNCTION__); sc->media = IFM_10G_SR; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: DBPRINT(sc, BXE_INFO_LOAD, "%s(): Found 10Gb twinax media.\n", __FUNCTION__); sc->media = IFM_10G_TWINAX; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823: DBPRINT(sc, BXE_INFO_LOAD, "%s(): Found 10GBase-T media.\n", __FUNCTION__); sc->media = IFM_10G_T; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN: default: sc->media = 0; rc = ENODEV; } return (rc); } /* * Device attach function. * * Allocates device resources, performs secondary chip identification, * resets and initializes the hardware, and initializes driver instance * variables. * * Returns: * 0 = Success, Positive value on failure. */ static int bxe_attach(device_t dev) { struct bxe_softc *sc; struct ifnet *ifp; int rc; sc = device_get_softc(dev); DBENTER(BXE_INFO_LOAD | BXE_INFO_RESET); sc->dev = dev; sc->bxe_unit = device_get_unit(dev); sc->bxe_func = pci_get_function(dev); sc->bxe_flags = 0; sc->state = BXE_STATE_CLOSED; rc = 0; DBPRINT(sc, BXE_FATAL, "%s(): ************************\n", __FUNCTION__); DBPRINT(sc, BXE_FATAL, "%s(): ** Debug mode enabled **\n", __FUNCTION__); DBPRINT(sc, BXE_FATAL, "%s(): ************************\n", __FUNCTION__); DBPRINT(sc, BXE_FATAL, "%s(): sc vaddr = 0x%08X:%08X\n", __FUNCTION__, (uint32_t) U64_HI(sc), (uint32_t) U64_LO(sc)); /* Get the user configurable values for driver load. */ bxe_tunables_set(sc); bxe_mutexes_alloc(sc); /* Prepare tick routine. */ callout_init_mtx(&sc->bxe_tick_callout, &sc->bxe_core_mtx, 0); /* Enable bus master capability */ pci_enable_busmaster(dev); /* Enable PCI BAR mapped memory for register access. */ rc = bxe_pci_resources_alloc(sc); if (rc != 0) { BXE_PRINTF("%s(%d): Error allocating PCI resources!\n", __FILE__, __LINE__); goto bxe_attach_fail; } /* Put indirect address registers into a sane state. */ pci_write_config(sc->dev, PCICFG_GRC_ADDRESS, PCICFG_VENDOR_ID_OFFSET, 4); REG_WR(sc, PXP2_REG_PGL_ADDR_88_F0 + BP_PORT(sc) * 16, 0); REG_WR(sc, PXP2_REG_PGL_ADDR_8C_F0 + BP_PORT(sc) * 16, 0); REG_WR(sc, PXP2_REG_PGL_ADDR_90_F0 + BP_PORT(sc) * 16, 0); REG_WR(sc, PXP2_REG_PGL_ADDR_94_F0 + BP_PORT(sc) * 16, 0); /* Get hardware info from shared memory and validate data. */ rc = bxe_hwinfo_function_get(sc); if (rc != 0) { DBPRINT(sc, BXE_WARN, "%s(): Failed to get hardware info!\n", __FUNCTION__); goto bxe_attach_fail; } /* Setup supported media options. */ rc = bxe_media_detect(sc); if (rc != 0) { BXE_PRINTF("%s(%d): Unknown media (PHY) type!\n", __FILE__, __LINE__); goto bxe_attach_fail; } /* Interface entrypoint for media type/status reporting. */ ifmedia_init(&sc->bxe_ifmedia, IFM_IMASK, bxe_ifmedia_upd, bxe_ifmedia_status); /* Default interface values. */ ifmedia_add(&sc->bxe_ifmedia, IFM_ETHER | sc->media | IFM_FDX, 0, NULL); ifmedia_add(&sc->bxe_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL); ifmedia_set(&sc->bxe_ifmedia, IFM_ETHER | IFM_AUTO); sc->bxe_ifmedia.ifm_media = sc->bxe_ifmedia.ifm_cur->ifm_media; /* Setup firmware arrays (firmware load comes later). */ rc = bxe_init_firmware(sc); if (rc) { BXE_PRINTF("%s(%d): Error preparing firmware load!\n", __FILE__, __LINE__); goto bxe_attach_fail; } #ifdef BXE_DEBUG /* Allocate a memory buffer for grcdump output.*/ sc->grcdump_buffer = malloc(BXE_GRCDUMP_BUF_SIZE, M_TEMP, M_NOWAIT); if (sc->grcdump_buffer == NULL) { BXE_PRINTF("%s(%d): Failed to allocate grcdump memory " "buffer!\n", __FILE__, __LINE__); rc = ENOBUFS; } #endif /* Check that NVRAM contents are valid.*/ rc = bxe_nvram_test(sc); if (rc != 0) { BXE_PRINTF("%s(%d): Failed NVRAM test!\n", __FILE__, __LINE__); goto bxe_attach_fail; } /* Allocate the appropriate interrupts.*/ rc = bxe_interrupt_alloc(sc); if (rc != 0) { BXE_PRINTF("%s(%d): Interrupt allocation failed!\n", __FILE__, __LINE__); goto bxe_attach_fail; } /* Useful for accessing unconfigured devices (i.e. factory diags).*/ if (nomcp) sc->bxe_flags |= BXE_NO_MCP_FLAG; /* If bootcode is not running only initialize port 0. */ if (nomcp && BP_PORT(sc)) { BXE_PRINTF( "%s(%d): Second device disabled (no bootcode), " "exiting...\n", __FILE__, __LINE__); rc = ENODEV; goto bxe_attach_fail; } /* Check if PXE/UNDI is still active and unload it. */ if (!NOMCP(sc)) bxe_undi_unload(sc); /* * Select the RX and TX ring sizes. The actual * ring size for TX is complicated by the fact * that a single TX frame may be broken up into * many buffer descriptors (tx_start_bd, * tx_parse_bd, tx_data_bd). In the best case, * there are always at least two BD's required * so we'll assume the best case here. */ sc->tx_ring_size = (USABLE_TX_BD >> 1); sc->rx_ring_size = USABLE_RX_BD; /* Assume receive IP/TCP/UDP checksum is enabled. */ /* ToDo: Change when IOCTL changes checksum offload? */ sc->rx_csum = 1; /* Disable WoL. */ sc->wol = 0; /* Assume a standard 1500 byte MTU size for mbuf allocations. */ sc->mbuf_alloc_size = MCLBYTES; /* Allocate DMA memory resources. */ rc = bxe_host_structures_alloc(sc->dev); if (rc != 0) { BXE_PRINTF("%s(%d): DMA memory allocation failed!\n", __FILE__, __LINE__); goto bxe_attach_fail; } /* Allocate a FreeBSD ifnet structure. */ ifp = sc->bxe_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { BXE_PRINTF("%s(%d): Interface allocation failed!\n", __FILE__, __LINE__); rc = ENXIO; goto bxe_attach_fail; } /* Initialize the FreeBSD ifnet interface. */ ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); /* Written by driver before attach, read-only afterwards. */ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; /* Driver entrypoints from the network interface. */ ifp->if_ioctl = bxe_ioctl; ifp->if_start = bxe_tx_start; #if __FreeBSD_version >= 800000 ifp->if_transmit = bxe_tx_mq_start; ifp->if_qflush = bxe_mq_flush; #endif #ifdef FreeBSD8_0 ifp->if_timer = 0; #endif ifp->if_init = bxe_init; ifp->if_hwassist = BXE_IF_HWASSIST; ifp->if_capabilities = BXE_IF_CAPABILITIES; /* TPA not enabled by default. */ ifp->if_capenable = BXE_IF_CAPABILITIES & ~IFCAP_LRO; if_initbaudrate(ifp, IF_Gbps(10)); ifp->if_snd.ifq_drv_maxlen = sc->tx_ring_size; IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); IFQ_SET_READY(&ifp->if_snd); /* Attach to the Ethernet interface list. */ ether_ifattach(ifp, sc->link_params.mac_addr); /* Attach the interrupts to the interrupt handlers. */ rc = bxe_interrupt_attach(sc); if (rc != 0) { BXE_PRINTF("%s(%d): Interrupt allocation failed!\n", __FILE__, __LINE__); goto bxe_attach_fail; } /* Print important adapter info for the user. */ bxe_print_adapter_info(sc); /* Add the supported sysctls to the kernel. */ bxe_add_sysctls(sc); bxe_attach_fail: if (rc != 0) bxe_detach(dev); DBEXIT(BXE_INFO_LOAD | BXE_INFO_RESET); return (rc); } /* * Supported link settings. * * Examines hardware configuration present in NVRAM and * determines the link settings that are supported between * the external PHY and the switch. * * Returns: * None. * * Side effects: * Sets sc->port.supported * Sets sc->link_params.phy_addr */ static void bxe_link_settings_supported(struct bxe_softc *sc, uint32_t switch_cfg) { uint32_t ext_phy_type; int port; DBENTER(BXE_VERBOSE_PHY); DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): switch_cfg = 0x%08X\n", __FUNCTION__, switch_cfg); port = BP_PORT(sc); /* Get the link settings supported by the external PHY. */ switch (switch_cfg) { case SWITCH_CFG_1G: ext_phy_type = SERDES_EXT_PHY_TYPE(sc->link_params.ext_phy_config); DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): 1G switch w/ ext_phy_type = " "0x%08X\n", __FUNCTION__, ext_phy_type); switch (ext_phy_type) { case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT: DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): 1G Direct.\n", __FUNCTION__); sc->port.supported |= (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_2500baseX_Full | SUPPORTED_TP | SUPPORTED_FIBRE | SUPPORTED_Autoneg | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482: DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): 1G 5482\n", __FUNCTION__); sc->port.supported |= (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_TP | SUPPORTED_FIBRE | SUPPORTED_Autoneg | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; default: BXE_PRINTF( "%s(%d): Bad NVRAM 1Gb PHY configuration data " "(ext_phy_config=0x%08X).\n", __FILE__, __LINE__, sc->link_params.ext_phy_config); goto bxe_link_settings_supported_exit; } sc->port.phy_addr = REG_RD(sc, NIG_REG_SERDES0_CTRL_PHY_ADDR + (port * 0x10)); DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): phy_addr = 0x%08X\n", __FUNCTION__, sc->port.phy_addr); break; case SWITCH_CFG_10G: ext_phy_type = XGXS_EXT_PHY_TYPE(sc->link_params.ext_phy_config); DBPRINT( sc, BXE_VERBOSE_PHY, "%s(): 10G switch w/ ext_phy_type = 0x%08X\n", __FUNCTION__, ext_phy_type); switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): 10G switch w/ direct connect.\n", __FUNCTION__); sc->port.supported |= (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_2500baseX_Full | SUPPORTED_10000baseT_Full | SUPPORTED_TP | SUPPORTED_FIBRE | SUPPORTED_Autoneg | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: DBPRINT(sc, BXE_VERBOSE_PHY, "ext_phy_type 0x%x (8072)\n",ext_phy_type); sc->port.supported |= (SUPPORTED_10000baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Autoneg | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: DBPRINT(sc, BXE_VERBOSE_PHY,"ext_phy_type 0x%x (8073)\n", ext_phy_type); sc->port.supported |= (SUPPORTED_10000baseT_Full | SUPPORTED_2500baseX_Full | SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Autoneg | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): 10G switch w/ 8705.\n",__FUNCTION__); sc->port.supported |= (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): 10G switch w/ 8706.\n", __FUNCTION__); sc->port.supported |= (SUPPORTED_10000baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): 10G switch w/ 8726.\n", __FUNCTION__); sc->port.supported |= (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: DBPRINT(sc, BXE_VERBOSE_PHY,"ext_phy_type 0x%x (8727)\n", ext_phy_type); sc->port.supported |= (SUPPORTED_10000baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_FIBRE | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): 10G switch w/ SFX7101.\n", __FUNCTION__); sc->port.supported |= (SUPPORTED_10000baseT_Full | SUPPORTED_TP | SUPPORTED_Autoneg | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481: DBPRINT(sc, BXE_VERBOSE_PHY, "ext_phy_type 0x%x (BCM8481)\n", ext_phy_type); sc->port.supported |= (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_10000baseT_Full | SUPPORTED_TP | SUPPORTED_Autoneg | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: DBPRINT(sc, BXE_WARN, "%s(): 10G XGXS PHY failure detected.\n", __FUNCTION__); break; BXE_PRINTF( "%s(%d): Bad NVRAM 10Gb PHY configuration data " "(ext_phy_config=0x%08X).\n", __FILE__, __LINE__, sc->link_params.ext_phy_config); goto bxe_link_settings_supported_exit; } sc->port.phy_addr = REG_RD(sc, NIG_REG_XGXS0_CTRL_PHY_ADDR +(port * 0x18)); break; default: DBPRINT(sc, BXE_WARN, "%s(): BAD switch configuration " "(link_config = 0x%08X)\n", __FUNCTION__, sc->port.link_config); goto bxe_link_settings_supported_exit; } sc->link_params.phy_addr = sc->port.phy_addr; /* Mask out unsupported speeds according to NVRAM. */ if ((sc->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_HALF) == 0) sc->port.supported &= ~SUPPORTED_10baseT_Half; if ((sc->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL) == 0) sc->port.supported &= ~SUPPORTED_10baseT_Full; if ((sc->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_HALF) == 0) sc->port.supported &= ~SUPPORTED_100baseT_Half; if ((sc->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_FULL) == 0) sc->port.supported &= ~SUPPORTED_100baseT_Full; if ((sc->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_1G) == 0) sc->port.supported &= ~(SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full); if ((sc->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_2_5G) == 0) sc->port.supported &= ~SUPPORTED_2500baseX_Full; if ((sc->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) == 0) sc->port.supported &= ~SUPPORTED_10000baseT_Full; DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): Supported link settings = 0x%b\n", __FUNCTION__, sc->port.supported, BXE_SUPPORTED_PRINTFB); bxe_link_settings_supported_exit: DBEXIT(BXE_VERBOSE_PHY); } /* * Requested link settings. * * Returns: * None. */ static void bxe_link_settings_requested(struct bxe_softc *sc) { uint32_t ext_phy_type; DBENTER(BXE_VERBOSE_PHY); sc->link_params.req_duplex = MEDIUM_FULL_DUPLEX; switch (sc->port.link_config & PORT_FEATURE_LINK_SPEED_MASK) { case PORT_FEATURE_LINK_SPEED_AUTO: if (sc->port.supported & SUPPORTED_Autoneg) { sc->link_params.req_line_speed |= SPEED_AUTO_NEG; sc->port.advertising = sc->port.supported; } else { ext_phy_type = XGXS_EXT_PHY_TYPE( sc->link_params.ext_phy_config); if ((ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705) || (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706)) { /* Force 10G, no autonegotiation. */ sc->link_params.req_line_speed = SPEED_10000; sc->port.advertising = ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE; break; } DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. Invalid " "link_config (0x%08X) - Autoneg not supported!\n", __FUNCTION__, sc->port.link_config); goto bxe_link_settings_requested_exit; } break; case PORT_FEATURE_LINK_SPEED_10M_FULL: if (sc->port.supported & SUPPORTED_10baseT_Full) { sc->link_params.req_line_speed = SPEED_10; sc->port.advertising = ADVERTISED_10baseT_Full | ADVERTISED_TP; } else { DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. Invalid " "link_config (0x%08X) - speed_cap_mask 0x%08X\n", __FUNCTION__, sc->port.link_config, sc->link_params.speed_cap_mask); goto bxe_link_settings_requested_exit; } break; case PORT_FEATURE_LINK_SPEED_10M_HALF: if (sc->port.supported & SUPPORTED_10baseT_Half) { sc->link_params.req_line_speed = SPEED_10; sc->link_params.req_duplex = MEDIUM_HALF_DUPLEX; sc->port.advertising = ADVERTISED_10baseT_Half | ADVERTISED_TP; } else { DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. Invalid " "link_config (0x%08X) - speed_cap_mask = 0x%08X\n", __FUNCTION__, sc->port.link_config, sc->link_params.speed_cap_mask); goto bxe_link_settings_requested_exit; } break; case PORT_FEATURE_LINK_SPEED_100M_FULL: if (sc->port.supported & SUPPORTED_100baseT_Full) { sc->link_params.req_line_speed = SPEED_100; sc->port.advertising = ADVERTISED_100baseT_Full | ADVERTISED_TP; } else { DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. Invalid " "link_config (0x%08X) - speed_cap_mask = 0x%08X\n", __FUNCTION__, sc->port.link_config, sc->link_params.speed_cap_mask); goto bxe_link_settings_requested_exit; } break; case PORT_FEATURE_LINK_SPEED_100M_HALF: if (sc->port.supported & SUPPORTED_100baseT_Half) { sc->link_params.req_line_speed = SPEED_100; sc->link_params.req_duplex = MEDIUM_HALF_DUPLEX; sc->port.advertising = ADVERTISED_100baseT_Half | ADVERTISED_TP; } else { DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. Invalid " "link_config (0x%08X) - speed_cap_mask = 0x%08X\n", __FUNCTION__, sc->port.link_config, sc->link_params.speed_cap_mask); goto bxe_link_settings_requested_exit; } break; case PORT_FEATURE_LINK_SPEED_1G: if (sc->port.supported & SUPPORTED_1000baseT_Full) { sc->link_params.req_line_speed = SPEED_1000; sc->port.advertising = ADVERTISED_1000baseT_Full | ADVERTISED_TP; } else { DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. Invalid " "link_config (0x%08X) - speed_cap_mask = 0x%08X\n", __FUNCTION__, sc->port.link_config, sc->link_params.speed_cap_mask); goto bxe_link_settings_requested_exit; } break; case PORT_FEATURE_LINK_SPEED_2_5G: if (sc->port.supported & SUPPORTED_2500baseX_Full) { sc->link_params.req_line_speed = SPEED_2500; sc->port.advertising = ADVERTISED_2500baseX_Full | ADVERTISED_TP; } else { DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. Invalid " "link_config (0x%08X) - speed_cap_mask = 0x%08X\n", __FUNCTION__, sc->port.link_config, sc->link_params.speed_cap_mask); goto bxe_link_settings_requested_exit; } break; case PORT_FEATURE_LINK_SPEED_10G_CX4: case PORT_FEATURE_LINK_SPEED_10G_KX4: case PORT_FEATURE_LINK_SPEED_10G_KR: if (sc->port.supported & SUPPORTED_10000baseT_Full) { sc->link_params.req_line_speed = SPEED_10000; sc->port.advertising = ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE; } else { DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. Invalid " "link_config (0x%08X) - speed_cap_mask = 0x%08X\n", __FUNCTION__, sc->port.link_config, sc->link_params.speed_cap_mask); goto bxe_link_settings_requested_exit; } break; default: DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. BAD link " "speed - link_config = 0x%08X\n", __FUNCTION__, sc->port.link_config); sc->link_params.req_line_speed = 0; sc->port.advertising = sc->port.supported; break; } DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): req_line_speed = %d, req_duplex = %d\n", __FUNCTION__, sc->link_params.req_line_speed, sc->link_params.req_duplex); sc->link_params.req_flow_ctrl = sc->port.link_config & PORT_FEATURE_FLOW_CONTROL_MASK; if ((sc->link_params.req_flow_ctrl == FLOW_CTRL_AUTO) && !(sc->port.supported & SUPPORTED_Autoneg)) sc->link_params.req_flow_ctrl = FLOW_CTRL_NONE; DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): req_flow_ctrl = 0x%08X, advertising = 0x%08X\n", __FUNCTION__, sc->link_params.req_flow_ctrl, sc->port.advertising); bxe_link_settings_requested_exit: DBEXIT(BXE_VERBOSE_PHY); } /* * Get function specific hardware configuration. * * Multiple function devices such as the BCM57711E have configuration * information that is specific to each PCIe function of the controller. * The number of PCIe functions is not necessarily the same as the number * of Ethernet ports supported by the device. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_hwinfo_function_get(struct bxe_softc *sc) { uint32_t mac_hi, mac_lo, val; int func, rc; DBENTER(BXE_VERBOSE_LOAD); rc = 0; func = BP_FUNC(sc); /* Get the common hardware configuration first. */ bxe_hwinfo_common_get(sc); /* Assume no outer VLAN/multi-function support. */ sc->e1hov = sc->e1hmf = 0; /* Get config info for mf enabled devices. */ if (CHIP_IS_E1H(sc)) { sc->mf_config[BP_E1HVN(sc)] = SHMEM_RD(sc, mf_cfg.func_mf_config[func].config); val = (SHMEM_RD(sc, mf_cfg.func_mf_config[func].e1hov_tag) & FUNC_MF_CFG_E1HOV_TAG_MASK); if (val != FUNC_MF_CFG_E1HOV_TAG_DEFAULT) { sc->e1hov = (uint16_t) val; sc->e1hmf = 1; } else { if (BP_E1HVN(sc)) { rc = EPERM; goto bxe_hwinfo_function_get_exit; } } } if (!NOMCP(sc)) { bxe_hwinfo_port_get(sc); sc->fw_seq = SHMEM_RD(sc, func_mb[func].drv_mb_header) & DRV_MSG_SEQ_NUMBER_MASK; } /* * Fetch the factory configured MAC address for multi function * devices. If this is not a multi-function device then the MAC * address was already read in the bxe_hwinfo_port_get() routine. * The MAC addresses used by the port are not the same as the MAC * addressed used by the function. */ if (IS_E1HMF(sc)) { mac_hi = SHMEM_RD(sc, mf_cfg.func_mf_config[func].mac_upper); mac_lo = SHMEM_RD(sc, mf_cfg.func_mf_config[func].mac_lower); if ((mac_lo == 0) && (mac_hi == 0)) { BXE_PRINTF("%s(%d): Invalid Ethernet address!\n", __FILE__, __LINE__); rc = ENODEV; } else { sc->link_params.mac_addr[0] = (u_char)(mac_hi >> 8); sc->link_params.mac_addr[1] = (u_char)(mac_hi); sc->link_params.mac_addr[2] = (u_char)(mac_lo >> 24); sc->link_params.mac_addr[3] = (u_char)(mac_lo >> 16); sc->link_params.mac_addr[4] = (u_char)(mac_lo >> 8); sc->link_params.mac_addr[5] = (u_char)(mac_lo); } } bxe_hwinfo_function_get_exit: DBEXIT(BXE_VERBOSE_LOAD); return (rc); } /* * Get port specific hardware configuration. * * Multiple port devices such as the BCM57710 have configuration * information that is specific to each Ethernet port of the * controller. This function reads that configuration * information from the bootcode's shared memory and saves it * for future use. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_hwinfo_port_get(struct bxe_softc *sc) { int i, port, rc; uint32_t val, mac_hi, mac_lo; DBENTER(BXE_VERBOSE_LOAD); rc = 0; port = BP_PORT(sc); sc->link_params.sc = sc; sc->link_params.port = port; /* Fetch several configuration values from bootcode shared memory. */ sc->link_params.lane_config = SHMEM_RD(sc, dev_info.port_hw_config[port].lane_config); sc->link_params.ext_phy_config = SHMEM_RD(sc, dev_info.port_hw_config[port].external_phy_config); if (XGXS_EXT_PHY_TYPE(sc->link_params.ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727_NOC) { sc->link_params.ext_phy_config &= ~PORT_HW_CFG_XGXS_EXT_PHY_TYPE_MASK; sc->link_params.ext_phy_config |= PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727; sc->link_params.feature_config_flags |= FEATURE_CONFIG_BCM8727_NOC; } sc->link_params.speed_cap_mask = SHMEM_RD(sc, dev_info.port_hw_config[port].speed_capability_mask); sc->port.link_config = SHMEM_RD(sc, dev_info.port_feature_config[port].link_config); /* Read the XGXS RX/TX preemphasis values. */ for (i = 0; i < 2; i++) { val = SHMEM_RD(sc, dev_info.port_hw_config[port].xgxs_config_rx[i<<1]); sc->link_params.xgxs_config_rx[i << 1] = ((val >> 16) & 0xffff); sc->link_params.xgxs_config_rx[(i << 1) + 1] = (val & 0xffff); val = SHMEM_RD(sc, dev_info.port_hw_config[port].xgxs_config_tx[i<<1]); sc->link_params.xgxs_config_tx[i << 1] = ((val >> 16) & 0xffff); sc->link_params.xgxs_config_tx[(i << 1) + 1] = (val & 0xffff); } /* Fetch the device configured link settings. */ sc->link_params.switch_cfg = sc->port.link_config & PORT_FEATURE_CONNECTED_SWITCH_MASK; bxe_link_settings_supported(sc, sc->link_params.switch_cfg); bxe_link_settings_requested(sc); mac_hi = SHMEM_RD(sc, dev_info.port_hw_config[port].mac_upper); mac_lo = SHMEM_RD(sc, dev_info.port_hw_config[port].mac_lower); if (mac_lo == 0 && mac_hi == 0) { BXE_PRINTF("%s(%d): No Ethernet address programmed on the " "controller!\n", __FILE__, __LINE__); rc = ENODEV; } else { sc->link_params.mac_addr[0] = (u_char)(mac_hi >> 8); sc->link_params.mac_addr[1] = (u_char)(mac_hi); sc->link_params.mac_addr[2] = (u_char)(mac_lo >> 24); sc->link_params.mac_addr[3] = (u_char)(mac_lo >> 16); sc->link_params.mac_addr[4] = (u_char)(mac_lo >> 8); sc->link_params.mac_addr[5] = (u_char)(mac_lo); } DBEXIT(BXE_VERBOSE_LOAD); return (rc); } /* * Get common hardware configuration. * * Multiple port devices such as the BCM57710 have configuration * information that is shared between all ports of the Ethernet * controller. This function reads that configuration * information from the bootcode's shared memory and saves it * for future use. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_hwinfo_common_get(struct bxe_softc *sc) { uint32_t val; int rc; DBENTER(BXE_VERBOSE_LOAD); rc = 0; /* Get the chip revision. */ sc->common.chip_id = sc->link_params.chip_id = ((REG_RD(sc, MISC_REG_CHIP_NUM) & 0xffff) << 16) | ((REG_RD(sc, MISC_REG_CHIP_REV) & 0x000f) << 12) | ((REG_RD(sc, MISC_REG_CHIP_METAL) & 0xff) << 4) | ((REG_RD(sc, MISC_REG_BOND_ID) & 0xf)); DBPRINT(sc, BXE_VERBOSE_LOAD, "%s(): chip_id = 0x%08X.\n", __FUNCTION__, sc->common.chip_id); val = (REG_RD(sc, 0x2874) & 0x55); if ((sc->common.chip_id & 0x1) || (CHIP_IS_E1(sc) && val) || (CHIP_IS_E1H(sc) && (val == 0x55))) { sc->bxe_flags |= BXE_ONE_PORT_FLAG; DBPRINT(sc, BXE_VERBOSE_LOAD, "%s(): Single port device.\n", __FUNCTION__); } /* Identify enabled PCI capabilites (PCIe, MSI-X, etc.). */ bxe_probe_pci_caps(sc); /* Get the NVRAM size. */ val = REG_RD(sc, MCP_REG_MCPR_NVM_CFG4); sc->common.flash_size = (NVRAM_1MB_SIZE << (val & MCPR_NVM_CFG4_FLASH_SIZE)); DBPRINT(sc, BXE_VERBOSE_LOAD, "%s(): flash_size = 0x%08x (%dKB)\n", __FUNCTION__, sc->common.flash_size,(sc->common.flash_size >> 10)); /* Find the shared memory base address. */ sc->common.shmem_base = sc->link_params.shmem_base = REG_RD(sc, MISC_REG_SHARED_MEM_ADDR); sc->common.shmem2_base = REG_RD(sc, MISC_REG_GENERIC_CR_0); DBPRINT(sc, BXE_VERBOSE_LOAD, "%s(): shmem_base = 0x%08X\n", __FUNCTION__, sc->common.shmem_base); /* Make sure the shared memory address is valid. */ if (!sc->common.shmem_base || (sc->common.shmem_base < 0xA0000) || (sc->common.shmem_base > 0xC0000)) { BXE_PRINTF("%s(%d): MCP is not active!\n", __FILE__, __LINE__); /* ToDo: Remove the NOMCP support. */ sc->bxe_flags |= BXE_NO_MCP_FLAG; rc = ENODEV; goto bxe_hwinfo_common_get_exit; } /* Make sure the shared memory contents are valid. */ val = SHMEM_RD(sc, validity_map[BP_PORT(sc)]); if ((val & (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB)) != (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB)) { BXE_PRINTF("%s(%d): Invalid NVRAM! Bad validity " "signature.\n", __FILE__, __LINE__); rc = ENODEV; goto bxe_hwinfo_common_get_exit; } /* Read the device configuration from shared memory. */ sc->common.hw_config = SHMEM_RD(sc, dev_info.shared_hw_config.config); sc->link_params.hw_led_mode = ((sc->common.hw_config & SHARED_HW_CFG_LED_MODE_MASK) >> SHARED_HW_CFG_LED_MODE_SHIFT); /* Check if we need to override the preemphasis values. */ sc->link_params.feature_config_flags = 0; val = SHMEM_RD(sc, dev_info.shared_feature_config.config); if (val & SHARED_FEAT_CFG_OVERRIDE_PREEMPHASIS_CFG_ENABLED) sc->link_params.feature_config_flags |= FEATURE_CONFIG_OVERRIDE_PREEMPHASIS_ENABLED; else sc->link_params.feature_config_flags &= ~FEATURE_CONFIG_OVERRIDE_PREEMPHASIS_ENABLED; /* In multifunction mode, we can't support WoL on a VN. */ if (BP_E1HVN(sc) == 0) { val = REG_RD(sc, PCICFG_OFFSET + PCICFG_PM_CAPABILITY); sc->bxe_flags |= (val & PCICFG_PM_CAPABILITY_PME_IN_D3_COLD) ? 0 : BXE_NO_WOL_FLAG; } else sc->bxe_flags |= BXE_NO_WOL_FLAG; DBPRINT(sc, BXE_VERBOSE_LOAD, "%s(): %sWoL capable\n", __FUNCTION__, (sc->bxe_flags & BXE_NO_WOL_FLAG) ? "Not " : ""); /* Check bootcode version */ sc->common.bc_ver = ((SHMEM_RD(sc, dev_info.bc_rev)) >> 8); if (sc->common.bc_ver < MIN_BXE_BC_VER) { BXE_PRINTF("%s(%d): Warning: This driver needs bootcode " "0x%08X but found 0x%08X, please upgrade!\n", __FILE__, __LINE__, MIN_BXE_BC_VER, sc->common.bc_ver); rc = ENODEV; goto bxe_hwinfo_common_get_exit; } bxe_hwinfo_common_get_exit: DBEXIT(BXE_VERBOSE_LOAD); return (rc); } /* * Remove traces of PXE boot by forcing UNDI driver unload. * * Returns: * None. */ static void bxe_undi_unload(struct bxe_softc *sc) { uint32_t reset_code, swap_en, swap_val, val; int func; DBENTER(BXE_VERBOSE_LOAD); /* Check if there is any driver already loaded */ val = REG_RD(sc, MISC_REG_UNPREPARED); if (val == 0x1) { /* Check if it is the UNDI driver. */ bxe_acquire_hw_lock(sc, HW_LOCK_RESOURCE_UNDI); val = REG_RD(sc, DORQ_REG_NORM_CID_OFST); if (val == 0x7) { reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS; func = BP_FUNC(sc); DBPRINT(sc, BXE_WARN, "%s(): UNDI is active! Resetting the device.\n", __FUNCTION__); /* Clear the UNDI indication. */ REG_WR(sc, DORQ_REG_NORM_CID_OFST, 0); /* Try to unload UNDI on port 0. */ sc->bxe_func = 0; sc->fw_seq = (SHMEM_RD(sc, func_mb[sc->bxe_func].drv_mb_header) & DRV_MSG_SEQ_NUMBER_MASK); reset_code = bxe_fw_command(sc, reset_code); /* Check if UNDI is active on port 1. */ if (reset_code != FW_MSG_CODE_DRV_UNLOAD_COMMON) { /* Send "done" for previous unload. */ bxe_fw_command(sc, DRV_MSG_CODE_UNLOAD_DONE); /* Now unload on port 1. */ sc->bxe_func = 1; sc->fw_seq = (SHMEM_RD(sc, func_mb[sc->bxe_func].drv_mb_header) & DRV_MSG_SEQ_NUMBER_MASK); reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS; bxe_fw_command(sc, reset_code); } /* It's now safe to release the lock. */ bxe_release_hw_lock(sc, HW_LOCK_RESOURCE_UNDI); REG_WR(sc, (BP_PORT(sc) ? HC_REG_CONFIG_1 : HC_REG_CONFIG_0), 0x1000); REG_WR(sc, (BP_PORT(sc) ? NIG_REG_LLH1_BRB1_DRV_MASK : NIG_REG_LLH0_BRB1_DRV_MASK), 0x0); REG_WR(sc, (BP_PORT(sc) ? NIG_REG_LLH1_BRB1_NOT_MCP : NIG_REG_LLH0_BRB1_NOT_MCP), 0x0); /* Clear AEU. */ REG_WR(sc, (BP_PORT(sc) ? MISC_REG_AEU_MASK_ATTN_FUNC_1 : MISC_REG_AEU_MASK_ATTN_FUNC_0), 0); DELAY(10000); /* Save NIG port swap information. */ swap_val = REG_RD(sc, NIG_REG_PORT_SWAP); swap_en = REG_RD(sc, NIG_REG_STRAP_OVERRIDE); /* Reset the controller. */ REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, 0xd3ffffff); REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, 0x00001403); /* Take the NIG out of reset and restore swap values.*/ REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, MISC_REGISTERS_RESET_REG_1_RST_NIG); REG_WR(sc, NIG_REG_PORT_SWAP, swap_val); REG_WR(sc, NIG_REG_STRAP_OVERRIDE, swap_en); /* Send completion message to the MCP. */ bxe_fw_command(sc, DRV_MSG_CODE_UNLOAD_DONE); /* * Restore our function and firmware sequence counter. */ sc->bxe_func = func; sc->fw_seq = (SHMEM_RD(sc, func_mb[sc->bxe_func].drv_mb_header) & DRV_MSG_SEQ_NUMBER_MASK); } else bxe_release_hw_lock(sc, HW_LOCK_RESOURCE_UNDI); } DBEXIT(BXE_VERBOSE_LOAD); } /* * Device detach function. * * Stops the controller, resets the controller, and releases resources. * * Returns: * 0 on success, !0 = failure. */ static int bxe_detach(device_t dev) { struct bxe_softc *sc; struct ifnet *ifp; int rc; sc = device_get_softc(dev); DBENTER(BXE_INFO_UNLOAD); rc = 0; ifp = sc->bxe_ifp; if (ifp != NULL && ifp->if_vlantrunk != NULL) { BXE_PRINTF("%s(%d): Cannot detach while VLANs are in use.\n", __FILE__, __LINE__); rc = EBUSY; goto bxe_detach_exit; } /* Stop and reset the controller if it was open. */ if (sc->state != BXE_STATE_CLOSED) { BXE_CORE_LOCK(sc); rc = bxe_stop_locked(sc, UNLOAD_CLOSE); BXE_CORE_UNLOCK(sc); } #ifdef BXE_DEBUG /* Free memory buffer for grcdump output.*/ if (sc->grcdump_buffer != NULL) free(sc->grcdump_buffer, M_TEMP); #endif /* Clean-up any remaining interrupt resources. */ bxe_interrupt_detach(sc); bxe_interrupt_free(sc); /* Release the network interface. */ if (ifp != NULL) ether_ifdetach(ifp); ifmedia_removeall(&sc->bxe_ifmedia); /* Release all remaining resources. */ bxe_release_resources(sc); /* Free all PCI resources. */ bxe_pci_resources_free(sc); pci_disable_busmaster(dev); bxe_mutexes_free(sc); bxe_detach_exit: DBEXIT(BXE_INFO_UNLOAD); return(0); } /* * Setup a leading connection for the controller. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_setup_leading(struct bxe_softc *sc) { int rc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_RAMROD); DBPRINT(sc, BXE_VERBOSE_LOAD, "%s(): Setup leading connection " "on fp[00].\n", __FUNCTION__); /* Reset IGU state for the leading connection. */ bxe_ack_sb(sc, sc->fp[0].sb_id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); /* Post a PORT_SETUP ramrod and wait for completion. */ bxe_sp_post(sc, RAMROD_CMD_ID_ETH_PORT_SETUP, 0, 0, 0, 0); /* Wait for the ramrod to complete on the leading connection. */ rc = bxe_wait_ramrod(sc, BXE_STATE_OPEN, 0, &(sc->state), 1); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_RAMROD); return (rc); } /* * Stop the leading connection on the controller. * * Returns: * None. */ static int bxe_stop_leading(struct bxe_softc *sc) { uint16_t dsb_sp_prod_idx; int rc, timeout; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD), "%s(): Stop client connection " "on fp[00].\n", __FUNCTION__); /* Send the ETH_HALT ramrod. */ sc->fp[0].state = BXE_FP_STATE_HALTING; bxe_sp_post(sc,RAMROD_CMD_ID_ETH_HALT, 0, 0, sc->fp[0].cl_id, 0); /* Poll for the ETH_HALT ramrod on the leading connection. */ rc = bxe_wait_ramrod(sc, BXE_FP_STATE_HALTED, 0, &(sc->fp[0].state), 1); if (rc) { DBPRINT(sc, BXE_FATAL, "%s(): Timeout waiting for " "STATE_HALTED ramrod completion!\n", __FUNCTION__); goto bxe_stop_leading_exit; } /* Get the default status block SP producer index. */ dsb_sp_prod_idx = *sc->dsb_sp_prod; /* After HALT we send PORT_DELETE ramrod. */ bxe_sp_post(sc, RAMROD_CMD_ID_ETH_PORT_DEL, 0, 0, 0, 1); /* Be patient but don't wait forever. */ timeout = 500; while (dsb_sp_prod_idx == *sc->dsb_sp_prod) { if (timeout == 0) { DBPRINT(sc, BXE_FATAL, "%s(): Timeout waiting for " "PORT_DEL ramrod completion!\n", __FUNCTION__); rc = EBUSY; break; } timeout--; DELAY(1000); rmb(); } /* Update the adapter and connection states. */ sc->state = BXE_STATE_CLOSING_WAIT4_UNLOAD; sc->fp[0].state = BXE_FP_STATE_CLOSED; bxe_stop_leading_exit: return (rc); } /* * Setup a client connection when using multi-queue/RSS. * * Returns: * Nothing. */ static int bxe_setup_multi(struct bxe_softc *sc, int index) { struct bxe_fastpath *fp; int rc; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD), "%s(): Setup client connection " "on fp[%02d].\n", __FUNCTION__, index); fp = &sc->fp[index]; /* Reset IGU state. */ bxe_ack_sb(sc, fp->sb_id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); /* Post a CLIENT_SETUP ramrod. */ fp->state = BXE_FP_STATE_OPENING; bxe_sp_post(sc, RAMROD_CMD_ID_ETH_CLIENT_SETUP, index, 0, fp->cl_id, 0); /* Wait for the ramrod to complete. */ rc = bxe_wait_ramrod(sc, BXE_FP_STATE_OPEN, index, &fp->state, 1); return (rc); } /* * Stop a client connection. * * Stops an individual client connection on the device. Use * bxe_stop_leading() for the first/default connection. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_stop_multi(struct bxe_softc *sc, int index) { struct bxe_fastpath *fp; int rc; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD), "%s(): Stop client connection " "on fp[%02d].\n", __FUNCTION__, index); fp = &sc->fp[index]; /* Halt the client connection. */ fp->state = BXE_FP_STATE_HALTING; bxe_sp_post(sc, RAMROD_CMD_ID_ETH_HALT, index, 0, fp->cl_id, 0); /* Wait for the HALT ramrod completion. */ rc = bxe_wait_ramrod(sc, BXE_FP_STATE_HALTED, index, &fp->state, 1); if (rc){ BXE_PRINTF("%s(%d): fp[%02d] client ramrod halt failed!\n", __FILE__, __LINE__, index); goto bxe_stop_multi_exit; } /* Delete the CFC entry. */ bxe_sp_post(sc, RAMROD_CMD_ID_ETH_CFC_DEL, index, 0, 0, 1); /* Poll for the DELETE ramrod completion. */ rc = bxe_wait_ramrod(sc, BXE_FP_STATE_CLOSED, index, &fp->state, 1); bxe_stop_multi_exit: return (rc); } /* * Hardware lock for shared, dual-port PHYs. * * Returns: * None. */ static void bxe_acquire_phy_lock(struct bxe_softc *sc) { uint32_t ext_phy_type; DBENTER(BXE_VERBOSE_PHY); ext_phy_type = XGXS_EXT_PHY_TYPE(sc->link_params.ext_phy_config); switch(ext_phy_type){ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: bxe_acquire_hw_lock(sc, HW_LOCK_RESOURCE_MDIO); break; default: break; } DBEXIT(BXE_VERBOSE_PHY); } /* * Hardware unlock for shared, dual-port PHYs. * * Returns: * None. */ static void bxe_release_phy_lock(struct bxe_softc *sc) { uint32_t ext_phy_type; DBENTER(BXE_VERBOSE_PHY); ext_phy_type = XGXS_EXT_PHY_TYPE(sc->link_params.ext_phy_config); switch(ext_phy_type){ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: bxe_release_hw_lock(sc, HW_LOCK_RESOURCE_MDIO); break; default: break; } DBEXIT(BXE_VERBOSE_PHY); } /* * * Returns: * None. */ static void bxe__link_reset(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_PHY); if (!NOMCP(sc)) { bxe_acquire_phy_lock(sc); bxe_link_reset(&sc->link_params, &sc->link_vars, 1); bxe_release_phy_lock(sc); } else { DBPRINT(sc, BXE_WARN, "%s(): Bootcode is not running, not resetting link!\n", __FUNCTION__); } DBEXIT(BXE_VERBOSE_PHY); } /* * Stop the controller. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_stop_locked(struct bxe_softc *sc, int unload_mode) { struct ifnet *ifp; struct mac_configuration_cmd *config; struct bxe_fastpath *fp; uint32_t reset_code; uint32_t emac_base, val; uint8_t entry, *mac_addr; int count, i, port, rc; DBENTER(BXE_INFO_LOAD | BXE_INFO_RESET | BXE_INFO_UNLOAD); ifp = sc->bxe_ifp; port = BP_PORT(sc), rc = reset_code = 0; BXE_CORE_LOCK_ASSERT(sc); /* Stop the periodic tick. */ callout_stop(&sc->bxe_tick_callout); sc->state = BXE_STATE_CLOSING_WAIT4_HALT; /* Prevent any further RX traffic. */ sc->rx_mode = BXE_RX_MODE_NONE; bxe_set_storm_rx_mode(sc); /* Tell the stack the driver is stopped and TX queue is full. */ if (ifp != NULL) ifp->if_drv_flags = 0; /* Tell the bootcode to stop watching for a heartbeat. */ SHMEM_WR(sc, func_mb[BP_FUNC(sc)].drv_pulse_mb, (DRV_PULSE_ALWAYS_ALIVE | sc->fw_drv_pulse_wr_seq)); /* Stop the statistics updates. */ bxe_stats_handle(sc, STATS_EVENT_STOP); /* Wait until all TX fastpath tasks have completed. */ for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; if (fp == NULL || fp->tx_pkt_cons_sb == NULL) break; count = 1000; while (bxe_has_tx_work(fp)) { bxe_txeof(fp); if (count == 0) { BXE_PRINTF( "%s(%d): Timeout wating for fp[%02d] transmits to complete!\n", __FILE__, __LINE__, i); break; } count--; DELAY(1000); rmb(); } } /* Wait until all slowpath tasks have completed. */ count = 1000; while ((sc->spq_left != MAX_SPQ_PENDING) && count--) DELAY(1000); /* Disable Interrupts */ bxe_int_disable(sc); DELAY(1000); /* Clear the MAC addresses. */ if (CHIP_IS_E1(sc)) { config = BXE_SP(sc, mcast_config); bxe_set_mac_addr_e1(sc, 0); for (i = 0; i < config->hdr.length; i++) CAM_INVALIDATE(&config->config_table[i]); config->hdr.length = i; config->hdr.offset = BXE_MAX_MULTICAST * (1 + port); config->hdr.client_id = BP_CL_ID(sc); config->hdr.reserved1 = 0; bxe_sp_post(sc, RAMROD_CMD_ID_ETH_SET_MAC, 0, U64_HI(BXE_SP_MAPPING(sc, mcast_config)), U64_LO(BXE_SP_MAPPING(sc, mcast_config)), 0); } else { REG_WR(sc, NIG_REG_LLH0_FUNC_EN + port * 8, 0); bxe_set_mac_addr_e1h(sc, 0); for (i = 0; i < MC_HASH_SIZE; i++) REG_WR(sc, MC_HASH_OFFSET(sc, i), 0); REG_WR(sc, MISC_REG_E1HMF_MODE, 0); } /* Determine if any WoL settings needed. */ if (unload_mode == UNLOAD_NORMAL) /* Driver initiatied WoL is disabled. */ reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS; else if (sc->bxe_flags & BXE_NO_WOL_FLAG) { /* Driver initiated WoL is disabled, use OOB WoL settings. */ reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_MCP; if (CHIP_IS_E1H(sc)) REG_WR(sc, MISC_REG_E1HMF_MODE, 0); } else if (sc->wol) { emac_base = BP_PORT(sc) ? GRCBASE_EMAC0 : GRCBASE_EMAC1; mac_addr = sc->link_params.mac_addr; entry = (BP_E1HVN(sc) + 1) * 8; val = (mac_addr[0] << 8) | mac_addr[1]; EMAC_WR(sc, EMAC_REG_EMAC_MAC_MATCH + entry, val); val = (mac_addr[2] << 24) | (mac_addr[3] << 16) | (mac_addr[4] << 8) | mac_addr[5]; EMAC_WR(sc, EMAC_REG_EMAC_MAC_MATCH + entry + 4, val); reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_EN; } else { /* Prevent WoL. */ reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS; } /* Stop all non-leading client connections. */ for (i = 1; i < sc->num_queues; i++) { if (bxe_stop_multi(sc, i)){ goto bxe_stop_locked_exit; } } /* Stop the leading client connection. */ rc = bxe_stop_leading(sc); DELAY(10000); bxe_stop_locked_exit: if (NOMCP(sc)) { DBPRINT(sc, BXE_INFO, "%s(): Old No MCP load counts: %d, %d, %d\n", __FUNCTION__, load_count[0], load_count[1], load_count[2]); load_count[0]--; load_count[1 + port]--; DBPRINT(sc, BXE_INFO, "%s(): New No MCP load counts: %d, %d, %d\n", __FUNCTION__, load_count[0], load_count[1], load_count[2]); if (load_count[0] == 0) reset_code = FW_MSG_CODE_DRV_UNLOAD_COMMON; else if (load_count[1 + BP_PORT(sc)] == 0) reset_code = FW_MSG_CODE_DRV_UNLOAD_PORT; else reset_code = FW_MSG_CODE_DRV_UNLOAD_FUNCTION; } else { /* Tell MCP driver unload is complete. */ reset_code = bxe_fw_command(sc, reset_code); } if ((reset_code == FW_MSG_CODE_DRV_UNLOAD_COMMON) || (reset_code == FW_MSG_CODE_DRV_UNLOAD_PORT)) bxe__link_reset(sc); DELAY(10000); /* Reset the chip */ bxe_reset_chip(sc, reset_code); DELAY(10000); /* Report UNLOAD_DONE to MCP */ if (!NOMCP(sc)) bxe_fw_command(sc, DRV_MSG_CODE_UNLOAD_DONE); sc->port.pmf = 0; /* Free RX chains and buffers. */ bxe_clear_rx_chains(sc); /* Free TX chains and buffers. */ bxe_clear_tx_chains(sc); sc->state = BXE_STATE_CLOSED; bxe_ack_int(sc); DBEXIT(BXE_INFO_LOAD | BXE_INFO_RESET |BXE_INFO_UNLOAD); return (rc); } /* * Device shutdown function. * * Stops and resets the controller. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_shutdown(device_t dev) { struct bxe_softc *sc; sc = device_get_softc(dev); DBENTER(BXE_INFO_LOAD | BXE_INFO_RESET | BXE_INFO_UNLOAD); /* Stop the controller, but only if it was ever started. * Stopping an uninitialized controller can cause * IPMI bus errors on some systems. */ BXE_CORE_LOCK(sc); if (sc->state != BXE_STATE_CLOSED) { bxe_stop_locked(sc, UNLOAD_NORMAL); } BXE_CORE_UNLOCK(sc); DBEXIT(BXE_INFO_LOAD | BXE_INFO_RESET | BXE_INFO_UNLOAD); return (0); } /* * Prints out link speed and duplex setting to console. * * Returns: * None. */ static void bxe_link_report(struct bxe_softc *sc) { uint32_t line_speed; uint16_t vn_max_rate; DBENTER(BXE_VERBOSE_PHY); if (sc->link_vars.link_up) { /* Report the link status change to OS. */ if (sc->state == BXE_STATE_OPEN) if_link_state_change(sc->bxe_ifp, LINK_STATE_UP); line_speed = sc->link_vars.line_speed; if (IS_E1HMF(sc)){ vn_max_rate = ((sc->mf_config[BP_E1HVN(sc)] & FUNC_MF_CFG_MAX_BW_MASK) >> FUNC_MF_CFG_MAX_BW_SHIFT) * 100; if (vn_max_rate < line_speed) line_speed = vn_max_rate; } BXE_PRINTF("Link is up, %d Mbps, ", line_speed); if (sc->link_vars.duplex == MEDIUM_FULL_DUPLEX) printf("full duplex"); else printf("half duplex"); if (sc->link_vars.flow_ctrl) { if (sc->link_vars.flow_ctrl & FLOW_CTRL_RX) { printf(", receive "); if (sc->link_vars.flow_ctrl & FLOW_CTRL_TX) printf("& transmit "); } else printf(", transmit "); printf("flow control ON"); } printf("\n"); } else { /* Report the link down */ BXE_PRINTF("Link is down\n"); if_link_state_change(sc->bxe_ifp, LINK_STATE_DOWN); } DBEXIT(BXE_VERBOSE_PHY); } /* * * Returns: * None. */ static void bxe__link_status_update(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_PHY); if (sc->stats_enable == FALSE || sc->state != BXE_STATE_OPEN) return; bxe_link_status_update(&sc->link_params, &sc->link_vars); if (sc->link_vars.link_up) bxe_stats_handle(sc, STATS_EVENT_LINK_UP); else bxe_stats_handle(sc, STATS_EVENT_STOP); bxe_read_mf_cfg(sc); /* Indicate link status. */ bxe_link_report(sc); DBEXIT(BXE_VERBOSE_PHY); } /* * Calculate flow control to advertise during autonegotiation. * * Returns: * None. */ static void bxe_calc_fc_adv(struct bxe_softc *sc) { DBENTER(BXE_EXTREME_PHY); switch (sc->link_vars.ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_MASK) { case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_NONE: sc->port.advertising &= ~(ADVERTISED_Asym_Pause | ADVERTISED_Pause); break; case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH: sc->port.advertising |= (ADVERTISED_Asym_Pause | ADVERTISED_Pause); break; case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC: sc->port.advertising |= ADVERTISED_Asym_Pause; break; default: sc->port.advertising &= ~(ADVERTISED_Asym_Pause | ADVERTISED_Pause); break; } DBEXIT(BXE_EXTREME_PHY); } /* * * Returns: * */ static uint8_t bxe_initial_phy_init(struct bxe_softc *sc) { uint8_t rc; DBENTER(BXE_VERBOSE_PHY); rc = 0; if (!NOMCP(sc)) { /* * It is recommended to turn off RX flow control for 5771x * when using jumbo frames for better performance. */ if (!IS_E1HMF(sc) && (sc->mbuf_alloc_size > 5000)) sc->link_params.req_fc_auto_adv = FLOW_CTRL_TX; else sc->link_params.req_fc_auto_adv = FLOW_CTRL_BOTH; bxe_acquire_phy_lock(sc); rc = bxe_phy_init(&sc->link_params, &sc->link_vars); bxe_release_phy_lock(sc); bxe_calc_fc_adv(sc); if (sc->link_vars.link_up) { bxe_stats_handle(sc,STATS_EVENT_LINK_UP); bxe_link_report(sc); } } else { DBPRINT(sc, BXE_FATAL, "%s(): Bootcode is not running, " "not initializing link!\n", __FUNCTION__); rc = EINVAL; } DBEXIT(BXE_VERBOSE_PHY); return (rc); } #if __FreeBSD_version >= 800000 /* * Allocate buffer rings used for multiqueue. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_alloc_buf_rings(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i, rc; DBENTER(BXE_VERBOSE_LOAD); rc = 0; for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; if (fp != NULL) { fp->br = buf_ring_alloc(BXE_BR_SIZE, M_DEVBUF, M_DONTWAIT, &fp->mtx); if (fp->br == NULL) { rc = ENOMEM; goto bxe_alloc_buf_rings_exit; } } else BXE_PRINTF("%s(%d): Bug!\n", __FILE__, __LINE__); } bxe_alloc_buf_rings_exit: DBEXIT(BXE_VERBOSE_LOAD); return (rc); } /* * Releases buffer rings used for multiqueue. * * Returns: * None */ static void bxe_free_buf_rings(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i; DBENTER(BXE_VERBOSE_UNLOAD); for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; if (fp != NULL) { if (fp->br != NULL) buf_ring_free(fp->br, M_DEVBUF); } } DBEXIT(BXE_VERBOSE_UNLOAD); } #endif /* * Handles controller initialization. * * Must be called from a locked routine. Since this code * may be called from the OS it does not provide a return * error value and must clean-up it's own mess. * * Returns: * Nothing. */ static void bxe_init_locked(struct bxe_softc *sc, int load_mode) { struct ifnet *ifp; uint32_t load_code; int error, i, port; DBENTER(BXE_INFO_LOAD | BXE_INFO_RESET); BXE_CORE_LOCK_ASSERT(sc); ifp = sc->bxe_ifp; /* Skip if we're in panic mode. */ if (sc->panic) { DBPRINT(sc, BXE_WARN, "%s(): Panic mode enabled, exiting!\n", __FUNCTION__); goto bxe_init_locked_exit; } /* Check if the driver is still running and bail out if it is. */ if (ifp->if_drv_flags & IFF_DRV_RUNNING) { DBPRINT(sc, BXE_WARN, "%s(): Init called while driver is running!\n", __FUNCTION__); goto bxe_init_locked_exit; } /* * Send LOAD_REQUEST command to MCP. * The MCP will return the type of LOAD * the driver should perform. * - If it is the first port to be initialized * then all common blocks should be initialized. * - If it is not the first port to be initialized * then don't do the common block initialization. */ sc->state = BXE_STATE_OPENING_WAIT4_LOAD; if (NOMCP(sc)) { port = BP_PORT(sc); DBPRINT(sc, BXE_INFO, "%s(): Old No MCP load counts: %d, %d, %d\n", __FUNCTION__, load_count[0], load_count[1], load_count[2]); load_count[0]++; load_count[1 + port]++; DBPRINT(sc, BXE_INFO, "%s(): New No MCP load counts: %d, %d, %d\n", __FUNCTION__, load_count[0], load_count[1], load_count[2]); /* No MCP to tell us what to do. */ if (load_count[0] == 1) load_code = FW_MSG_CODE_DRV_LOAD_COMMON; else if (load_count[1 + port] == 1) load_code = FW_MSG_CODE_DRV_LOAD_PORT; else load_code = FW_MSG_CODE_DRV_LOAD_FUNCTION; } else { /* Ask the MCP what type of initialization we need to do. */ load_code = bxe_fw_command(sc, DRV_MSG_CODE_LOAD_REQ); if ((load_code == 0) || (load_code == FW_MSG_CODE_DRV_LOAD_REFUSED)) { BXE_PRINTF("%s(%d): Bootcode refused load request.!\n", __FILE__, __LINE__); goto bxe_init_locked_failed1; } } /* Keep track of whether we are controlling the port. */ if ((load_code == FW_MSG_CODE_DRV_LOAD_COMMON) || (load_code == FW_MSG_CODE_DRV_LOAD_PORT)) sc->port.pmf = 1; else sc->port.pmf = 0; /* Block any interrupts until we're ready. */ sc->intr_sem = 1; /* Initialize hardware. */ error = bxe_init_hw(sc, load_code); if (error != 0){ BXE_PRINTF("%s(%d): Hardware initialization failed, " "aborting!\n", __FILE__, __LINE__); goto bxe_init_locked_failed1; } /* Calculate and save the Ethernet MTU size. */ sc->port.ether_mtu = ifp->if_mtu + ETHER_HDR_LEN + (ETHER_VLAN_ENCAP_LEN * 2) + ETHER_CRC_LEN + 4; DBPRINT(sc, BXE_INFO, "%s(): Setting MTU = %d\n", __FUNCTION__, sc->port.ether_mtu); /* Setup the mbuf allocation size for RX frames. */ if (sc->port.ether_mtu <= MCLBYTES) sc->mbuf_alloc_size = MCLBYTES; else if (sc->port.ether_mtu <= PAGE_SIZE) sc->mbuf_alloc_size = PAGE_SIZE; else sc->mbuf_alloc_size = MJUM9BYTES; DBPRINT(sc, BXE_INFO, "%s(): mbuf_alloc_size = %d, " "max_frame_size = %d\n", __FUNCTION__, sc->mbuf_alloc_size, sc->port.ether_mtu); /* Setup NIC internals and enable interrupts. */ error = bxe_init_nic(sc, load_code); if (error != 0) { BXE_PRINTF("%s(%d): NIC initialization failed, " "aborting!\n", __FILE__, __LINE__); goto bxe_init_locked_failed1; } if ((load_code == FW_MSG_CODE_DRV_LOAD_COMMON) && (sc->common.shmem2_base)){ if (sc->dcc_enable == TRUE) { BXE_PRINTF("Enabing DCC support\n"); SHMEM2_WR(sc, dcc_support, (SHMEM_DCC_SUPPORT_DISABLE_ENABLE_PF_TLV | SHMEM_DCC_SUPPORT_BANDWIDTH_ALLOCATION_TLV)); } } #if __FreeBSD_version >= 800000 /* Allocate buffer rings for multiqueue operation. */ error = bxe_alloc_buf_rings(sc); if (error != 0) { BXE_PRINTF("%s(%d): Buffer ring initialization failed, " "aborting!\n", __FILE__, __LINE__); goto bxe_init_locked_failed1; } #endif /* Tell MCP that driver load is done. */ if (!NOMCP(sc)) { load_code = bxe_fw_command(sc, DRV_MSG_CODE_LOAD_DONE); if (!load_code) { BXE_PRINTF("%s(%d): Driver load failed! No MCP " "response to LOAD_DONE!\n", __FILE__, __LINE__); goto bxe_init_locked_failed2; } } sc->state = BXE_STATE_OPENING_WAIT4_PORT; /* Enable ISR for PORT_SETUP ramrod. */ sc->intr_sem = 0; /* Setup the leading connection for the controller. */ error = bxe_setup_leading(sc); if (error != 0) { DBPRINT(sc, BXE_FATAL, "%s(): Initial PORT_SETUP ramrod " "failed. State is not OPEN!\n", __FUNCTION__); goto bxe_init_locked_failed3; } if (CHIP_IS_E1H(sc)) { if (sc->mf_config[BP_E1HVN(sc)] & FUNC_MF_CFG_FUNC_DISABLED) { BXE_PRINTF("Multi-function mode is disabled\n"); /* sc->state = BXE_STATE_DISABLED; */ } /* Setup additional client connections for RSS/multi-queue */ if (sc->state == BXE_STATE_OPEN) { for (i = 1; i < sc->num_queues; i++) { if (bxe_setup_multi(sc, i)) { DBPRINT(sc, BXE_FATAL, "%s(): fp[%02d] CLIENT_SETUP ramrod failed! State not OPEN!\n", __FUNCTION__, i); goto bxe_init_locked_failed4; } } } } DELAY(5000); bxe_int_enable(sc); DELAY(5000); /* Initialize statistics. */ bxe_stats_init(sc); DELAY(1000); /* Load our MAC address. */ bcopy(IF_LLADDR(sc->bxe_ifp), sc->link_params.mac_addr, ETHER_ADDR_LEN); if (CHIP_IS_E1(sc)) bxe_set_mac_addr_e1(sc, 1); else bxe_set_mac_addr_e1h(sc, 1); DELAY(1000); /* Perform PHY initialization for the primary port. */ if (sc->port.pmf) bxe_initial_phy_init(sc); DELAY(1000); /* Start fastpath. */ switch (load_mode) { case LOAD_NORMAL: case LOAD_OPEN: /* Initialize the receive filters. */ bxe_set_rx_mode(sc); break; case LOAD_DIAG: /* Initialize the receive filters. */ bxe_set_rx_mode(sc); sc->state = BXE_STATE_DIAG; break; default: DBPRINT(sc, BXE_WARN, "%s(): Unknown load mode (%d)!\n", __FUNCTION__, load_mode); break; } if (!sc->port.pmf) bxe__link_status_update(sc); DELAY(1000); /* Tell the stack the driver is running. */ ifp->if_drv_flags = IFF_DRV_RUNNING; /* Schedule our periodic timer tick. */ callout_reset(&sc->bxe_tick_callout, hz, bxe_tick, sc); /* Everything went OK, go ahead and exit. */ goto bxe_init_locked_exit; bxe_init_locked_failed4: /* Try and gracefully shutdown the device because of a failure. */ for (i = 1; i < sc->num_queues; i++) bxe_stop_multi(sc, i); bxe_init_locked_failed3: bxe_stop_leading(sc); bxe_stats_handle(sc, STATS_EVENT_STOP); bxe_init_locked_failed2: bxe_int_disable(sc); bxe_init_locked_failed1: if (!NOMCP(sc)) { bxe_fw_command(sc, DRV_MSG_CODE_LOAD_DONE); bxe_fw_command(sc, DRV_MSG_CODE_UNLOAD_REQ_WOL_MCP); bxe_fw_command(sc, DRV_MSG_CODE_UNLOAD_DONE); } sc->port.pmf = 0; #if __FreeBSD_version >= 800000 bxe_free_buf_rings(sc); #endif DBPRINT(sc, BXE_WARN, "%s(): Initialization failed!\n", __FUNCTION__); bxe_init_locked_exit: DBEXIT(BXE_INFO_LOAD | BXE_INFO_RESET); } /* * Ramrod wait function. * * Waits for a ramrod command to complete. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_wait_ramrod(struct bxe_softc *sc, int state, int idx, int *state_p, int poll) { int rc, timeout; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_RAMROD); DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): %s for state 0x%08X on " "fp[%02d], currently 0x%08X.\n", __FUNCTION__, poll ? "Polling" : "Waiting", state, idx, *state_p); rc = 0; timeout = 5000; while (timeout) { /* Manually check for the completion. */ if (poll) { bxe_rxeof(sc->fp); /* * Some commands don't use the leading client * connection. */ if (idx) bxe_rxeof(&sc->fp[idx]); } /* State may be changed by bxe_sp_event(). */ mb(); if (*state_p == state) goto bxe_wait_ramrod_exit; timeout--; /* Pause 1ms before checking again. */ DELAY(1000); } /* We timed out polling for a completion. */ DBPRINT(sc, BXE_FATAL, "%s(): Timeout %s for state 0x%08X on fp[%02d]. " "Got 0x%x instead\n", __FUNCTION__, poll ? "polling" : "waiting", state, idx, *state_p); rc = EBUSY; bxe_wait_ramrod_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_RAMROD); return (rc); } /* * * */ static void bxe_write_dmae_phys_len(struct bxe_softc *sc, bus_addr_t phys_addr, uint32_t addr, uint32_t len) { int dmae_wr_max, offset; DBENTER(BXE_INSANE_REGS); dmae_wr_max = DMAE_LEN32_WR_MAX(sc); offset = 0; while (len > dmae_wr_max) { bxe_write_dmae(sc, phys_addr + offset, addr + offset, dmae_wr_max); offset += dmae_wr_max * 4; len -= dmae_wr_max; } bxe_write_dmae(sc, phys_addr + offset, addr + offset, len); DBEXIT(BXE_INSANE_REGS); } #define INIT_MEM_WR(block, reg, part, hw, data, reg_off, len) \ bxe_init_str_wr(sc, GRCBASE_##block + reg + reg_off * 4, data, len) /* * Write a block of data to a range of registers. * * Returns: * None. */ static void bxe_init_str_wr(struct bxe_softc *sc, uint32_t addr, const uint32_t *data, uint32_t len) { uint32_t i; for (i = 0; i < len; i++) REG_WR(sc, addr + i * 4, data[i]); } /* * Write a block of data to a range of registers using indirect access. * * Returns: * None. */ static void bxe_init_ind_wr(struct bxe_softc *sc, uint32_t addr, const uint32_t *data, uint16_t len) { uint32_t i; for (i = 0; i < len; i++) REG_WR_IND(sc, addr + i * 4, data[i]); } /* * * Returns: * None. */ static void bxe_write_big_buf(struct bxe_softc *sc, uint32_t addr, uint32_t len) { DBENTER(BXE_INSANE_REGS); #ifdef BXE_USE_DMAE if (sc->dmae_ready) bxe_write_dmae_phys_len(sc, sc->gz_dma.paddr, addr, len); else bxe_init_str_wr(sc, addr, sc->gz, len); #else bxe_init_str_wr(sc, addr, sc->gz, len); #endif DBEXIT(BXE_INSANE_REGS); } /* * Fill areas of device memory with the specified value. * * Generally used to clear a small area of device memory prior to writing * firmware to STORM memory or writing STORM firmware to device memory. * * Returns: * None. */ static void bxe_init_fill(struct bxe_softc *sc, uint32_t addr, int fill, uint32_t len) { uint32_t cur_len, i, leftovers, length; DBENTER(BXE_VERBOSE_LOAD); length = (((len * 4) > BXE_FW_BUF_SIZE) ? BXE_FW_BUF_SIZE : (len * 4)); leftovers = length / 4; memset(sc->gz, fill, length); for (i = 0; i < len; i += leftovers) { cur_len = min(leftovers, len - i); bxe_write_big_buf(sc, addr + i * 4, cur_len); } DBEXIT(BXE_VERBOSE_LOAD); } /* * * Returns: * None. */ static void bxe_init_wr_64(struct bxe_softc *sc, uint32_t addr, const uint32_t *data, uint32_t len64) { uint64_t data64, *pdata; uint32_t buf_len32, cur_len, len; int i; DBENTER(BXE_INSANE_REGS); buf_len32 = BXE_FW_BUF_SIZE / 4; len = len64 * 2; /* 64 bit value is in a blob: first low DWORD, then high DWORD. */ data64 = HILO_U64((*(data + 1)), (*data)); len64 = min((uint32_t)(BXE_FW_BUF_SIZE / 8), len64); for (i = 0; i < len64; i++) { pdata = ((uint64_t *)(sc->gz)) + i; *pdata = data64; } for (i = 0; i < len; i += buf_len32) { cur_len = min(buf_len32, len - i); bxe_write_big_buf(sc, addr + i*4, cur_len); } DBEXIT(BXE_INSANE_REGS); } /* * There are different blobs for each PRAM section. In addition, each * blob write operation is divided into multiple, smaller write * operations in order to decrease the amount of physically contiguous * buffer memory needed. Thus, when we select a blob, the address may * be with some offset from the beginning of PRAM section. The same * holds for the INT_TABLE sections. */ #define IF_IS_INT_TABLE_ADDR(base, addr) \ if (((base) <= (addr)) && ((base) + 0x400 >= (addr))) #define IF_IS_PRAM_ADDR(base, addr) \ if (((base) <= (addr)) && ((base) + 0x40000 >= (addr))) /* * * Returns: * None. */ static const uint8_t * bxe_sel_blob(struct bxe_softc *sc, uint32_t addr, const uint8_t *data) { IF_IS_INT_TABLE_ADDR(TSEM_REG_INT_TABLE, addr) data = INIT_TSEM_INT_TABLE_DATA(sc); else IF_IS_INT_TABLE_ADDR(CSEM_REG_INT_TABLE, addr) data = INIT_CSEM_INT_TABLE_DATA(sc); else IF_IS_INT_TABLE_ADDR(USEM_REG_INT_TABLE, addr) data = INIT_USEM_INT_TABLE_DATA(sc); else IF_IS_INT_TABLE_ADDR(XSEM_REG_INT_TABLE, addr) data = INIT_XSEM_INT_TABLE_DATA(sc); else IF_IS_PRAM_ADDR(TSEM_REG_PRAM, addr) data = INIT_TSEM_PRAM_DATA(sc); else IF_IS_PRAM_ADDR(CSEM_REG_PRAM, addr) data = INIT_CSEM_PRAM_DATA(sc); else IF_IS_PRAM_ADDR(USEM_REG_PRAM, addr) data = INIT_USEM_PRAM_DATA(sc); else IF_IS_PRAM_ADDR(XSEM_REG_PRAM, addr) data = INIT_XSEM_PRAM_DATA(sc); return (data); } static void bxe_write_big_buf_wb(struct bxe_softc *sc, uint32_t addr, uint32_t len) { if (sc->dmae_ready) bxe_write_dmae_phys_len(sc, sc->gz_dma.paddr, addr, len); else bxe_init_ind_wr(sc, addr, sc->gz, len); } #define VIRT_WR_DMAE_LEN(sc, data, addr, len32, le32_swap) \ do { \ memcpy(sc->gz, data, (len32)*4); \ bxe_write_big_buf_wb(sc, addr, len32); \ } while (0) /* * * Returns: * None. */ static void bxe_init_wr_wb(struct bxe_softc *sc, uint32_t addr, const uint32_t *data, uint32_t len) { const uint32_t *old_data; DBENTER(BXE_INSANE_REGS); old_data = data; data = (const uint32_t *)bxe_sel_blob(sc, addr, (const uint8_t *)data); if (sc->dmae_ready) { if (old_data != data) VIRT_WR_DMAE_LEN(sc, data, addr, len, 1); else VIRT_WR_DMAE_LEN(sc, data, addr, len, 0); } else bxe_init_ind_wr(sc, addr, data, len); DBEXIT(BXE_INSANE_REGS); } static void bxe_init_wr_zp(struct bxe_softc *sc, uint32_t addr, uint32_t len, uint32_t blob_off) { BXE_PRINTF("%s(%d): Compressed FW is not supported yet. " "ERROR: address:0x%x len:0x%x blob_offset:0x%x\n", __FILE__, __LINE__, addr, len, blob_off); } /* * Initialize blocks of the device. * * This routine basically performs bulk register programming for different * blocks within the controller. The file bxe_init_values.h contains a * series of register access operations (read, write, fill, etc.) as well * as a BLOB of data to initialize multiple blocks within the controller. * Block initialization may be supported by all controllers or by specific * models only. * * Returns: * None. */ static void bxe_init_block(struct bxe_softc *sc, uint32_t block, uint32_t stage) { union init_op *op; const uint32_t *data, *data_base; uint32_t i, op_type, addr, len; uint16_t op_end, op_start; int hw_wr; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); op_start = INIT_OPS_OFFSETS(sc)[BLOCK_OPS_IDX(block, stage, STAGE_START)]; op_end = INIT_OPS_OFFSETS(sc)[BLOCK_OPS_IDX(block, stage, STAGE_END)]; /* If empty block */ if (op_start == op_end) return; hw_wr = OP_WR_ASIC; data_base = INIT_DATA(sc); for (i = op_start; i < op_end; i++) { op = (union init_op *)&(INIT_OPS(sc)[i]); op_type = op->str_wr.op; addr = op->str_wr.offset; len = op->str_wr.data_len; data = data_base + op->str_wr.data_off; /* HW/EMUL specific */ if ((op_type > OP_WB) && (op_type == hw_wr)) op_type = OP_WR; switch (op_type) { case OP_RD: REG_RD(sc, addr); break; case OP_WR: REG_WR(sc, addr, op->write.val); break; case OP_SW: bxe_init_str_wr(sc, addr, data, len); break; case OP_WB: bxe_init_wr_wb(sc, addr, data, len); break; case OP_SI: bxe_init_ind_wr(sc, addr, data, len); break; case OP_ZR: bxe_init_fill(sc, addr, 0, op->zero.len); break; case OP_ZP: bxe_init_wr_zp(sc, addr, len, op->str_wr.data_off); break; case OP_WR_64: bxe_init_wr_64(sc, addr, data, len); break; default: /* happens whenever an op is of a diff HW */ break; } } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Handles controller initialization when called from an unlocked routine. * ifconfig calls this function. * * Returns: * None. */ static void bxe_init(void *xsc) { struct bxe_softc *sc; sc = xsc; BXE_CORE_LOCK(sc); bxe_init_locked(sc, LOAD_NORMAL); BXE_CORE_UNLOCK(sc); } /* * Release all resources used by the driver. * * Releases all resources acquired by the driver including interrupts, * interrupt handler, interfaces, mutexes, and DMA memory. * * Returns: * None. */ static void bxe_release_resources(struct bxe_softc *sc) { device_t dev; DBENTER(BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); dev = sc->dev; /* Release the FreeBSD interface. */ if (sc->bxe_ifp != NULL) if_free(sc->bxe_ifp); /* Free the DMA resources. */ bxe_host_structures_free(sc); #if __FreeBSD_version >= 800000 /* Free multiqueue buffer rings. */ bxe_free_buf_rings(sc); #endif } /* * Indirect register write. * * Writes NetXtreme II registers using an index/data register pair in PCI * configuration space. Using this mechanism avoids issues with posted * writes but is much slower than memory-mapped I/O. * * Returns: * None. */ static void bxe_reg_wr_ind(struct bxe_softc *sc, uint32_t offset, uint32_t val) { DBPRINT(sc, BXE_INSANE_REGS, "%s(); offset = 0x%08X, val = 0x%08X\n", __FUNCTION__, offset, val); pci_write_config(sc->dev, PCICFG_GRC_ADDRESS, offset, 4); pci_write_config(sc->dev, PCICFG_GRC_DATA, val, 4); /* Return to a safe address. */ pci_write_config(sc->dev, PCICFG_GRC_ADDRESS, PCICFG_VENDOR_ID_OFFSET, 4); } /* * Indirect register read. * * Reads NetXtreme II registers using an index/data register pair in PCI * configuration space. Using this mechanism avoids issues with posted * reads but is much slower than memory-mapped I/O. * * Returns: * The value of the register. */ static uint32_t bxe_reg_rd_ind(struct bxe_softc *sc, uint32_t offset) { uint32_t val; pci_write_config(sc->dev, PCICFG_GRC_ADDRESS, offset, 4); val = pci_read_config(sc->dev, PCICFG_GRC_DATA, 4); /* Return to a safe address. */ pci_write_config(sc->dev, PCICFG_GRC_ADDRESS, PCICFG_VENDOR_ID_OFFSET, 4); DBPRINT(sc, BXE_INSANE_REGS, "%s(); offset = 0x%08X, val = 0x%08X\n", __FUNCTION__, offset, val); return (val); } static uint32_t dmae_reg_go_c[] = { DMAE_REG_GO_C0, DMAE_REG_GO_C1, DMAE_REG_GO_C2, DMAE_REG_GO_C3, DMAE_REG_GO_C4, DMAE_REG_GO_C5, DMAE_REG_GO_C6, DMAE_REG_GO_C7, DMAE_REG_GO_C8, DMAE_REG_GO_C9, DMAE_REG_GO_C10, DMAE_REG_GO_C11, DMAE_REG_GO_C12, DMAE_REG_GO_C13, DMAE_REG_GO_C14, DMAE_REG_GO_C15 }; /* * Copy DMAE command into memory and start the command. * * Returns: * None. */ static void bxe_post_dmae(struct bxe_softc *sc, struct dmae_command *dmae, int idx) { uint32_t cmd_offset; int i; cmd_offset = (DMAE_REG_CMD_MEM + sizeof(struct dmae_command) * idx); for (i = 0; i < (sizeof(struct dmae_command) / 4); i++) { REG_WR(sc, cmd_offset + i * 4, *(((uint32_t *)dmae) + i)); DBPRINT(sc, BXE_INSANE_REGS, "%s(): DMAE cmd[%d].%d : 0x%08X\n", __FUNCTION__, idx, i, cmd_offset + i * 4); } /* Kick off the command. */ REG_WR(sc, dmae_reg_go_c[idx], 1); } /* * Perform a DMAE write to device memory. * * Some of the registers on the 577XX controller are 128bits wide. It is * required that when accessing those registers that they be written * atomically and that no intervening bus acceses to the device occur. * This could be handled by a lock held across all driver instances for * the device or it can be handled by performing a DMA operation when * writing to the device. This code implements the latter. * * Returns: * None. */ void bxe_write_dmae(struct bxe_softc *sc, bus_addr_t dma_addr, uint32_t dst_addr, uint32_t len32) { struct dmae_command dmae; uint32_t *data, *wb_comp; int timeout; DBENTER(BXE_INSANE_REGS); DBPRINT(sc, BXE_EXTREME_REGS, "%s(): host addr = 0x%jX, device addr = 0x%08X, length = %d.\n", __FUNCTION__, (uintmax_t)dma_addr, dst_addr, (int)len32); wb_comp = BXE_SP(sc, wb_comp); /* Fall back to indirect access if DMAE is not ready. */ if (!sc->dmae_ready) { data = BXE_SP(sc, wb_data[0]); DBPRINT(sc, BXE_WARN, "%s(): DMAE not ready, " "using indirect.\n", __FUNCTION__); bxe_init_ind_wr(sc, dst_addr, data, len32); goto bxe_write_dmae_exit; } memset(&dmae, 0, sizeof(struct dmae_command)); dmae.opcode = (DMAE_CMD_SRC_PCI | DMAE_CMD_DST_GRC | DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (BP_PORT(sc) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (BP_E1HVN(sc) << DMAE_CMD_E1HVN_SHIFT)); dmae.src_addr_lo = U64_LO(dma_addr); dmae.src_addr_hi = U64_HI(dma_addr); dmae.dst_addr_lo = dst_addr >> 2; dmae.dst_addr_hi = 0; dmae.len = len32; dmae.comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, wb_comp)); dmae.comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, wb_comp)); dmae.comp_val = BXE_WB_COMP_VAL; BXE_DMAE_LOCK(sc); *wb_comp = 0; bxe_post_dmae(sc, &dmae, INIT_DMAE_C(sc)); DELAY(50); /* Wait up to 200ms. */ timeout = 4000; while (*wb_comp != BXE_WB_COMP_VAL) { if (!timeout) { DBPRINT(sc, BXE_FATAL, "%s(): DMAE timeout (dst_addr = 0x%08X, len = %d)!\n", __FUNCTION__, dst_addr, len32); break; } timeout--; DELAY(50); } BXE_DMAE_UNLOCK(sc); bxe_write_dmae_exit: DBEXIT(BXE_INSANE_REGS); } /* * Perform a DMAE read from to device memory. * * Some of the registers on the 577XX controller are 128bits wide. It is * required that when accessing those registers that they be read * atomically and that no intervening bus acceses to the device occur. * This could be handled by a lock held across all driver instances for * the device or it can be handled by performing a DMA operation when * reading from the device. This code implements the latter. * * Returns: * None. */ void bxe_read_dmae(struct bxe_softc *sc, uint32_t src_addr, uint32_t len32) { struct dmae_command dmae; uint32_t *data, *wb_comp; int i, timeout; DBENTER(BXE_INSANE_REGS); wb_comp = BXE_SP(sc, wb_comp); /* Fall back to indirect access if DMAE is not ready. */ if (!sc->dmae_ready) { data = BXE_SP(sc, wb_data[0]); DBPRINT(sc, BXE_WARN, "%s(): DMAE not ready, " "using indirect.\n", __FUNCTION__); for (i = 0; i < len32; i++) data[i] = bxe_reg_rd_ind(sc, src_addr + i * 4); goto bxe_read_dmae_exit; } memset(&dmae, 0, sizeof(struct dmae_command)); dmae.opcode = (DMAE_CMD_SRC_GRC | DMAE_CMD_DST_PCI | DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (BP_PORT(sc) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (BP_E1HVN(sc) << DMAE_CMD_E1HVN_SHIFT)); dmae.src_addr_lo = src_addr >> 2; dmae.src_addr_hi = 0; dmae.dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, wb_data)); dmae.dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, wb_data)); dmae.len = len32; dmae.comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, wb_comp)); dmae.comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, wb_comp)); dmae.comp_val = BXE_WB_COMP_VAL; BXE_DMAE_LOCK(sc); memset(BXE_SP(sc, wb_data[0]), 0, sizeof(uint32_t) * 4); *wb_comp = 0; bxe_post_dmae(sc, &dmae, INIT_DMAE_C(sc)); DELAY(50); timeout = 4000; while (*wb_comp != BXE_WB_COMP_VAL) { if (!timeout) { DBPRINT(sc, BXE_FATAL, "%s(): DMAE timeout (src_addr = 0x%08X, len = %d)!\n", __FUNCTION__, src_addr, len32); break; } timeout--; DELAY(50); } BXE_DMAE_UNLOCK(sc); bxe_read_dmae_exit: DBEXIT(BXE_INSANE_REGS); } /* * DMAE write wrapper. * * Returns: * None. */ static void bxe_wb_wr(struct bxe_softc *sc, int reg, uint32_t val_hi, uint32_t val_lo) { uint32_t wb_write[2]; wb_write[0] = val_hi; wb_write[1] = val_lo; REG_WR_DMAE(sc, reg, wb_write, 2); } /* * Poll a register waiting for a value. * * Returns: * The last read register value. */ static __inline uint32_t bxe_reg_poll(struct bxe_softc *sc, uint32_t reg, uint32_t expected, int ms, int wait) { uint32_t val; do { val = REG_RD(sc, reg); if (val == expected) break; ms -= wait; DELAY(wait * 1000); } while (ms > 0); return (val); } /* * Microcode assert display. * * This function walks through each STORM processor and prints out a * listing of all asserts currently in effect. Useful for post-mortem * debugging. * * Returns: * The number of asserts detected. */ static int bxe_mc_assert(struct bxe_softc *sc) { uint32_t row0, row1, row2, row3; char last_idx; int i, rc; DBENTER(BXE_VERBOSE_INTR); rc = 0; /* XSTORM */ last_idx = REG_RD8(sc, BAR_XSTORM_INTMEM + XSTORM_ASSERT_LIST_INDEX_OFFSET); if (last_idx) DBPRINT(sc, BXE_FATAL, "DATA XSTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx); /* Print the asserts */ for (i = 0; i < STORM_ASSERT_ARRAY_SIZE; i++) { row0 = REG_RD(sc, BAR_XSTORM_INTMEM + XSTORM_ASSERT_LIST_OFFSET(i)); row1 = REG_RD(sc, BAR_XSTORM_INTMEM + XSTORM_ASSERT_LIST_OFFSET(i) + 4); row2 = REG_RD(sc, BAR_XSTORM_INTMEM + XSTORM_ASSERT_LIST_OFFSET(i) + 8); row3 = REG_RD(sc, BAR_XSTORM_INTMEM + XSTORM_ASSERT_LIST_OFFSET(i) + 12); if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { DBPRINT(sc, BXE_FATAL, "DATA XSTORM_ASSERT_INDEX %d = " "0x%08x 0x%08x 0x%08x 0x%08x\n", i, row3, row2, row1, row0); rc++; } else break; } /* TSTORM */ last_idx = REG_RD8(sc, BAR_TSTORM_INTMEM + TSTORM_ASSERT_LIST_INDEX_OFFSET); if (last_idx) DBPRINT(sc, BXE_FATAL, "DATA TSTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx); /* Print the asserts */ for (i = 0; i < STORM_ASSERT_ARRAY_SIZE; i++) { row0 = REG_RD(sc, BAR_TSTORM_INTMEM + TSTORM_ASSERT_LIST_OFFSET(i)); row1 = REG_RD(sc, BAR_TSTORM_INTMEM + TSTORM_ASSERT_LIST_OFFSET(i) + 4); row2 = REG_RD(sc, BAR_TSTORM_INTMEM + TSTORM_ASSERT_LIST_OFFSET(i) + 8); row3 = REG_RD(sc, BAR_TSTORM_INTMEM + TSTORM_ASSERT_LIST_OFFSET(i) + 12); if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { DBPRINT(sc, BXE_FATAL, "DATA TSTORM_ASSERT_INDEX %d = " "0x%08x 0x%08x 0x%08x 0x%08x\n", i, row3, row2, row1, row0); rc++; } else break; } /* CSTORM */ last_idx = REG_RD8(sc, BAR_CSTORM_INTMEM + CSTORM_ASSERT_LIST_INDEX_OFFSET); if (last_idx) DBPRINT(sc, BXE_FATAL, "DATA CSTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx); /* Print the asserts */ for (i = 0; i < STORM_ASSERT_ARRAY_SIZE; i++) { row0 = REG_RD(sc, BAR_CSTORM_INTMEM + CSTORM_ASSERT_LIST_OFFSET(i)); row1 = REG_RD(sc, BAR_CSTORM_INTMEM + CSTORM_ASSERT_LIST_OFFSET(i) + 4); row2 = REG_RD(sc, BAR_CSTORM_INTMEM + CSTORM_ASSERT_LIST_OFFSET(i) + 8); row3 = REG_RD(sc, BAR_CSTORM_INTMEM + CSTORM_ASSERT_LIST_OFFSET(i) + 12); if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { DBPRINT(sc, BXE_FATAL, "DATA CSTORM_ASSERT_INDEX %d = " "0x%08x 0x%08x 0x%08x 0x%08x\n", i, row3, row2, row1, row0); rc++; } else break; } /* USTORM */ last_idx = REG_RD8(sc, BAR_USTORM_INTMEM + USTORM_ASSERT_LIST_INDEX_OFFSET); if (last_idx) DBPRINT(sc, BXE_FATAL, "DATA USTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx); /* Print the asserts */ for (i = 0; i < STORM_ASSERT_ARRAY_SIZE; i++) { row0 = REG_RD(sc, BAR_USTORM_INTMEM + USTORM_ASSERT_LIST_OFFSET(i)); row1 = REG_RD(sc, BAR_USTORM_INTMEM + USTORM_ASSERT_LIST_OFFSET(i) + 4); row2 = REG_RD(sc, BAR_USTORM_INTMEM + USTORM_ASSERT_LIST_OFFSET(i) + 8); row3 = REG_RD(sc, BAR_USTORM_INTMEM + USTORM_ASSERT_LIST_OFFSET(i) + 12); if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { DBPRINT(sc, BXE_FATAL, "DATA USTORM_ASSERT_INDEX %d = " "0x%08x 0x%08x 0x%08x 0x%08x\n", i, row3, row2, row1, row0); rc++; } else break; } DBEXIT(BXE_VERBOSE_INTR); return (rc); } /* * Perform a panic dump. * * Returns: * None */ static void bxe_panic_dump(struct bxe_softc *sc) { DBENTER(BXE_FATAL); sc->stats_state = STATS_STATE_DISABLED; BXE_PRINTF("---------- Begin crash dump ----------\n"); /* Idle check is run twice to verify the controller has stopped. */ bxe_idle_chk(sc); bxe_idle_chk(sc); bxe_mc_assert(sc); #ifdef BXE_DEBUG bxe_breakpoint(sc); #endif BXE_PRINTF("---------- End crash dump ----------\n"); DBEXIT(BXE_FATAL); } /* * Enables interrupt generation. * * Returns: * None. */ static void bxe_int_enable(struct bxe_softc *sc) { uint32_t hc_addr, val; int port; DBENTER(BXE_VERBOSE_INTR); port = BP_PORT(sc); hc_addr = port ? HC_REG_CONFIG_1 : HC_REG_CONFIG_0; val = REG_RD(sc, hc_addr); if (sc->msix_count > 0) { if (sc->msix_count == 1) { /* Single interrupt, multiple queues.*/ DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): Setting host coalescing registers for MSI-X (SIMQ).\n", __FUNCTION__); /* Clear INTx. */ val &= ~HC_CONFIG_0_REG_INT_LINE_EN_0; /* Enable single ISR mode, MSI/MSI-X, and attention messages. */ val |= (HC_CONFIG_0_REG_SINGLE_ISR_EN_0 | HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0 | HC_CONFIG_0_REG_ATTN_BIT_EN_0); } else { /* Multiple interrupts, multiple queues.*/ DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): Setting host coalescing registers for MSI-X (MIMQ).\n", __FUNCTION__); /* Clear single ISR mode and INTx. */ val &= ~(HC_CONFIG_0_REG_SINGLE_ISR_EN_0 | HC_CONFIG_0_REG_INT_LINE_EN_0); /* Enable MSI/MSI-X and attention messages. */ val |= (HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0 | HC_CONFIG_0_REG_ATTN_BIT_EN_0); } } else if (sc->msi_count > 0) { if (sc->msi_count == 1) { /* Single interrupt, multiple queues.*/ DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): Setting host coalescing registers for MSI (SIMQ).\n", __FUNCTION__); /* Clear INTx. */ val &= ~HC_CONFIG_0_REG_INT_LINE_EN_0; /* Enable single ISR mode, MSI/MSI-X, and attention * messages. */ val |= (HC_CONFIG_0_REG_SINGLE_ISR_EN_0 | HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0 | HC_CONFIG_0_REG_ATTN_BIT_EN_0); } else { /* Multiple interrupts, multiple queues.*/ DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): Setting host coalescing registers for" "MSI (MIMQ).\n", __FUNCTION__); /* Clear single ISR mode and INTx. */ val &= ~(HC_CONFIG_0_REG_SINGLE_ISR_EN_0 | HC_CONFIG_0_REG_INT_LINE_EN_0); /* Enable MSI/MSI-X and attention messages. */ val |= (HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0 | HC_CONFIG_0_REG_ATTN_BIT_EN_0); } } else { /* Single interrupt, single queue. */ DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): Setting host coalescing registers for INTA#.\n", __FUNCTION__); val |= (HC_CONFIG_0_REG_SINGLE_ISR_EN_0 | HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0 | HC_CONFIG_0_REG_INT_LINE_EN_0 | HC_CONFIG_0_REG_ATTN_BIT_EN_0); REG_WR(sc, hc_addr, val); val &= ~HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0; } /* Write the interrupt mode to the host coalescing block. */ REG_WR(sc, hc_addr, val); if (CHIP_IS_E1H(sc)) { /* Init leading/trailing edge attention generation. */ if (IS_E1HMF(sc)) { val = (0xee0f | (1 << (BP_E1HVN(sc) + 4))); /* * Check if this driver instance is the port * master function. */ if (sc->port.pmf) /* Enable nig & GPIO3 attentions. */ val |= 0x1100; } else val = 0xffff; REG_WR(sc, HC_REG_TRAILING_EDGE_0 + port * 8, val); REG_WR(sc, HC_REG_LEADING_EDGE_0 + port * 8, val); } DBEXIT(BXE_VERBOSE_INTR); } /* * Disables interrupt generation. * * Returns: * None. */ static void bxe_int_disable(struct bxe_softc *sc) { uint32_t hc_addr, val; int port; DBENTER(BXE_VERBOSE_INTR | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); port = BP_PORT(sc); hc_addr = port ? HC_REG_CONFIG_1 : HC_REG_CONFIG_0; val = REG_RD(sc, hc_addr); val &= ~(HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0 | HC_CONFIG_0_REG_INT_LINE_EN_0 | HC_CONFIG_0_REG_ATTN_BIT_EN_0); REG_WR(sc, hc_addr, val); if (REG_RD(sc, hc_addr)!= val) { DBPRINT(sc, BXE_WARN, "%s(): BUG! Returned value from IGU " "doesn't match value written (0x%08X).\n", __FUNCTION__, val); } DBEXIT(BXE_VERBOSE_INTR | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } #define BXE_CRC32_RESIDUAL 0xdebb20e3 /* * Returns: * 0 = Success, !0 = Failure. */ static int bxe_nvram_acquire_lock(struct bxe_softc *sc) { uint32_t val; int i, port, rc; DBENTER(BXE_VERBOSE_NVRAM); port = BP_PORT(sc); rc = 0; val = 0; /* Acquire the NVRAM lock. */ REG_WR(sc, MCP_REG_MCPR_NVM_SW_ARB, (MCPR_NVM_SW_ARB_ARB_REQ_SET1 << port)); for (i = 0; i < NVRAM_TIMEOUT_COUNT * 10; i++) { val = REG_RD(sc, MCP_REG_MCPR_NVM_SW_ARB); if (val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port)) break; DELAY(5); } if (!(val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port))) { DBPRINT(sc, BXE_WARN, "%s(): Cannot acquire NVRAM lock!\n", __FUNCTION__); rc = EBUSY; } DBEXIT(BXE_VERBOSE_NVRAM); return (rc); } /* * Returns: * 0 = Success, !0 = Failure. */ static int bxe_nvram_release_lock(struct bxe_softc *sc) { uint32_t val; int i, port, rc; DBENTER(BXE_VERBOSE_NVRAM); port = BP_PORT(sc); rc = 0; val = 0; /* Release the NVRAM lock. */ REG_WR(sc, MCP_REG_MCPR_NVM_SW_ARB, (MCPR_NVM_SW_ARB_ARB_REQ_CLR1 << port)); for (i = 0; i < NVRAM_TIMEOUT_COUNT * 10; i++) { val = REG_RD(sc, MCP_REG_MCPR_NVM_SW_ARB); if (!(val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port))) break; DELAY(5); } if (val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port)) { DBPRINT(sc, BXE_WARN, "%s(): Cannot release NVRAM lock!\n", __FUNCTION__); rc = EBUSY; } DBEXIT(BXE_VERBOSE_NVRAM); return (rc); } /* * Returns: * None. */ static void bxe_nvram_enable_access(struct bxe_softc *sc) { uint32_t val; DBENTER(BXE_VERBOSE_NVRAM); val = REG_RD(sc, MCP_REG_MCPR_NVM_ACCESS_ENABLE); /* Enable both bits, even on read */ REG_WR(sc, MCP_REG_MCPR_NVM_ACCESS_ENABLE, (val | MCPR_NVM_ACCESS_ENABLE_EN | MCPR_NVM_ACCESS_ENABLE_WR_EN)); DBEXIT(BXE_VERBOSE_NVRAM); } /* * Returns: * None. */ static void bxe_nvram_disable_access(struct bxe_softc *sc) { uint32_t val; DBENTER(BXE_VERBOSE_NVRAM); val = REG_RD(sc, MCP_REG_MCPR_NVM_ACCESS_ENABLE); /* Disable both bits, even after read. */ REG_WR(sc, MCP_REG_MCPR_NVM_ACCESS_ENABLE, (val & ~(MCPR_NVM_ACCESS_ENABLE_EN | MCPR_NVM_ACCESS_ENABLE_WR_EN))); DBEXIT(BXE_VERBOSE_NVRAM); } /* * Returns: * 0 = Success, !0 = Failure. */ static int bxe_nvram_read_dword(struct bxe_softc *sc, uint32_t offset, uint32_t *ret_val, uint32_t cmd_flags) { uint32_t val; int i, rc; DBENTER(BXE_INSANE_NVRAM); /* Build the command word. */ cmd_flags |= MCPR_NVM_COMMAND_DOIT; /* Need to clear DONE bit separately. */ REG_WR(sc, MCP_REG_MCPR_NVM_COMMAND, MCPR_NVM_COMMAND_DONE); /* Address within the NVRAM to read. */ REG_WR(sc, MCP_REG_MCPR_NVM_ADDR, (offset & MCPR_NVM_ADDR_NVM_ADDR_VALUE)); /* Issue a read command. */ REG_WR(sc, MCP_REG_MCPR_NVM_COMMAND, cmd_flags); /* Wait for completion. */ *ret_val = 0; rc = EBUSY; for (i = 0; i < NVRAM_TIMEOUT_COUNT; i++) { DELAY(5); val = REG_RD(sc, MCP_REG_MCPR_NVM_COMMAND); if (val & MCPR_NVM_COMMAND_DONE) { val = REG_RD(sc, MCP_REG_MCPR_NVM_READ); val = htobe32(val); *ret_val = val; rc = 0; break; } } DBPRINT(sc, BXE_INSANE_NVRAM, "%s(): Read 0x%08X from offset 0x%08X.\n", __FUNCTION__, *ret_val, offset); DBEXIT(BXE_INSANE_NVRAM); return (rc); } /* * Returns: * 0 = Success, !0 = Failure. */ static int bxe_nvram_read(struct bxe_softc *sc, uint32_t offset, uint8_t *ret_buf, int buf_size) { uint32_t cmd_flags, val; int rc; DBENTER(BXE_EXTREME_NVRAM); if ((offset & 0x03) || (buf_size & 0x03) || (buf_size == 0)) { DBPRINT(sc, BXE_WARN, "%s(): Unaligned address or invalid " "buffer for NVRAM read (offset = 0x%08X, buf_size = %d)!\n", __FUNCTION__, offset, buf_size); rc = EINVAL; goto bxe_nvram_read_exit; } if (offset + buf_size > sc->common.flash_size) { DBPRINT(sc, BXE_WARN, "%s(): Read extends beyond the end of " "the NVRAM (offset (0x%08X) + buf_size (%d) > flash_size " "(0x%08X))!\n", __FUNCTION__, offset, buf_size, sc->common.flash_size); rc = EINVAL; goto bxe_nvram_read_exit; } rc = bxe_nvram_acquire_lock(sc); if (rc) goto bxe_nvram_read_exit; bxe_nvram_enable_access(sc); /* Read the first word(s). */ cmd_flags = MCPR_NVM_COMMAND_FIRST; while ((buf_size > sizeof(uint32_t)) && (rc == 0)) { rc = bxe_nvram_read_dword(sc, offset, &val, cmd_flags); memcpy(ret_buf, &val, 4); /* Advance to the next DWORD. */ offset += sizeof(uint32_t); ret_buf += sizeof(uint32_t); buf_size -= sizeof(uint32_t); cmd_flags = 0; } /* Read the final word. */ if (rc == 0) { cmd_flags |= MCPR_NVM_COMMAND_LAST; rc = bxe_nvram_read_dword(sc, offset, &val, cmd_flags); memcpy(ret_buf, &val, 4); } /* Disable access to NVRAM interface. */ bxe_nvram_disable_access(sc); bxe_nvram_release_lock(sc); bxe_nvram_read_exit: DBEXIT(BXE_EXTREME_NVRAM); return (rc); } #ifdef BXE_NVRAM_WRITE_SUPPORT /* * Returns: * 0 = Success, !0 = Failure. */ static int bxe_nvram_write_dword(struct bxe_softc *sc, uint32_t offset, uint32_t val, uint32_t cmd_flags) { int i, rc; DBENTER(BXE_VERBOSE_NVRAM); /* Build the command word. */ cmd_flags |= MCPR_NVM_COMMAND_DOIT | MCPR_NVM_COMMAND_WR; /* Need to clear DONE bit separately. */ REG_WR(sc, MCP_REG_MCPR_NVM_COMMAND, MCPR_NVM_COMMAND_DONE); /* Write the data. */ REG_WR(sc, MCP_REG_MCPR_NVM_WRITE, val); /* Address to write within the NVRAM. */ REG_WR(sc, MCP_REG_MCPR_NVM_ADDR, (offset & MCPR_NVM_ADDR_NVM_ADDR_VALUE)); /* Issue the write command. */ REG_WR(sc, MCP_REG_MCPR_NVM_COMMAND, cmd_flags); /* Wait for completion. */ rc = EBUSY; for (i = 0; i < NVRAM_TIMEOUT_COUNT; i++) { DELAY(5); val = REG_RD(sc, MCP_REG_MCPR_NVM_COMMAND); if (val & MCPR_NVM_COMMAND_DONE) { rc = 0; break; } } DBEXIT(BXE_VERBOSE_NVRAM); return (rc); } #define BYTE_OFFSET(offset) (8 * (offset & 0x03)) /* * Returns: * */ static int bxe_nvram_write1(struct bxe_softc *sc, uint32_t offset, uint8_t *data_buf, int buf_size) { uint32_t align_offset, cmd_flags, val; int rc; DBENTER(BXE_VERBOSE_NVRAM); if (offset + buf_size > sc->common.flash_size) { DBPRINT(sc, BXE_WARN, "%s(): Write extends beyond the end of " "the NVRAM (offset (0x%08X) + buf_size (%d) > flash_size " "(0x%08X))!\n", __FUNCTION__, offset, buf_size, sc->common.flash_size); rc = EINVAL; goto bxe_nvram_write1_exit; } /* request access to nvram interface */ rc = bxe_nvram_acquire_lock(sc); if (rc) goto bxe_nvram_write1_exit; /* Enable access to the NVRAM interface. */ bxe_nvram_enable_access(sc); cmd_flags = (MCPR_NVM_COMMAND_FIRST | MCPR_NVM_COMMAND_LAST); align_offset = (offset & ~0x03); rc = bxe_nvram_read_dword(sc, align_offset, &val, cmd_flags); if (rc == 0) { val &= ~(0xff << BYTE_OFFSET(offset)); val |= (*data_buf << BYTE_OFFSET(offset)); val = be32toh(val); rc = bxe_nvram_write_dword(sc, align_offset, val, cmd_flags); } /* Disable access to the NVRAM interface. */ bxe_nvram_disable_access(sc); bxe_nvram_release_lock(sc); bxe_nvram_write1_exit: DBEXIT(BXE_VERBOSE_NVRAM); return (rc); } /* * Returns: * 0 = Success, !0 = Failure. */ static int bxe_nvram_write(struct bxe_softc *sc, uint32_t offset, uint8_t *data_buf, int buf_size) { uint32_t cmd_flags, val, written_so_far; int rc; rc = 0; if (buf_size == 1) return (bxe_nvram_write1(sc, offset, data_buf, buf_size)); if ((offset & 0x03) || (buf_size & 0x03) || (buf_size == 0)) { DBPRINT(sc, BXE_WARN, "%s(): Unaligned address or invalid " "buffer for NVRAM write " "(offset = 0x%08X, buf_size = %d)!\n", __FUNCTION__, offset, buf_size); rc = EINVAL; goto bxe_nvram_write_exit; } if (offset + buf_size > sc->common.flash_size) { DBPRINT(sc, BXE_WARN, "%s(): Write extends beyond the end of " "the NVRAM (offset (0x%08X) + buf_size (%d) > flash_size " "(0x%08X))!\n", __FUNCTION__, offset, buf_size, sc->common.flash_size); rc = EINVAL; goto bxe_nvram_write_exit; } /* Request access to NVRAM interface. */ rc = bxe_nvram_acquire_lock(sc); if (rc) goto bxe_nvram_write_exit; /* Enable access to the NVRAM interface. */ bxe_nvram_enable_access(sc); written_so_far = 0; cmd_flags = MCPR_NVM_COMMAND_FIRST; while ((written_so_far < buf_size) && (rc == 0)) { if (written_so_far == (buf_size - sizeof(uint32_t))) cmd_flags |= MCPR_NVM_COMMAND_LAST; else if (((offset + 4) % NVRAM_PAGE_SIZE) == 0) cmd_flags |= MCPR_NVM_COMMAND_LAST; else if ((offset % NVRAM_PAGE_SIZE) == 0) cmd_flags |= MCPR_NVM_COMMAND_FIRST; memcpy(&val, data_buf, 4); rc = bxe_nvram_write_dword(sc, offset, val, cmd_flags); /* Advance to the next DWORD. */ offset += sizeof(uint32_t); data_buf += sizeof(uint32_t); written_so_far += sizeof(uint32_t); cmd_flags = 0; } /* Disable access to the NVRAM interface. */ bxe_nvram_disable_access(sc); bxe_nvram_release_lock(sc); bxe_nvram_write_exit: DBEXIT(BXE_VERBOSE_NVRAM); return (rc); } #endif /* * This function validates NVRAM content by reading spcific * regions and validating that the NVRAM checksum matches the * actual content. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_nvram_test(struct bxe_softc *sc) { static const struct { int offset; int size; } nvram_tbl[] = { { 0, 0x14 }, /* bootstrap area*/ { 0x14, 0xec }, /* directory area */ { 0x100, 0x350 }, /* manuf_info */ { 0x450, 0xf0 }, /* feature_info */ { 0x640, 0x64 }, /* upgrade_key_info */ { 0x708, 0x70 }, /* manuf_key_info */ { 0, 0 } }; uint32_t magic, csum, buf[0x350 / 4]; uint8_t *data; int i, rc; DBENTER(BXE_VERBOSE_NVRAM); data = (uint8_t *) buf; /* Read the DWORD at offset 0 in NVRAM. */ rc = bxe_nvram_read(sc, 0, data, 4); if (rc) { BXE_PRINTF("%s(%d): Error (%d) returned reading NVRAM!\n", __FILE__, __LINE__, rc); goto bxe_nvram_test_exit; } /* Make sure we found our magic value. */ magic = be32toh(buf[0]); if (magic != 0x669955aa) { BXE_PRINTF("%s(%d): Invalid magic value (0x%08x) found!\n", __FILE__, __LINE__, magic); rc = ENODEV; goto bxe_nvram_test_exit; } /* Read through each region in NVRAM and validate the checksum. */ for (i = 0; nvram_tbl[i].size; i++) { DBPRINT(sc, BXE_VERBOSE_NVRAM, "%s(): Testing NVRAM region %d, " "starting offset = %d, length = %d\n", __FUNCTION__, i, nvram_tbl[i].offset, nvram_tbl[i].size); rc = bxe_nvram_read(sc, nvram_tbl[i].offset, data, nvram_tbl[i].size); if (rc) { BXE_PRINTF("%s(%d): Error (%d) returned reading NVRAM " "region %d!\n", __FILE__, __LINE__, rc, i); goto bxe_nvram_test_exit; } csum = ether_crc32_le(data, nvram_tbl[i].size); if (csum != BXE_CRC32_RESIDUAL) { BXE_PRINTF("%s(%d): Checksum error (0x%08X) for NVRAM " "region %d!\n", __FILE__, __LINE__, csum, i); rc = ENODEV; goto bxe_nvram_test_exit; } } bxe_nvram_test_exit: DBEXIT(BXE_VERBOSE_NVRAM); return (rc); } /* * Acknowledge status block and modify interrupt mode. * * Returns: * None. */ static __inline void bxe_ack_sb(struct bxe_softc *sc, uint8_t sb_id, uint8_t storm, uint16_t index, uint8_t int_mode, uint8_t update) { struct igu_ack_register igu_ack; uint32_t hc_addr; hc_addr = (HC_REG_COMMAND_REG + BP_PORT(sc) * 32 + COMMAND_REG_INT_ACK); igu_ack.status_block_index = index; igu_ack.sb_id_and_flags = ((sb_id << IGU_ACK_REGISTER_STATUS_BLOCK_ID_SHIFT) | (storm << IGU_ACK_REGISTER_STORM_ID_SHIFT) | (update << IGU_ACK_REGISTER_UPDATE_INDEX_SHIFT) | (int_mode << IGU_ACK_REGISTER_INTERRUPT_MODE_SHIFT)); rmb(); REG_WR(sc, hc_addr, (*(uint32_t *) &igu_ack)); wmb(); } /* * Update fastpath status block index. * * Returns: * 0 = Nu completes, 1 = TX completes, 2 = RX completes, * 3 = RX & TX completes */ static __inline uint16_t bxe_update_fpsb_idx(struct bxe_fastpath *fp) { struct host_status_block *fpsb; uint16_t rc; fpsb = fp->status_block; rc = 0; rmb(); /* Check for any CSTORM transmit completions. */ if (fp->fp_c_idx != le16toh(fpsb->c_status_block.status_block_index)) { fp->fp_c_idx = le16toh(fpsb->c_status_block.status_block_index); rc |= 0x1; } /* Check for any USTORM receive completions. */ if (fp->fp_u_idx != le16toh(fpsb->u_status_block.status_block_index)) { fp->fp_u_idx = le16toh(fpsb->u_status_block.status_block_index); rc |= 0x2; } return (rc); } /* * Acknowledge interrupt. * * Returns: * Interrupt value read from IGU. */ static uint16_t bxe_ack_int(struct bxe_softc *sc) { uint32_t hc_addr, result; hc_addr = HC_REG_COMMAND_REG + BP_PORT(sc) * 32 + COMMAND_REG_SIMD_MASK; result = REG_RD(sc, hc_addr); DBPRINT(sc, BXE_INSANE_INTR, "%s(): Read 0x%08X from HC addr 0x%08X\n", __FUNCTION__, result, hc_addr); return (result); } /* * Slowpath event handler. * * Checks that a ramrod completion occurs while the * controller is in the proper state. * * Returns: * None. */ static void bxe_sp_event(struct bxe_fastpath *fp, union eth_rx_cqe *rr_cqe) { struct bxe_softc *sc; int cid, command; sc = fp->sc; DBENTER(BXE_VERBOSE_RAMROD); cid = SW_CID(rr_cqe->ramrod_cqe.conn_and_cmd_data); command = CQE_CMD(rr_cqe->ramrod_cqe.conn_and_cmd_data); DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): CID = %d, ramrod command = %d, " "device state = 0x%08X, fp[%02d].state = 0x%08X, type = %d\n", __FUNCTION__, cid, command, sc->state, fp->index, fp->state, rr_cqe->ramrod_cqe.ramrod_type); /* Free up an entry on the slowpath queue. */ sc->spq_left++; /* Handle ramrod commands that completed on a client connection. */ if (fp->index) { /* Check for a completion for the current state. */ switch (command | fp->state) { case (RAMROD_CMD_ID_ETH_CLIENT_SETUP | BXE_FP_STATE_OPENING): DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): Completed fp[%02d] CLIENT_SETUP Ramrod.\n", __FUNCTION__, cid); fp->state = BXE_FP_STATE_OPEN; break; case (RAMROD_CMD_ID_ETH_HALT | BXE_FP_STATE_HALTING): DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): Completed fp[%02d] ETH_HALT ramrod\n", __FUNCTION__, cid); fp->state = BXE_FP_STATE_HALTED; break; default: DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): Unexpected microcode reply (%d) while " "in state 0x%04X!\n", __FUNCTION__, command, fp->state); } goto bxe_sp_event_exit; } /* Handle ramrod commands that completed on the leading connection. */ switch (command | sc->state) { case (RAMROD_CMD_ID_ETH_PORT_SETUP | BXE_STATE_OPENING_WAIT4_PORT): DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): Completed PORT_SETUP ramrod.\n", __FUNCTION__); sc->state = BXE_STATE_OPEN; break; case (RAMROD_CMD_ID_ETH_HALT | BXE_STATE_CLOSING_WAIT4_HALT): DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): Completed ETH_HALT ramrod.\n", __FUNCTION__); sc->state = BXE_STATE_CLOSING_WAIT4_DELETE; fp->state = BXE_FP_STATE_HALTED; break; case (RAMROD_CMD_ID_ETH_CFC_DEL | BXE_STATE_CLOSING_WAIT4_HALT): DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): Completed fp[%02d] ETH_CFC_DEL ramrod.\n", __FUNCTION__, cid); sc->fp[cid].state = BXE_FP_STATE_CLOSED; break; case (RAMROD_CMD_ID_ETH_SET_MAC | BXE_STATE_OPEN): DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): Completed ETH_SET_MAC ramrod in STATE_OPEN state.\n", __FUNCTION__); break; case (RAMROD_CMD_ID_ETH_SET_MAC | BXE_STATE_CLOSING_WAIT4_HALT): DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): Completed ETH_SET_MAC ramrod in " "CLOSING_WAIT4_HALT state.\n", __FUNCTION__); break; default: DBPRINT(sc, BXE_FATAL, "%s(): Unexpected microcode reply (%d)! " "State is 0x%08X\n", __FUNCTION__, command, sc->state); } bxe_sp_event_exit: /* Force bxe_wait_ramrod() to see the change. */ mb(); DBEXIT(BXE_VERBOSE_RAMROD); } /* * Lock access to a hardware resource using controller arbitration * register. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_acquire_hw_lock(struct bxe_softc *sc, uint32_t resource) { uint32_t hw_lock_control_reg, lock_status, resource_bit; uint8_t func; int cnt, rc; DBENTER(BXE_VERBOSE_MISC); DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Locking resource 0x%08X\n", __FUNCTION__, resource); func = BP_FUNC(sc); resource_bit = 1 << resource; rc = 0; hw_lock_control_reg = ((func <= 5) ? (MISC_REG_DRIVER_CONTROL_1 + func * 8) : (MISC_REG_DRIVER_CONTROL_7 + (func - 6) * 8)); /* Validating that the resource is within range. */ if (resource > HW_LOCK_MAX_RESOURCE_VALUE) { DBPRINT(sc, BXE_WARN, "%s(): Resource is out of range! " "resource(0x%08X) > HW_LOCK_MAX_RESOURCE_VALUE(0x%08X)\n", __FUNCTION__, resource, HW_LOCK_MAX_RESOURCE_VALUE); rc = EINVAL; goto bxe_acquire_hw_lock_exit; } /* Validating that the resource is not already taken. */ lock_status = REG_RD(sc, hw_lock_control_reg); if (lock_status & resource_bit) { DBPRINT(sc, BXE_WARN, "%s(): Failed to acquire lock! " "lock_status = 0x%08X, resource_bit = 0x%08X\n", __FUNCTION__, lock_status, resource_bit); rc = EEXIST; goto bxe_acquire_hw_lock_exit; } /* Try for 5 seconds every 5ms. */ for (cnt = 0; cnt < 1000; cnt++) { /* Try to acquire the lock. */ REG_WR(sc, hw_lock_control_reg + 4, resource_bit); lock_status = REG_RD(sc, hw_lock_control_reg); if (lock_status & resource_bit) goto bxe_acquire_hw_lock_exit; DELAY(5000); } DBPRINT(sc, BXE_WARN, "%s(): Timeout!\n", __FUNCTION__); rc = EAGAIN; bxe_acquire_hw_lock_exit: DBEXIT(BXE_VERBOSE_MISC); return (rc); } /* * Unlock access to a hardware resource using controller arbitration * register. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_release_hw_lock(struct bxe_softc *sc, uint32_t resource) { uint32_t hw_lock_control_reg, lock_status, resource_bit; uint8_t func; int rc; DBENTER(BXE_VERBOSE_MISC); DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Unlocking resource 0x%08X\n", __FUNCTION__, resource); resource_bit = 1 << resource; func = BP_FUNC(sc); rc = 0; /* Validating that the resource is within range */ if (resource > HW_LOCK_MAX_RESOURCE_VALUE) { DBPRINT(sc, BXE_WARN, "%s(): Resource is out of range! " "resource(0x%08X) > HW_LOCK_MAX_RESOURCE_VALUE(0x%08X)\n", __FUNCTION__, resource, HW_LOCK_MAX_RESOURCE_VALUE); rc = EINVAL; goto bxe_release_hw_lock_exit; } /* Find the register for the resource lock. */ hw_lock_control_reg = ((func <= 5) ? (MISC_REG_DRIVER_CONTROL_1 + func * 8) : (MISC_REG_DRIVER_CONTROL_7 + (func - 6) * 8)); /* Validating that the resource is currently taken */ lock_status = REG_RD(sc, hw_lock_control_reg); if (!(lock_status & resource_bit)) { DBPRINT(sc, BXE_WARN, "%s(): The resource is not currently " "locked! lock_status = 0x%08X, resource_bit = 0x%08X\n", __FUNCTION__, lock_status, resource_bit); rc = EFAULT; goto bxe_release_hw_lock_exit; } /* Free the hardware lock. */ REG_WR(sc, hw_lock_control_reg, resource_bit); bxe_release_hw_lock_exit: DBEXIT(BXE_VERBOSE_MISC); return (rc); } int bxe_get_gpio(struct bxe_softc *sc, int gpio_num, uint8_t port) { uint32_t gpio_mask, gpio_reg; int gpio_port, gpio_shift, value; /* The GPIO should be swapped if swap register is set and active */ gpio_port = (REG_RD(sc, NIG_REG_PORT_SWAP) && REG_RD(sc, NIG_REG_STRAP_OVERRIDE)) ^ port; gpio_shift = gpio_num + (gpio_port ? MISC_REGISTERS_GPIO_PORT_SHIFT : 0); gpio_mask = 1 << gpio_shift; if (gpio_num > MISC_REGISTERS_GPIO_3) { DBPRINT(sc, BXE_WARN, "%s(): Invalid GPIO %d\n", __FUNCTION__, gpio_num); return (-EINVAL); } /* read GPIO value */ gpio_reg = REG_RD(sc, MISC_REG_GPIO); /* get the requested pin value */ if ((gpio_reg & gpio_mask) == gpio_mask) value = 1; else value = 0; DBPRINT(sc, BXE_VERBOSE_PHY, "pin %d value 0x%x\n", gpio_num, value); return (value); } /* * Sets the state of a General Purpose I/O (GPIO). * * Returns: * None. */ int bxe_set_gpio(struct bxe_softc *sc, int gpio_num, uint32_t mode, uint8_t port) { uint32_t gpio_reg, gpio_mask; int gpio_port, gpio_shift, rc; DBENTER(BXE_VERBOSE_MISC); /* The GPIO should be swapped if swap register is set and active. */ gpio_port = (REG_RD(sc, NIG_REG_PORT_SWAP) && REG_RD(sc, NIG_REG_STRAP_OVERRIDE)) ^ port; gpio_shift = gpio_num + (gpio_port ? MISC_REGISTERS_GPIO_PORT_SHIFT : 0); gpio_mask = (1 << gpio_shift); rc = 0; if (gpio_num > MISC_REGISTERS_GPIO_3) { DBPRINT(sc, BXE_FATAL, "%s(): Invalid GPIO (%d)!\n", __FUNCTION__, gpio_num); rc = EINVAL; goto bxe_set_gpio_exit; } /* Make sure no one else is trying to use the GPIO. */ rc = bxe_acquire_hw_lock(sc, HW_LOCK_RESOURCE_GPIO); if (rc) { DBPRINT(sc, BXE_WARN, "%s(): Can't acquire GPIO lock!\n", __FUNCTION__); goto bxe_set_gpio_exit; } /* Read GPIO and mask all but the float bits. */ gpio_reg = (REG_RD(sc, MISC_REG_GPIO) & MISC_REGISTERS_GPIO_FLOAT); switch (mode) { case MISC_REGISTERS_GPIO_OUTPUT_LOW: DBPRINT(sc, BXE_VERBOSE, "%s(): Set GPIO %d (shift %d) -> " "output low\n", __FUNCTION__, gpio_num, gpio_shift); gpio_reg &= ~(gpio_mask << MISC_REGISTERS_GPIO_FLOAT_POS); gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_CLR_POS); break; case MISC_REGISTERS_GPIO_OUTPUT_HIGH: DBPRINT(sc, BXE_VERBOSE, "%s(): Set GPIO %d (shift %d) -> " "output high\n", __FUNCTION__, gpio_num, gpio_shift); gpio_reg &= ~(gpio_mask << MISC_REGISTERS_GPIO_FLOAT_POS); gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_SET_POS); break; case MISC_REGISTERS_GPIO_INPUT_HI_Z: DBPRINT(sc, BXE_VERBOSE, "%s(): Set GPIO %d (shift %d) -> " "input\n", __FUNCTION__, gpio_num, gpio_shift); gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_FLOAT_POS); break; default: DBPRINT(sc, BXE_FATAL, "%s(): Unknown GPIO mode (0x%08X)!\n", __FUNCTION__, mode); break; } REG_WR(sc, MISC_REG_GPIO, gpio_reg); rc = bxe_release_hw_lock(sc, HW_LOCK_RESOURCE_GPIO); if (rc) { DBPRINT(sc, BXE_WARN, "%s(): Can't release GPIO lock!\n", __FUNCTION__); } bxe_set_gpio_exit: DBEXIT(BXE_VERBOSE_MISC); return (rc); } int bxe_set_gpio_int(struct bxe_softc *sc, int gpio_num, uint32_t mode, uint8_t port) { uint32_t gpio_mask, gpio_reg; int gpio_port, gpio_shift; /* The GPIO should be swapped if swap register is set and active */ gpio_port = (REG_RD(sc, NIG_REG_PORT_SWAP) && REG_RD(sc, NIG_REG_STRAP_OVERRIDE)) ^ port; gpio_shift = gpio_num + (gpio_port ? MISC_REGISTERS_GPIO_PORT_SHIFT : 0); gpio_mask = (1 << gpio_shift); if (gpio_num > MISC_REGISTERS_GPIO_3) { DBPRINT(sc, BXE_WARN, "%s(): Invalid GPIO %d\n", __FUNCTION__, gpio_num); return (-EINVAL); } bxe_acquire_hw_lock(sc, HW_LOCK_RESOURCE_GPIO); /* read GPIO int */ gpio_reg = REG_RD(sc, MISC_REG_GPIO_INT); switch (mode) { case MISC_REGISTERS_GPIO_INT_OUTPUT_CLR: DBPRINT(sc, BXE_VERBOSE_PHY, "Clear GPIO INT %d (shift %d) -> " "output low\n", gpio_num, gpio_shift); /* clear SET and set CLR */ gpio_reg &= ~(gpio_mask << MISC_REGISTERS_GPIO_INT_SET_POS); gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_INT_CLR_POS); break; case MISC_REGISTERS_GPIO_INT_OUTPUT_SET: DBPRINT(sc, BXE_VERBOSE_PHY, "Set GPIO INT %d (shift %d) -> " "output high\n", gpio_num, gpio_shift); /* clear CLR and set SET */ gpio_reg &= ~(gpio_mask << MISC_REGISTERS_GPIO_INT_CLR_POS); gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_INT_SET_POS); break; default: break; } REG_WR(sc, MISC_REG_GPIO_INT, gpio_reg); bxe_release_hw_lock(sc, HW_LOCK_RESOURCE_GPIO); return (0); } /* * Sets the state of a Shared Purpose I/O (SPIO). * * Returns: * 0 = Success, !0 = Failure. */ int bxe_set_spio(struct bxe_softc *sc, int spio_num, uint32_t mode) { uint32_t spio_reg, spio_mask; int rc; rc = 0; spio_mask = 1 << spio_num; /* Validate the SPIO. */ if ((spio_num < MISC_REGISTERS_SPIO_4) || (spio_num > MISC_REGISTERS_SPIO_7)) { DBPRINT(sc, BXE_WARN, "%s(): Invalid SPIO (%d)!\n", __FUNCTION__, spio_num); rc = EINVAL; goto bxe_set_spio_exit; } rc = bxe_acquire_hw_lock(sc, HW_LOCK_RESOURCE_SPIO); if (rc) { DBPRINT(sc, BXE_WARN, "%s(): Can't acquire SPIO lock!\n", __FUNCTION__); goto bxe_set_spio_exit; } /* Read SPIO and mask all but the float bits. */ spio_reg = (REG_RD(sc, MISC_REG_SPIO) & MISC_REGISTERS_SPIO_FLOAT); switch (mode) { case MISC_REGISTERS_SPIO_OUTPUT_LOW : DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Set SPIO %d -> " "output low\n", __FUNCTION__, spio_num); spio_reg &= ~(spio_mask << MISC_REGISTERS_SPIO_FLOAT_POS); spio_reg |= (spio_mask << MISC_REGISTERS_SPIO_CLR_POS); break; case MISC_REGISTERS_SPIO_OUTPUT_HIGH : DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Set SPIO %d -> " "output high\n", __FUNCTION__, spio_num); spio_reg &= ~(spio_mask << MISC_REGISTERS_SPIO_FLOAT_POS); spio_reg |= (spio_mask << MISC_REGISTERS_SPIO_SET_POS); break; case MISC_REGISTERS_SPIO_INPUT_HI_Z: DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Set SPIO %d -> " "input\n", __FUNCTION__, spio_num); spio_reg |= (spio_mask << MISC_REGISTERS_SPIO_FLOAT_POS); break; default: DBPRINT(sc, BXE_WARN, "%s(): Unknown SPIO mode (0x%08X)!\n", __FUNCTION__, mode); break; } REG_WR(sc, MISC_REG_SPIO, spio_reg); rc = bxe_release_hw_lock(sc, HW_LOCK_RESOURCE_SPIO); if (rc) { DBPRINT(sc, BXE_WARN, "%s(): Can't release SPIO lock!\n", __FUNCTION__); } bxe_set_spio_exit: return (rc); } /* * When the 57711E is operating in multi-function mode, the controller * must be configured to arbitrate TX between multiple VNICs. * * Returns: * None. */ static void bxe_init_port_minmax(struct bxe_softc *sc) { uint32_t fair_periodic_timeout_usec, r_param, t_fair; DBENTER(BXE_VERBOSE_MISC); r_param = sc->link_vars.line_speed / 8; memset(&(sc->cmng.rs_vars), 0, sizeof(struct rate_shaping_vars_per_port)); memset(&(sc->cmng.fair_vars), 0, sizeof(struct fairness_vars_per_port)); /* 100 usec in SDM ticks = 25 since each tick is 4 usec. */ sc->cmng.rs_vars.rs_periodic_timeout = RS_PERIODIC_TIMEOUT_USEC / 4; /* * This is the threshold below which no timer arming will occur. * We use a coefficient of 1, 25 so that the threshold is a * little bigger that real time to compensate for timer * in-accuracy. */ sc->cmng.rs_vars.rs_threshold = (RS_PERIODIC_TIMEOUT_USEC * r_param * 5) / 4; /* Resolution of fairness timer. */ fair_periodic_timeout_usec = QM_ARB_BYTES / r_param; /* For 10G it is 1000us, for 1G it is 10000us. */ t_fair = T_FAIR_COEF / sc->link_vars.line_speed; /* This is the threshold where we won't arm the timer anymore. */ sc->cmng.fair_vars.fair_threshold = QM_ARB_BYTES; /* * Multiply by 1e3/8 to get bytes/msec. We don't want the * credits to pass a credit of the T_FAIR*FAIR_MEM (algorithm * resolution) */ sc->cmng.fair_vars.upper_bound = r_param * t_fair * FAIR_MEM; /* Since each tick is 4 us. */ sc->cmng.fair_vars.fairness_timeout = fair_periodic_timeout_usec / 4; DBEXIT(BXE_VERBOSE_MISC); } /* * This function is called when a link interrupt is generated * and configures the controller for the new link state. * * Returns: * None. */ static void bxe_link_attn(struct bxe_softc *sc) { struct host_port_stats *pstats; uint32_t pause_enabled; int func, i, port, vn; DBENTER(BXE_VERBOSE_PHY); /* Make sure that we are synced with the current statistics. */ bxe_stats_handle(sc, STATS_EVENT_STOP); bxe_link_update(&sc->link_params, &sc->link_vars); if (sc->link_vars.link_up) { if (CHIP_IS_E1H(sc)) { port = BP_PORT(sc); pause_enabled = 0; if (sc->link_vars.flow_ctrl & FLOW_CTRL_TX) pause_enabled = 1; REG_WR(sc, BAR_USTORM_INTMEM + USTORM_ETH_PAUSE_ENABLED_OFFSET(port), pause_enabled); } if (sc->link_vars.mac_type == MAC_TYPE_BMAC) { pstats = BXE_SP(sc, port_stats); /* Reset old BMAC statistics. */ memset(&(pstats->mac_stx[0]), 0, sizeof(struct mac_stx)); } if ((sc->state == BXE_STATE_OPEN) || (sc->state == BXE_STATE_DISABLED)) bxe_stats_handle(sc, STATS_EVENT_LINK_UP); } /* Need additional handling for multi-function devices. */ if (IS_E1HMF(sc)) { port = BP_PORT(sc); if (sc->link_vars.link_up) { if (sc->dcc_enable == TRUE) { bxe_congestionmgmt(sc, TRUE); /* Store in internal memory. */ for (i = 0; i < sizeof(struct cmng_struct_per_port) / 4; i++) { REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_CMNG_PER_PORT_VARS_OFFSET(port) + (i*4), ((uint32_t *)(&sc->cmng))[i]); } } } for (vn = VN_0; vn < E1HVN_MAX; vn++) { /* Don't send an attention to ourselves. */ if (vn == BP_E1HVN(sc)) continue; func = ((vn << 1) | port); /* * Send an attention to other drivers on the same port. */ REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_0 + (LINK_SYNC_ATTENTION_BIT_FUNC_0 + func) * 4, 1); } } DBEXIT(BXE_VERBOSE_PHY); } /* * Sets the driver instance as the port management function (PMF). * * This is only used on "multi-function" capable devices such as the * 57711E and initializes the controller so that the PMF driver instance * can interact with other driver instances that may be operating on * the same Ethernet port. * * Returns: * None. */ static void bxe_pmf_update(struct bxe_softc *sc) { uint32_t val; int port; /* Record that this driver instance is managing the port. */ sc->port.pmf = 1; DBPRINT(sc, BXE_INFO, "%s(): Enabling this port as PMF.\n", __FUNCTION__); /* Enable NIG attention. */ port = BP_PORT(sc); val = (0xff0f | (1 << (BP_E1HVN(sc) + 4))); REG_WR(sc, HC_REG_TRAILING_EDGE_0 + port * 8, val); REG_WR(sc, HC_REG_LEADING_EDGE_0 + port * 8, val); bxe_stats_handle(sc, STATS_EVENT_PMF); } /* 8073 Download definitions */ /* spi Parameters.*/ #define SPI_CTRL_1_L 0xC000 #define SPI_CTRL_1_H 0xC002 #define SPI_CTRL_2_L 0xC400 #define SPI_CTRL_2_H 0xC402 #define SPI_TXFIFO 0xD000 #define SPI_RXFIFO 0xD400 /* Input Command Messages.*/ /* * Write CPU/SPI Control Regs, followed by Count And CPU/SPI Controller * Reg add/data pairs. */ #define WR_CPU_CTRL_REGS 0x11 /* * Read CPU/SPI Control Regs, followed by Count and CPU/SPI Controller * Register Add. */ #define RD_CPU_CTRL_REGS 0xEE /* * Write CPU/SPI Control Regs Continously, followed by Count and * CPU/SPI Controller Reg addr and data's. */ #define WR_CPU_CTRL_FIFO 0x66 /* Output Command Messages.*/ #define DONE 0x4321 /* SPI Controller Commands (known As messages).*/ #define MSGTYPE_HWR 0x40 #define MSGTYPE_HRD 0x80 #define WRSR_OPCODE 0x01 #define WR_OPCODE 0x02 #define RD_OPCODE 0x03 #define WRDI_OPCODE 0x04 #define RDSR_OPCODE 0x05 #define WREN_OPCODE 0x06 #define WR_BLOCK_SIZE 0x40 /* Maximum 64 Bytes Writes.*/ /* * Post a slowpath command. * * A slowpath command is used to propogate a configuration change through * the controller in a controlled manner, allowing each STORM processor and * other H/W blocks to phase in the change. The commands sent on the * slowpath are referred to as ramrods. Depending on the ramrod used the * completion of the ramrod will occur in different ways. Here's a * breakdown of ramrods and how they complete: * * RAMROD_CMD_ID_ETH_PORT_SETUP * Used to setup the leading connection on a port. Completes on the * Receive Completion Queue (RCQ) of that port (typically fp[0]). * * RAMROD_CMD_ID_ETH_CLIENT_SETUP * Used to setup an additional connection on a port. Completes on the * RCQ of the multi-queue/RSS connection being initialized. * * RAMROD_CMD_ID_ETH_STAT_QUERY * Used to force the storm processors to update the statistics database * in host memory. This ramrod is send on the leading connection CID and * completes as an index increment of the CSTORM on the default status * block. * * RAMROD_CMD_ID_ETH_UPDATE * Used to update the state of the leading connection, usually to udpate * the RSS indirection table. Completes on the RCQ of the leading * connection. (Not currently used under FreeBSD until OS support becomes * available.) * * RAMROD_CMD_ID_ETH_HALT * Used when tearing down a connection prior to driver unload. Completes * on the RCQ of the multi-queue/RSS connection being torn down. Don't * use this on the leading connection. * * RAMROD_CMD_ID_ETH_SET_MAC * Sets the Unicast/Broadcast/Multicast used by the port. Completes on * the RCQ of the leading connection. * * RAMROD_CMD_ID_ETH_CFC_DEL * Used when tearing down a conneciton prior to driver unload. Completes * on the RCQ of the leading connection (since the current connection * has been completely removed from controller memory). * * RAMROD_CMD_ID_ETH_PORT_DEL * Used to tear down the leading connection prior to driver unload, * typically fp[0]. Completes as an index increment of the CSTORM on the * default status block. * * RAMROD_CMD_ID_ETH_FORWARD_SETUP * Used for connection offload. Completes on the RCQ of the multi-queue * RSS connection that is being offloaded. (Not currently used under * FreeBSD.) * * There can only be one command pending per function. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_sp_post(struct bxe_softc *sc, int command, int cid, uint32_t data_hi, uint32_t data_lo, int common) { int func, rc; DBRUNMSG((BXE_EXTREME_LOAD | BXE_EXTREME_RESET | BXE_EXTREME_UNLOAD | BXE_EXTREME_RAMROD), bxe_decode_ramrod_cmd(sc, command)); DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): cid = %d, data_hi = 0x%08X, " "data_low = 0x%08X, remaining spq entries = %d\n", __FUNCTION__, cid, data_hi, data_lo, sc->spq_left); rc = 0; /* Skip all slowpath commands if the driver has panic'd. */ if (sc->panic) { rc = EIO; goto bxe_sp_post_exit; } BXE_SP_LOCK(sc); /* We are limited to 8 slowpath commands. */ if (!sc->spq_left) { BXE_PRINTF("%s(%d): Slowpath queue is full!\n", __FILE__, __LINE__); bxe_panic_dump(sc); rc = EBUSY; goto bxe_sp_post_exit; } /* Encode the CID with the command. */ sc->spq_prod_bd->hdr.conn_and_cmd_data = htole32(((command << SPE_HDR_CMD_ID_SHIFT) | HW_CID(sc, cid))); sc->spq_prod_bd->hdr.type = htole16(ETH_CONNECTION_TYPE); if (common) sc->spq_prod_bd->hdr.type |= htole16((1 << SPE_HDR_COMMON_RAMROD_SHIFT)); /* Point the hardware at the new configuration data. */ sc->spq_prod_bd->data.mac_config_addr.hi = htole32(data_hi); sc->spq_prod_bd->data.mac_config_addr.lo = htole32(data_lo); /* Reduce the number of available slots for slowpath commands. */ sc->spq_left--; /* Manage the end of the ring. */ if (sc->spq_prod_bd == sc->spq_last_bd) { sc->spq_prod_bd = sc->spq; sc->spq_prod_idx = 0; DBPRINT(sc, BXE_VERBOSE, "%s(): End of slowpath queue.\n", __FUNCTION__); } else { sc->spq_prod_bd++; sc->spq_prod_idx++; } func = BP_FUNC(sc); /* Kick off the slowpath command. */ REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_SPQ_PROD_OFFSET(func), sc->spq_prod_idx); bxe_sp_post_exit: BXE_SP_UNLOCK(sc); return (rc); } /* * Acquire the MCP access lock. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_acquire_alr(struct bxe_softc *sc) { uint32_t val; int i, rc, retries; DBENTER(BXE_VERBOSE_MISC); rc = 0; retries = 100; /* Acquire lock using mcpr_access_lock SPLIT register. */ for (i = 0; i < retries * 10; i++) { val = 1UL << 31; REG_WR(sc, GRCBASE_MCP + 0x9c, val); val = REG_RD(sc, GRCBASE_MCP + 0x9c); if (val & (1L << 31)) break; DELAY(5000); } if (!(val & (1L << 31))) { DBPRINT(sc, BXE_WARN, "%s(): Cannot acquire MCP split access lock.\n", __FUNCTION__); rc = EBUSY; } DBEXIT(BXE_VERBOSE_MISC); return (rc); } /* * Release the MCP access lock. * * Returns: * None. */ static void bxe_release_alr(struct bxe_softc* sc) { DBENTER(BXE_VERBOSE_MISC); REG_WR(sc, GRCBASE_MCP + 0x9c, 0); DBEXIT(BXE_VERBOSE_MISC); } /* * Update driver's copies of the values in the host default status block. * * Returns: * Bitmap indicating changes to the block. */ static __inline uint16_t bxe_update_dsb_idx(struct bxe_softc *sc) { struct host_def_status_block *dsb; uint16_t rc; rc = 0; dsb = sc->def_sb; /* Read memory barrier since block is written by hardware. */ rmb(); if (sc->def_att_idx != le16toh(dsb->atten_status_block.attn_bits_index)) { sc->def_att_idx = le16toh(dsb->atten_status_block.attn_bits_index); rc |= 0x1; } if (sc->def_c_idx != le16toh(dsb->c_def_status_block.status_block_index)) { sc->def_c_idx = le16toh(dsb->c_def_status_block.status_block_index); rc |= 0x2; } if (sc->def_u_idx != le16toh(dsb->u_def_status_block.status_block_index)) { sc->def_u_idx = le16toh(dsb->u_def_status_block.status_block_index); rc |= 0x4; } if (sc->def_x_idx != le16toh(dsb->x_def_status_block.status_block_index)) { sc->def_x_idx = le16toh(dsb->x_def_status_block.status_block_index); rc |= 0x8; } if (sc->def_t_idx != le16toh(dsb->t_def_status_block.status_block_index)) { sc->def_t_idx = le16toh(dsb->t_def_status_block.status_block_index); rc |= 0x10; } return (rc); } /* * Handle any attentions that have been newly asserted. * * Returns: * None */ static void bxe_attn_int_asserted(struct bxe_softc *sc, uint32_t asserted) { uint32_t aeu_addr, hc_addr, nig_int_mask_addr; uint32_t aeu_mask, nig_mask; int port, rc; DBENTER(BXE_VERBOSE_INTR); port = BP_PORT(sc); hc_addr = (HC_REG_COMMAND_REG + port * 32 + COMMAND_REG_ATTN_BITS_SET); aeu_addr = port ? MISC_REG_AEU_MASK_ATTN_FUNC_1 : MISC_REG_AEU_MASK_ATTN_FUNC_0; nig_int_mask_addr = port ? NIG_REG_MASK_INTERRUPT_PORT1 : NIG_REG_MASK_INTERRUPT_PORT0; nig_mask = 0; if (sc->attn_state & asserted) BXE_PRINTF("%s(%d): IGU attention ERROR!\n", __FILE__, __LINE__); rc = bxe_acquire_hw_lock(sc, HW_LOCK_RESOURCE_PORT0_ATT_MASK + port); if (rc) { DBPRINT(sc, BXE_WARN, "%s(): Failed to acquire attention lock for port %d!\n", __FUNCTION__, port); goto bxe_attn_int_asserted_exit; } aeu_mask = REG_RD(sc, aeu_addr); DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): aeu_mask = 0x%08X, newly asserted = 0x%08X\n", __FUNCTION__, aeu_mask, asserted); aeu_mask &= ~(asserted & 0xff); DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): new mask = 0x%08X\n", __FUNCTION__, aeu_mask); REG_WR(sc, aeu_addr, aeu_mask); rc = bxe_release_hw_lock(sc, HW_LOCK_RESOURCE_PORT0_ATT_MASK + port); if (rc) { DBPRINT(sc, BXE_WARN, "%s(): Failed to release attention lock!\n", __FUNCTION__); goto bxe_attn_int_asserted_exit; } DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): attn_state = 0x%08X\n", __FUNCTION__, sc->attn_state); sc->attn_state |= asserted; DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): new attn_state = 0x%08X\n", __FUNCTION__, sc->attn_state); if (asserted & ATTN_HARD_WIRED_MASK) { if (asserted & ATTN_NIG_FOR_FUNC) { bxe_acquire_phy_lock(sc); /* Save NIG interrupt mask. */ nig_mask = REG_RD(sc, nig_int_mask_addr); REG_WR(sc, nig_int_mask_addr, 0); bxe_link_attn(sc); } if (asserted & ATTN_SW_TIMER_4_FUNC) DBPRINT(sc, BXE_WARN, "%s(): ATTN_SW_TIMER_4_FUNC!\n", __FUNCTION__); if (asserted & GPIO_2_FUNC) DBPRINT(sc, BXE_WARN, "%s(): GPIO_2_FUNC!\n", __FUNCTION__); if (asserted & GPIO_3_FUNC) DBPRINT(sc, BXE_WARN, "%s(): GPIO_3_FUNC!\n", __FUNCTION__); if (asserted & GPIO_4_FUNC) DBPRINT(sc, BXE_WARN, "%s(): GPIO_4_FUNC!\n", __FUNCTION__); if (port == 0) { if (asserted & ATTN_GENERAL_ATTN_1) { DBPRINT(sc, BXE_WARN, "%s(): ATTN_GENERAL_ATTN_1!\n", __FUNCTION__); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_1, 0x0); } if (asserted & ATTN_GENERAL_ATTN_2) { DBPRINT(sc, BXE_WARN, "%s(): ATTN_GENERAL_ATTN_2!\n", __FUNCTION__); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_2, 0x0); } if (asserted & ATTN_GENERAL_ATTN_3) { DBPRINT(sc, BXE_WARN, "%s(): ATTN_GENERAL_ATTN_3!\n", __FUNCTION__); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_3, 0x0); } } else { if (asserted & ATTN_GENERAL_ATTN_4) { DBPRINT(sc, BXE_WARN, "%s(): ATTN_GENERAL_ATTN_4!\n", __FUNCTION__); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_4, 0x0); } if (asserted & ATTN_GENERAL_ATTN_5) { DBPRINT(sc, BXE_WARN, "%s(): ATTN_GENERAL_ATTN_5!\n", __FUNCTION__); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_5, 0x0); } if (asserted & ATTN_GENERAL_ATTN_6) { DBPRINT(sc, BXE_WARN, "%s(): ATTN_GENERAL_ATTN_6!\n", __FUNCTION__); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_6, 0x0); } } } DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): Writing 0x%08X to HC addr 0x%08X\n", __FUNCTION__, asserted, hc_addr); REG_WR(sc, hc_addr, asserted); /* Now set back the NIG mask. */ if (asserted & ATTN_NIG_FOR_FUNC) { REG_WR(sc, nig_int_mask_addr, nig_mask); bxe_release_phy_lock(sc); } bxe_attn_int_asserted_exit: DBEXIT(BXE_VERBOSE_INTR); } /* * Handle any attentions that have been newly deasserted. * * Returns: * None */ static __inline void bxe_attn_int_deasserted0(struct bxe_softc *sc, uint32_t attn) { uint32_t val, swap_val, swap_override; int port, reg_offset; DBENTER(BXE_VERBOSE_INTR); port = BP_PORT(sc); reg_offset = port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0 : MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0; /* Handle SPIO5 attention. */ if (attn & AEU_INPUTS_ATTN_BITS_SPIO5) { val = REG_RD(sc, reg_offset); val &= ~AEU_INPUTS_ATTN_BITS_SPIO5; REG_WR(sc, reg_offset, val); DBPRINT(sc, BXE_FATAL, "%s(): SPIO5 H/W attention!\n", __FUNCTION__); /* Fan failure attention */ switch (XGXS_EXT_PHY_TYPE(sc->link_params.ext_phy_config)) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: /* * SPIO5 is used on A1022G boards to indicate * fan failure. Shutdown the controller and * associated PHY to avoid damage. */ /* Low power mode is controled by GPIO 2. */ bxe_set_gpio(sc, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); /* PHY reset is controled by GPIO 1. */ bxe_set_gpio(sc, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481: /* * The PHY reset is controlled by GPIO 1. * Fake the port number to cancel the swap done in * set_gpio(). */ swap_val = REG_RD(sc, NIG_REG_PORT_SWAP); swap_override = REG_RD(sc, NIG_REG_STRAP_OVERRIDE); port = (swap_val && swap_override) ^ 1; bxe_set_gpio(sc, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); break; default: break; } /* Mark the failure. */ sc->link_params.ext_phy_config &= ~PORT_HW_CFG_XGXS_EXT_PHY_TYPE_MASK; sc->link_params.ext_phy_config |= PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE; SHMEM_WR(sc, dev_info.port_hw_config[port].external_phy_config, sc->link_params.ext_phy_config); /* Log the failure */ BXE_PRINTF("A fan failure has caused the driver to " "shutdown the device to prevent permanent damage.\n"); } if (attn & (AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_0 | AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_1)) { bxe_acquire_phy_lock(sc); bxe_handle_module_detect_int(&sc->link_params); bxe_release_phy_lock(sc); } /* Checking for an assert on the zero block */ if (attn & HW_INTERRUT_ASSERT_SET_0) { val = REG_RD(sc, reg_offset); val &= ~(attn & HW_INTERRUT_ASSERT_SET_0); REG_WR(sc, reg_offset, val); BXE_PRINTF("%s(%d): FATAL hardware block attention " "(set0 = 0x%08X)!\n", __FILE__, __LINE__, (attn & (uint32_t)HW_INTERRUT_ASSERT_SET_0)); bxe_panic_dump(sc); } DBEXIT(BXE_VERBOSE_INTR); } /* * Handle any attentions that have been newly deasserted. * * Returns: * None */ static __inline void bxe_attn_int_deasserted1(struct bxe_softc *sc, uint32_t attn) { uint32_t val; int port, reg_offset; DBENTER(BXE_VERBOSE_INTR); if (attn & AEU_INPUTS_ATTN_BITS_DOORBELLQ_HW_INTERRUPT) { val = REG_RD(sc, DORQ_REG_DORQ_INT_STS_CLR); DBPRINT(sc, BXE_FATAL, "%s(): Doorbell hardware attention (0x%08X).\n", __FUNCTION__, val); /* DORQ discard attention */ if (val & 0x2) DBPRINT(sc, BXE_FATAL, "%s(): FATAL doorbell queue error!\n", __FUNCTION__); } if (attn & HW_INTERRUT_ASSERT_SET_1) { port = BP_PORT(sc); reg_offset = port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_1 : MISC_REG_AEU_ENABLE1_FUNC_0_OUT_1; val = REG_RD(sc, reg_offset); val &= ~(attn & HW_INTERRUT_ASSERT_SET_1); REG_WR(sc, reg_offset, val); BXE_PRINTF("%s(%d): FATAL hardware block attention " "(set1 = 0x%08X)!\n", __FILE__, __LINE__, (attn & (uint32_t)HW_INTERRUT_ASSERT_SET_1)); bxe_panic_dump(sc); } DBEXIT(BXE_VERBOSE_INTR); } /* * Handle any attentions that have been newly deasserted. * * Returns: * None */ static __inline void bxe_attn_int_deasserted2(struct bxe_softc *sc, uint32_t attn) { uint32_t val; int port, reg_offset; DBENTER(BXE_VERBOSE_INTR); if (attn & AEU_INPUTS_ATTN_BITS_CFC_HW_INTERRUPT) { val = REG_RD(sc, CFC_REG_CFC_INT_STS_CLR); DBPRINT(sc, BXE_FATAL, "%s(): CFC hardware attention (0x%08X).\n", __FUNCTION__, val); /* CFC error attention. */ if (val & 0x2) DBPRINT(sc, BXE_FATAL, "%s(): FATAL CFC error!\n", __FUNCTION__); } if (attn & AEU_INPUTS_ATTN_BITS_PXP_HW_INTERRUPT) { val = REG_RD(sc, PXP_REG_PXP_INT_STS_CLR_0); DBPRINT(sc, BXE_FATAL, "%s(): PXP hardware attention (0x%08X).\n", __FUNCTION__, val); /* RQ_USDMDP_FIFO_OVERFLOW */ if (val & 0x18000) DBPRINT(sc, BXE_FATAL, "%s(): FATAL PXP error!\n", __FUNCTION__); } if (attn & HW_INTERRUT_ASSERT_SET_2) { port = BP_PORT(sc); reg_offset = port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_2 : MISC_REG_AEU_ENABLE1_FUNC_0_OUT_2; val = REG_RD(sc, reg_offset); val &= ~(attn & HW_INTERRUT_ASSERT_SET_2); REG_WR(sc, reg_offset, val); BXE_PRINTF("%s(%d): FATAL hardware block attention (set2 = " "0x%08X)! port=%d, val written=0x%x attn=0x%x\n", __FILE__, __LINE__, (attn & (uint32_t)HW_INTERRUT_ASSERT_SET_2), port, val, attn); bxe_panic_dump(sc); } DBEXIT(BXE_VERBOSE_INTR); } /* * Handle any attentions that have been newly deasserted. * * Returns: * None */ static __inline void bxe_attn_int_deasserted3(struct bxe_softc *sc, uint32_t attn) { uint32_t val; int func; DBENTER(BXE_VERBOSE_INTR); if (attn & EVEREST_GEN_ATTN_IN_USE_MASK) { /* Look for any port assertions. */ if (attn & BXE_PMF_LINK_ASSERT) { /* * We received a message from the driver instance * that is managing the Ethernet port (link up/down). * Go ahead and handle it. */ func = BP_FUNC(sc); DBPRINT(sc, BXE_INFO, "%s(): Received link attention from PMF.\n", __FUNCTION__); /* Clear the attention. */ REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_12 + func * 4, 0); sc->mf_config[BP_E1HVN(sc)] = SHMEM_RD(sc, mf_cfg.func_mf_config[(sc->bxe_func & 1)].config); val = SHMEM_RD(sc, func_mb[func].drv_status); if (sc->dcc_enable == TRUE) { if (val & DRV_STATUS_DCC_EVENT_MASK) bxe_dcc_event(sc, val & DRV_STATUS_DCC_EVENT_MASK); } bxe__link_status_update(sc); if ((sc->port.pmf == 0) && (val & DRV_STATUS_PMF)) bxe_pmf_update(sc); /* Look for any microcode assertions. */ } else if (attn & BXE_MC_ASSERT_BITS) { DBPRINT(sc, BXE_FATAL, "%s(): Microcode assert!\n", __FUNCTION__); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_10, 0); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_9, 0); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_8, 0); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_7, 0); bxe_panic_dump(sc); /* Look for any bootcode assertions. */ } else if (attn & BXE_MCP_ASSERT) { DBPRINT(sc, BXE_FATAL, "%s(): Bootcode assert!\n", __FUNCTION__); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_11, 0); DBRUN(bxe_dump_fw(sc)); } else DBPRINT(sc, BXE_FATAL, "%s(): Unknown hardware assertion " "(attn = 0x%08X)!\n", __FUNCTION__, attn); } /* Look for any hardware latched attentions. */ if (attn & EVEREST_LATCHED_ATTN_IN_USE_MASK) { DBPRINT(sc, BXE_FATAL, "%s(): Latched attention 0x%08X (masked)!\n", __FUNCTION__, attn); /* Check if a GRC register access timeout occurred. */ if (attn & BXE_GRC_TIMEOUT) { val = CHIP_IS_E1H(sc) ? REG_RD(sc, MISC_REG_GRC_TIMEOUT_ATTN) : 0; DBPRINT(sc, BXE_WARN, "%s(): GRC timeout for register 0x%08X!\n", __FUNCTION__, val); } /* Check if a GRC reserved register was accessed. */ if (attn & BXE_GRC_RSV) { val = CHIP_IS_E1H(sc) ? REG_RD(sc, MISC_REG_GRC_RSV_ATTN) : 0; DBPRINT(sc, BXE_WARN, "%s(): GRC register 0x%08X is reserved!\n", __FUNCTION__, val); } REG_WR(sc, MISC_REG_AEU_CLR_LATCH_SIGNAL, 0x7ff); } DBEXIT(BXE_VERBOSE_INTR); } /* * Handle any attentions that have been newly deasserted. * * Returns: * None */ static void bxe_attn_int_deasserted(struct bxe_softc *sc, uint32_t deasserted) { struct attn_route attn; struct attn_route group_mask; uint32_t val, reg_addr, aeu_mask; int index, port; DBENTER(BXE_VERBOSE_INTR); /* * Need to take HW lock because MCP or other port might also try * to handle this event. */ bxe_acquire_alr(sc); port = BP_PORT(sc); /* Get the current attention signal bits. */ attn.sig[0] = REG_RD(sc, MISC_REG_AEU_AFTER_INVERT_1_FUNC_0 + port * 4); attn.sig[1] = REG_RD(sc, MISC_REG_AEU_AFTER_INVERT_2_FUNC_0 + port * 4); attn.sig[2] = REG_RD(sc, MISC_REG_AEU_AFTER_INVERT_3_FUNC_0 + port * 4); attn.sig[3] = REG_RD(sc, MISC_REG_AEU_AFTER_INVERT_4_FUNC_0 + port * 4); DBPRINT(sc, BXE_EXTREME_INTR, "%s(): attention = 0x%08X 0x%08X 0x%08X 0x%08X\n", __FUNCTION__, attn.sig[0], attn.sig[1], attn.sig[2], attn.sig[3]); /* * Compare the current attention bits to each attention group * to see if anyone has registered this attention. */ for (index = 0; index < MAX_DYNAMIC_ATTN_GRPS; index++) { if (deasserted & (1 << index)) { group_mask = sc->attn_group[index]; DBPRINT(sc, BXE_EXTREME_INTR, "%s(): group[%02d] = 0x%08X 0x%08X 0x%08x 0X%08x\n", __FUNCTION__, index, group_mask.sig[0], group_mask.sig[1], group_mask.sig[2], group_mask.sig[3]); /* Handle any registered attentions. */ bxe_attn_int_deasserted3(sc, attn.sig[3] & group_mask.sig[3]); bxe_attn_int_deasserted1(sc, attn.sig[1] & group_mask.sig[1]); bxe_attn_int_deasserted2(sc, attn.sig[2] & group_mask.sig[2]); bxe_attn_int_deasserted0(sc, attn.sig[0] & group_mask.sig[0]); if ((attn.sig[0] & group_mask.sig[0] & HW_PRTY_ASSERT_SET_0) || (attn.sig[1] & group_mask.sig[1] & HW_PRTY_ASSERT_SET_1) || (attn.sig[2] & group_mask.sig[2] & HW_PRTY_ASSERT_SET_2)) BXE_PRINTF("%s(%d): FATAL hardware block " "parity attention!\n", __FILE__, __LINE__); } } bxe_release_alr(sc); reg_addr = (HC_REG_COMMAND_REG + port * 32 + COMMAND_REG_ATTN_BITS_CLR); val = ~deasserted; DBPRINT(sc, BXE_EXTREME_INTR, "%s(): About to mask 0x%08X at HC addr 0x%08X\n", __FUNCTION__, deasserted, reg_addr); REG_WR(sc, reg_addr, val); if (~sc->attn_state & deasserted) DBPRINT(sc, BXE_FATAL, "%s(): IGU Bug!\n", __FUNCTION__); reg_addr = port ? MISC_REG_AEU_MASK_ATTN_FUNC_1 : MISC_REG_AEU_MASK_ATTN_FUNC_0; bxe_acquire_hw_lock(sc, HW_LOCK_RESOURCE_PORT0_ATT_MASK + port); aeu_mask = REG_RD(sc, reg_addr); DBPRINT(sc, BXE_EXTREME_INTR, "%s(): Current aeu_mask = 0x%08X, newly deasserted = 0x%08X\n", __FUNCTION__, aeu_mask, deasserted); aeu_mask |= (deasserted & 0xff); DBPRINT(sc, BXE_EXTREME_INTR, "%s(): New aeu_mask = 0x%08X\n", __FUNCTION__, aeu_mask); REG_WR(sc, reg_addr, aeu_mask); bxe_release_hw_lock(sc, HW_LOCK_RESOURCE_PORT0_ATT_MASK + port); DBPRINT(sc, BXE_EXTREME_INTR, "%s(): Current attn_state = 0x%08X\n", __FUNCTION__, sc->attn_state); sc->attn_state &= ~deasserted; DBPRINT(sc, BXE_EXTREME_INTR, "%s(): New attn_state = 0x%08X\n", __FUNCTION__, sc->attn_state); DBEXIT(BXE_VERBOSE_INTR); } /* * Handle interrupts caused by internal attentions (everything else other * than RX, TX, and link state changes). * * Returns: * None */ static void bxe_attn_int(struct bxe_softc* sc) { uint32_t attn_ack, attn_bits, attn_state; uint32_t asserted, deasserted; DBENTER(BXE_VERBOSE_INTR); attn_bits = le32toh(sc->def_sb->atten_status_block.attn_bits); attn_ack = le32toh(sc->def_sb->atten_status_block.attn_bits_ack); attn_state = sc->attn_state; asserted = attn_bits & ~attn_ack & ~attn_state; deasserted = ~attn_bits & attn_ack & attn_state; /* Make sure we're in a sane state. */ if (~(attn_bits ^ attn_ack) & (attn_bits ^ attn_state)) BXE_PRINTF("%s(%d): Bad attention state!\n", __FILE__, __LINE__); /* Handle any attentions that are newly asserted. */ if (asserted) { DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): attn_state = 0x%08X, attn_bits = 0x%08X, " "attn_ack = 0x%08X, asserted = 0x%08X\n", __FUNCTION__, attn_state, attn_bits, attn_ack, asserted); bxe_attn_int_asserted(sc, asserted); } /* Handle any attentions that are newly deasserted. */ if (deasserted) { DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): attn_state = 0x%08X, attn_bits = 0x%08X, " "attn_ack = 0x%08X, deasserted = 0x%08X\n", __FUNCTION__, attn_state, attn_bits, attn_ack, deasserted); bxe_attn_int_deasserted(sc, deasserted); } DBEXIT(BXE_VERBOSE_INTR); } /* sum[hi:lo] += add[hi:lo] */ #define ADD_64(s_hi, a_hi, s_lo, a_lo) do { \ s_lo += a_lo; \ s_hi += a_hi + ((s_lo < a_lo) ? 1 : 0); \ } while (0) /* Subtraction = minuend -= subtrahend */ #define SUB_64(m_hi, s_hi, m_lo, s_lo) \ do { \ DIFF_64(m_hi, m_hi, s_hi, m_lo, m_lo, s_lo); \ } while (0) /* difference = minuend - subtrahend */ #define DIFF_64(d_hi, m_hi, s_hi, d_lo, m_lo, s_lo) do { \ if (m_lo < s_lo) { \ /* underflow */ \ d_hi = m_hi - s_hi; \ if (d_hi > 0) { \ /* we can 'loan' 1 */ \ d_hi--; \ d_lo = m_lo + (UINT_MAX - s_lo) + 1; \ } else { \ /* m_hi <= s_hi */ \ d_hi = 0; \ d_lo = 0; \ } \ } else { \ /* m_lo >= s_lo */ \ if (m_hi < s_hi) { \ d_hi = 0; \ d_lo = 0; \ } else { \ /* m_hi >= s_hi */ \ d_hi = m_hi - s_hi; \ d_lo = m_lo - s_lo; \ } \ } \ } while (0) #define UPDATE_STAT64(s, t) do { \ DIFF_64(diff.hi, new->s##_hi, pstats->mac_stx[0].t##_hi,\ diff.lo, new->s##_lo, pstats->mac_stx[0].t##_lo); \ pstats->mac_stx[0].t##_hi = new->s##_hi; \ pstats->mac_stx[0].t##_lo = new->s##_lo; \ ADD_64(pstats->mac_stx[1].t##_hi, diff.hi, \ pstats->mac_stx[1].t##_lo, diff.lo); \ } while (0) #define UPDATE_STAT64_NIG(s, t) do { \ DIFF_64(diff.hi, new->s##_hi, old->s##_hi, \ diff.lo, new->s##_lo, old->s##_lo); \ ADD_64(estats->t##_hi, diff.hi, \ estats->t##_lo, diff.lo); \ } while (0) /* sum[hi:lo] += add */ #define ADD_EXTEND_64(s_hi, s_lo, a) do { \ s_lo += a; \ s_hi += (s_lo < a) ? 1 : 0; \ } while (0) #define UPDATE_EXTEND_STAT(s) do { \ ADD_EXTEND_64(pstats->mac_stx[1].s##_hi, \ pstats->mac_stx[1].s##_lo, new->s); \ } while (0) #define UPDATE_EXTEND_TSTAT(s, t) do { \ diff = (tclient->s) - (old_tclient->s); \ old_tclient->s = (tclient->s); \ ADD_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \ } while (0) #define UPDATE_EXTEND_XSTAT(s, t) do { \ diff = xclient->s - old_xclient->s; \ old_xclient->s = xclient->s; \ ADD_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \ } while (0) #define UPDATE_EXTEND_USTAT(s, t) do { \ diff = uclient->s - old_uclient->s; \ old_uclient->s = uclient->s; \ ADD_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \ } while (0) #define SUB_EXTEND_64(m_hi, m_lo, s)do { \ SUB_64(m_hi, 0, m_lo, s); \ } while (0) #define SUB_EXTEND_USTAT(s, t)do { \ diff = (uclient->s) - (old_uclient->s); \ SUB_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \ } while (0) #ifdef __i386__ #define BITS_PER_LONG 32 #else #define BITS_PER_LONG 64 #endif static __inline long bxe_hilo(uint32_t *hiref) { uint32_t lo; lo = *(hiref + 1); #if (BITS_PER_LONG == 64) uint32_t hi = *hiref; return (HILO_U64(hi, lo)); #else return (lo); #endif } /* * Request the STORM statistics by posting a slowpath ramrod. * * Returns: * None. */ static void bxe_stats_storm_post(struct bxe_softc *sc) { struct eth_query_ramrod_data ramrod_data = {0}; int i, rc; DBENTER(BXE_INSANE_STATS); if (!sc->stats_pending) { ramrod_data.drv_counter = sc->stats_counter++; ramrod_data.collect_port = sc->port.pmf ? 1 : 0; for (i = 0; i < sc->num_queues; i++) ramrod_data.ctr_id_vector |= (1 << sc->fp[i].cl_id); rc = bxe_sp_post(sc, RAMROD_CMD_ID_ETH_STAT_QUERY, 0, ((uint32_t *)&ramrod_data)[1], ((uint32_t *)&ramrod_data)[0], 0); if (rc == 0) { /* Stats ramrod has it's own slot on the SPQ. */ sc->spq_left++; sc->stats_pending = 1; } } DBEXIT(BXE_INSANE_STATS); } /* * Setup the adrress used by the driver to report port-based statistics * back to the controller. * * Returns: * None. */ static void bxe_stats_port_base_init(struct bxe_softc *sc) { uint32_t *stats_comp; struct dmae_command *dmae; DBENTER(BXE_VERBOSE_STATS); /* Only the port management function (PMF) does this work. */ if ((sc->port.pmf == 0) || !sc->port.port_stx) { BXE_PRINTF("%s(%d): Invalid statistcs port setup!\n", __FILE__, __LINE__); goto bxe_stats_port_base_init_exit; } stats_comp = BXE_SP(sc, stats_comp); sc->executer_idx = 0; /* DMA the address of the drivers port statistics block. */ dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = (DMAE_CMD_SRC_PCI | DMAE_CMD_DST_GRC | DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (BP_PORT(sc) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (BP_E1HVN(sc) << DMAE_CMD_E1HVN_SHIFT)); dmae->src_addr_lo = U64_LO(BXE_SP_MAPPING(sc, port_stats)); dmae->src_addr_hi = U64_HI(BXE_SP_MAPPING(sc, port_stats)); dmae->dst_addr_lo = sc->port.port_stx >> 2; dmae->dst_addr_hi = 0; dmae->len = sizeof(struct host_port_stats) >> 2; dmae->comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_val = DMAE_COMP_VAL; *stats_comp = 0; bxe_stats_hw_post(sc); bxe_stats_comp(sc); bxe_stats_port_base_init_exit: DBEXIT(BXE_VERBOSE_STATS); } /* * Setup the adrress used by the driver to report function-based statistics * back to the controller. * * Returns: * None. */ static void bxe_stats_func_base_init(struct bxe_softc *sc) { int port, func; int vn, vn_max; uint32_t func_stx; DBENTER(BXE_VERBOSE_STATS); /* Only the port management function (PMF) does this work. */ if ((sc->port.pmf == 0) || !sc->func_stx) { BXE_PRINTF("%s(%d): Invalid statistcs function setup!\n", __FILE__, __LINE__); goto bxe_stats_func_base_init_exit; } port = BP_PORT(sc); func_stx = sc->func_stx; vn_max = IS_E1HMF(sc) ? E1HVN_MAX : E1VN_MAX; /* Initialize each function individually. */ for (vn = VN_0; vn < vn_max; vn++) { func = 2 * vn + port; sc->func_stx = SHMEM_RD(sc, func_mb[func].fw_mb_param); bxe_stats_func_init(sc); bxe_stats_hw_post(sc); bxe_stats_comp(sc); } sc->func_stx = func_stx; bxe_stats_func_base_init_exit: DBEXIT(BXE_VERBOSE_STATS); } /* * DMA the function-based statistics to the controller. * * Returns: * None. */ static void bxe_stats_func_base_update(struct bxe_softc *sc) { uint32_t *stats_comp; struct dmae_command *dmae; DBENTER(BXE_VERBOSE_STATS); /* Only the port management function (PMF) does this work. */ if ((sc->port.pmf == 0) || !sc->func_stx) { BXE_PRINTF("%s(%d): Invalid statistcs function update!\n", __FILE__, __LINE__); goto bxe_stats_func_base_update_exit; } dmae = &sc->stats_dmae; stats_comp = BXE_SP(sc, stats_comp); sc->executer_idx = 0; memset(dmae, 0, sizeof(struct dmae_command)); /* DMA the function statistics from the driver to the H/W. */ dmae->opcode = (DMAE_CMD_SRC_GRC | DMAE_CMD_DST_PCI | DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (BP_PORT(sc) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (BP_E1HVN(sc) << DMAE_CMD_E1HVN_SHIFT)); dmae->src_addr_lo = sc->func_stx >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, func_stats_base)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, func_stats_base)); dmae->len = sizeof(struct host_func_stats) >> 2; dmae->comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_val = DMAE_COMP_VAL; *stats_comp = 0; bxe_stats_hw_post(sc); bxe_stats_comp(sc); bxe_stats_func_base_update_exit: DBEXIT(BXE_VERBOSE_STATS); } /* * Initialize statistics. * * Returns: * Nothing. */ static void bxe_stats_init(struct bxe_softc *sc) { struct bxe_fastpath *fp; int func, i, port; DBENTER(BXE_VERBOSE_STATS); if (sc->stats_enable == FALSE) goto bxe_stats_init_exit; port = BP_PORT(sc); func = BP_FUNC(sc); sc->executer_idx = 0; sc->stats_counter = 0; sc->stats_pending = 0; /* Fetch the offset of port & function statistics in shared memory. */ if (NOMCP(sc)){ sc->port.port_stx = 0; sc->func_stx = 0; } else{ sc->port.port_stx = SHMEM_RD(sc, port_mb[port].port_stx); sc->func_stx = SHMEM_RD(sc, func_mb[func].fw_mb_param); } DBPRINT(sc, BXE_VERBOSE_STATS, "%s(): sc->port.port_stx = 0x%08X\n", __FUNCTION__, sc->port.port_stx); DBPRINT(sc, BXE_VERBOSE_STATS, "%s(): sc->func_stx = 0x%08X\n", __FUNCTION__, sc->func_stx); /* Port statistics. */ memset(&(sc->port.old_nig_stats), 0, sizeof(struct nig_stats)); sc->port.old_nig_stats.brb_discard = REG_RD(sc, NIG_REG_STAT0_BRB_DISCARD + port * 0x38); sc->port.old_nig_stats.brb_truncate = REG_RD(sc, NIG_REG_STAT0_BRB_TRUNCATE + port * 0x38); REG_RD_DMAE(sc, NIG_REG_STAT0_EGRESS_MAC_PKT0 + port * 0x50, &(sc->port.old_nig_stats.egress_mac_pkt0_lo), 2); REG_RD_DMAE(sc, NIG_REG_STAT0_EGRESS_MAC_PKT1 + port * 0x50, &(sc->port.old_nig_stats.egress_mac_pkt1_lo), 2); /* Function statistics. */ for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; /* Clear all per-queue statistics. */ memset(&fp->old_tclient, 0, sizeof(struct tstorm_per_client_stats)); memset(&fp->old_uclient, 0, sizeof(struct ustorm_per_client_stats)); memset(&fp->old_xclient, 0, sizeof(struct xstorm_per_client_stats)); memset(&fp->eth_q_stats, 0, sizeof(struct bxe_q_stats)); } /* ToDo: Clear any driver specific statistics? */ sc->stats_state = STATS_STATE_DISABLED; if (sc->port.pmf == 1) { /* Init port & function stats if we're PMF. */ if (sc->port.port_stx) bxe_stats_port_base_init(sc); if (sc->func_stx) bxe_stats_func_base_init(sc); } else if (sc->func_stx) /* Update function stats if we're not PMF. */ bxe_stats_func_base_update(sc); bxe_stats_init_exit: DBEXIT(BXE_VERBOSE_STATS); } /* * * Returns: * None. */ static void bxe_stats_hw_post(struct bxe_softc *sc) { struct dmae_command *dmae; uint32_t *stats_comp; int loader_idx; DBENTER(BXE_INSANE_STATS); dmae = &sc->stats_dmae; stats_comp = BXE_SP(sc, stats_comp); *stats_comp = DMAE_COMP_VAL; if (sc->executer_idx) { loader_idx = PMF_DMAE_C(sc); memset(dmae, 0, sizeof(struct dmae_command)); dmae->opcode = (DMAE_CMD_SRC_PCI | DMAE_CMD_DST_GRC | DMAE_CMD_C_DST_GRC | DMAE_CMD_C_ENABLE | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (BP_PORT(sc) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (BP_E1HVN(sc) << DMAE_CMD_E1HVN_SHIFT)); dmae->src_addr_lo = U64_LO(BXE_SP_MAPPING(sc, dmae[0])); dmae->src_addr_hi = U64_HI(BXE_SP_MAPPING(sc, dmae[0])); dmae->dst_addr_lo = (DMAE_REG_CMD_MEM + sizeof(struct dmae_command) * (loader_idx + 1)) >> 2; dmae->dst_addr_hi = 0; dmae->len = sizeof(struct dmae_command) >> 2; if (CHIP_IS_E1(sc)) dmae->len--; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx + 1] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; *stats_comp = 0; bxe_post_dmae(sc, dmae, loader_idx); } else if (sc->func_stx) { *stats_comp = 0; bxe_post_dmae(sc, dmae, INIT_DMAE_C(sc)); } DBEXIT(BXE_INSANE_STATS); } /* * Delay routine which polls for the DMA engine to complete. * * Returns: * 0 = Failure, !0 = Success */ static int bxe_stats_comp(struct bxe_softc *sc) { uint32_t *stats_comp; int cnt; DBENTER(BXE_VERBOSE_STATS); stats_comp = BXE_SP(sc, stats_comp); cnt = 10; while (*stats_comp != DMAE_COMP_VAL) { if (!cnt) { BXE_PRINTF("%s(%d): Timeout waiting for statistics " "completions.\n", __FILE__, __LINE__); break; } cnt--; DELAY(1000); } DBEXIT(BXE_VERBOSE_STATS); /* ToDo: Shouldn't this return the value of cnt? */ return (1); } /* * DMA port statistcs from controller to driver. * * Returns: * None. */ static void bxe_stats_pmf_update(struct bxe_softc *sc) { struct dmae_command *dmae; uint32_t opcode, *stats_comp; int loader_idx; DBENTER(BXE_VERBOSE_STATS); stats_comp = BXE_SP(sc, stats_comp); loader_idx = PMF_DMAE_C(sc); /* We shouldn't be here if any of the following are false. */ if (!IS_E1HMF(sc) || (sc->port.pmf == 0) || !sc->port.port_stx) { BXE_PRINTF("%s(%d): Statistics bug!\n", __FILE__, __LINE__); goto bxe_stats_pmf_update_exit; } sc->executer_idx = 0; /* Instruct DMA engine to copy port statistics from H/W to driver. */ opcode = (DMAE_CMD_SRC_GRC | DMAE_CMD_DST_PCI | DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (BP_PORT(sc) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (BP_E1HVN(sc) << DMAE_CMD_E1HVN_SHIFT)); dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = (opcode | DMAE_CMD_C_DST_GRC); dmae->src_addr_lo = sc->port.port_stx >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, port_stats)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, port_stats)); dmae->len = DMAE_LEN32_RD_MAX; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = (opcode | DMAE_CMD_C_DST_PCI); dmae->src_addr_lo = (sc->port.port_stx >> 2) + DMAE_LEN32_RD_MAX; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, port_stats) + DMAE_LEN32_RD_MAX * 4); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, port_stats) + DMAE_LEN32_RD_MAX * 4); dmae->len = (sizeof(struct host_port_stats) >> 2) - DMAE_LEN32_RD_MAX; dmae->comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_val = DMAE_COMP_VAL; /* Start the DMA and wait for the result. */ *stats_comp = 0; bxe_stats_hw_post(sc); bxe_stats_comp(sc); bxe_stats_pmf_update_exit: DBEXIT(BXE_VERBOSE_STATS); } /* * Prepare the DMAE parameters required for all statistics. * * This function should only be called by the driver instance * that is designated as the port management function (PMF). * * Returns: * None. */ static void bxe_stats_port_init(struct bxe_softc *sc) { struct dmae_command *dmae; uint32_t mac_addr, opcode, *stats_comp; int loader_idx, port, vn; DBENTER(BXE_VERBOSE_STATS); port = BP_PORT(sc); vn = BP_E1HVN(sc); loader_idx = PMF_DMAE_C(sc); stats_comp = BXE_SP(sc, stats_comp); /* Only the port management function (PMF) does this work. */ if (!sc->link_vars.link_up || (sc->port.pmf == 0)) { BXE_PRINTF("%s(%d): Invalid statistics port setup!\n", __FILE__, __LINE__); goto bxe_stats_port_init_exit; } sc->executer_idx = 0; /* The same opcde is used for multiple DMA operations. */ opcode = (DMAE_CMD_SRC_PCI | DMAE_CMD_DST_GRC | DMAE_CMD_C_DST_GRC | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (port ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (vn << DMAE_CMD_E1HVN_SHIFT)); /* Setup the DMA for port statistics. */ if (sc->port.port_stx) { dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = U64_LO(BXE_SP_MAPPING(sc, port_stats)); dmae->src_addr_hi = U64_HI(BXE_SP_MAPPING(sc, port_stats)); dmae->dst_addr_lo = sc->port.port_stx >> 2; dmae->dst_addr_hi = 0; dmae->len = sizeof(struct host_port_stats) >> 2; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; } /* Setup the DMA for function statistics. */ if (sc->func_stx) { dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = U64_LO(BXE_SP_MAPPING(sc, func_stats)); dmae->src_addr_hi = U64_HI(BXE_SP_MAPPING(sc, func_stats)); dmae->dst_addr_lo = sc->func_stx >> 2; dmae->dst_addr_hi = 0; dmae->len = sizeof(struct host_func_stats) >> 2; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; } /* Setup statistics reporting for the MAC. */ opcode = (DMAE_CMD_SRC_GRC | DMAE_CMD_DST_PCI | DMAE_CMD_C_DST_GRC | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (port ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (vn << DMAE_CMD_E1HVN_SHIFT)); if (sc->link_vars.mac_type == MAC_TYPE_BMAC) { /* Enable statistics for the 10Gb BMAC. */ mac_addr = (port ? NIG_REG_INGRESS_BMAC1_MEM : NIG_REG_INGRESS_BMAC0_MEM); /* Setup BMAC TX statistics (TX_STAT_GTPKT .. TX_STAT_GTBYT). */ dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = (mac_addr + BIGMAC_REGISTER_TX_STAT_GTPKT) >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, mac_stats)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, mac_stats)); dmae->len = (8 + BIGMAC_REGISTER_TX_STAT_GTBYT - BIGMAC_REGISTER_TX_STAT_GTPKT) >> 2; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; /* Setup BMAC RX statistcs (RX_STAT_GR64 .. RX_STAT_GRIPJ). */ dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = (mac_addr + BIGMAC_REGISTER_RX_STAT_GR64) >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, mac_stats) + offsetof(struct bmac_stats, rx_stat_gr64_lo)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, mac_stats) + offsetof(struct bmac_stats, rx_stat_gr64_lo)); dmae->len = (8 + BIGMAC_REGISTER_RX_STAT_GRIPJ - BIGMAC_REGISTER_RX_STAT_GR64) >> 2; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; } else if (sc->link_vars.mac_type == MAC_TYPE_EMAC) { /* Enable statistics for the 1Gb EMAC. */ mac_addr = (port ? GRCBASE_EMAC1 : GRCBASE_EMAC0); /* Setup EMAC RX statistics. */ dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = (mac_addr + EMAC_REG_EMAC_RX_STAT_AC) >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, mac_stats)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, mac_stats)); dmae->len = EMAC_REG_EMAC_RX_STAT_AC_COUNT; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; /* Setup additional EMAC RX statistics. */ dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = (mac_addr + EMAC_REG_EMAC_RX_STAT_AC_28) >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, mac_stats) + offsetof(struct emac_stats, rx_stat_falsecarriererrors)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, mac_stats) + offsetof(struct emac_stats, rx_stat_falsecarriererrors)); dmae->len = 1; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; /* Setup EMAC TX statistics. */ dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = (mac_addr + EMAC_REG_EMAC_TX_STAT_AC) >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, mac_stats) + offsetof(struct emac_stats, tx_stat_ifhcoutoctets)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, mac_stats) + offsetof(struct emac_stats, tx_stat_ifhcoutoctets)); dmae->len = EMAC_REG_EMAC_TX_STAT_AC_COUNT; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; } else { DBPRINT(sc, BXE_WARN, "%s(): Undefined MAC type.\n", __FUNCTION__); } /* Enable NIG statistics. */ dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = (port ? NIG_REG_STAT1_BRB_DISCARD : NIG_REG_STAT0_BRB_DISCARD) >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, nig_stats)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, nig_stats)); dmae->len = (sizeof(struct nig_stats) - 4 * sizeof(uint32_t)) >> 2; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = (port ? NIG_REG_STAT1_EGRESS_MAC_PKT0 : NIG_REG_STAT0_EGRESS_MAC_PKT0) >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, nig_stats) + offsetof(struct nig_stats, egress_mac_pkt0_lo)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, nig_stats) + offsetof(struct nig_stats, egress_mac_pkt0_lo)); dmae->len = (2 * sizeof(uint32_t)) >> 2; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = (DMAE_CMD_SRC_GRC | DMAE_CMD_DST_PCI | DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (port ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (vn << DMAE_CMD_E1HVN_SHIFT)); dmae->src_addr_lo = (port ? NIG_REG_STAT1_EGRESS_MAC_PKT1 : NIG_REG_STAT0_EGRESS_MAC_PKT1) >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, nig_stats) + offsetof(struct nig_stats, egress_mac_pkt1_lo)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, nig_stats) + offsetof(struct nig_stats, egress_mac_pkt1_lo)); dmae->len = (2 * sizeof(uint32_t)) >> 2; dmae->comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_val = DMAE_COMP_VAL; /* Clear the statistics completion value. */ *stats_comp = 0; bxe_stats_port_init_exit: DBEXIT(BXE_VERBOSE_STATS); } /* * Prepare the DMAE parameters required for function statistics. * * This function is called by all driver instances. * * Returns: * None. */ static void bxe_stats_func_init(struct bxe_softc *sc) { struct dmae_command *dmae; uint32_t *stats_comp; DBENTER(BXE_VERBOSE_STATS); if (!sc->func_stx) { BXE_PRINTF("%s(%d): Invalid statistics function setup!\n", __FILE__, __LINE__); goto bxe_stats_func_init_exit; } dmae = &sc->stats_dmae; stats_comp = BXE_SP(sc, stats_comp); sc->executer_idx = 0; memset(dmae, 0, sizeof(struct dmae_command)); /* Setup the DMA for function statistics. */ dmae->opcode = (DMAE_CMD_SRC_PCI | DMAE_CMD_DST_GRC | DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (BP_PORT(sc) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (BP_E1HVN(sc) << DMAE_CMD_E1HVN_SHIFT)); dmae->src_addr_lo = U64_LO(BXE_SP_MAPPING(sc, func_stats)); dmae->src_addr_hi = U64_HI(BXE_SP_MAPPING(sc, func_stats)); dmae->dst_addr_lo = sc->func_stx >> 2; dmae->dst_addr_hi = 0; dmae->len = sizeof(struct host_func_stats) >> 2; dmae->comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_val = DMAE_COMP_VAL; *stats_comp = 0; bxe_stats_func_init_exit: DBEXIT(BXE_VERBOSE_STATS); } /* * Starts a statistics update DMA and waits for completion. * * Returns: * None. */ static void bxe_stats_start(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_STATS); if (sc->port.pmf == 1) bxe_stats_port_init(sc); else if (sc->func_stx) bxe_stats_func_init(sc); bxe_stats_hw_post(sc); bxe_stats_storm_post(sc); DBEXIT(BXE_VERBOSE_STATS); } /* * Returns: * None. */ static void bxe_stats_pmf_start(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_STATS); bxe_stats_comp(sc); bxe_stats_pmf_update(sc); bxe_stats_start(sc); DBEXIT(BXE_VERBOSE_STATS); } /* * Returns: * None. */ static void bxe_stats_restart(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_STATS); bxe_stats_comp(sc); bxe_stats_start(sc); DBEXIT(BXE_VERBOSE_STATS); } /* * Update the Big MAC (10Gb BMAC) statistics. * * Returns: * None. */ static void bxe_stats_bmac_update(struct bxe_softc *sc) { struct bmac_stats *new; struct host_port_stats *pstats; struct bxe_port_stats *estats; struct regpair diff; DBENTER(BXE_INSANE_STATS); new = BXE_SP(sc, mac_stats.bmac_stats); pstats = BXE_SP(sc, port_stats); estats = &sc->eth_stats; UPDATE_STAT64(rx_stat_grerb, rx_stat_ifhcinbadoctets); UPDATE_STAT64(rx_stat_grfcs, rx_stat_dot3statsfcserrors); UPDATE_STAT64(rx_stat_grund, rx_stat_etherstatsundersizepkts); UPDATE_STAT64(rx_stat_grovr, rx_stat_dot3statsframestoolong); UPDATE_STAT64(rx_stat_grfrg, rx_stat_etherstatsfragments); UPDATE_STAT64(rx_stat_grjbr, rx_stat_etherstatsjabbers); UPDATE_STAT64(rx_stat_grxcf, rx_stat_maccontrolframesreceived); UPDATE_STAT64(rx_stat_grxpf, rx_stat_xoffstateentered); UPDATE_STAT64(rx_stat_grxpf, rx_stat_bmac_xpf); UPDATE_STAT64(tx_stat_gtxpf, tx_stat_outxoffsent); UPDATE_STAT64(tx_stat_gtxpf, tx_stat_flowcontroldone); UPDATE_STAT64(tx_stat_gt64, tx_stat_etherstatspkts64octets); UPDATE_STAT64(tx_stat_gt127, tx_stat_etherstatspkts65octetsto127octets); UPDATE_STAT64(tx_stat_gt255, tx_stat_etherstatspkts128octetsto255octets); UPDATE_STAT64(tx_stat_gt511, tx_stat_etherstatspkts256octetsto511octets); UPDATE_STAT64(tx_stat_gt1023, tx_stat_etherstatspkts512octetsto1023octets); UPDATE_STAT64(tx_stat_gt1518, tx_stat_etherstatspkts1024octetsto1522octets); UPDATE_STAT64(tx_stat_gt2047, tx_stat_bmac_2047); UPDATE_STAT64(tx_stat_gt4095, tx_stat_bmac_4095); UPDATE_STAT64(tx_stat_gt9216, tx_stat_bmac_9216); UPDATE_STAT64(tx_stat_gt16383, tx_stat_bmac_16383); UPDATE_STAT64(tx_stat_gterr, tx_stat_dot3statsinternalmactransmiterrors); UPDATE_STAT64(tx_stat_gtufl, tx_stat_bmac_ufl); estats->pause_frames_received_hi = pstats->mac_stx[1].rx_stat_bmac_xpf_hi; estats->pause_frames_received_lo = pstats->mac_stx[1].rx_stat_bmac_xpf_lo; estats->pause_frames_sent_hi = pstats->mac_stx[1].tx_stat_outxoffsent_hi; estats->pause_frames_sent_lo = pstats->mac_stx[1].tx_stat_outxoffsent_lo; DBEXIT(BXE_INSANE_STATS); } /* * Update the Ethernet MAC (1Gb EMAC) statistics. * * Returns: * None. */ static void bxe_stats_emac_update(struct bxe_softc *sc) { struct emac_stats *new; struct host_port_stats *pstats; struct bxe_port_stats *estats; DBENTER(BXE_INSANE_STATS); new = BXE_SP(sc, mac_stats.emac_stats); pstats = BXE_SP(sc, port_stats); estats = &sc->eth_stats; UPDATE_EXTEND_STAT(rx_stat_ifhcinbadoctets); UPDATE_EXTEND_STAT(tx_stat_ifhcoutbadoctets); UPDATE_EXTEND_STAT(rx_stat_dot3statsfcserrors); UPDATE_EXTEND_STAT(rx_stat_dot3statsalignmenterrors); UPDATE_EXTEND_STAT(rx_stat_dot3statscarriersenseerrors); UPDATE_EXTEND_STAT(rx_stat_falsecarriererrors); UPDATE_EXTEND_STAT(rx_stat_etherstatsundersizepkts); UPDATE_EXTEND_STAT(rx_stat_dot3statsframestoolong); UPDATE_EXTEND_STAT(rx_stat_etherstatsfragments); UPDATE_EXTEND_STAT(rx_stat_etherstatsjabbers); UPDATE_EXTEND_STAT(rx_stat_maccontrolframesreceived); UPDATE_EXTEND_STAT(rx_stat_xoffstateentered); UPDATE_EXTEND_STAT(rx_stat_xonpauseframesreceived); UPDATE_EXTEND_STAT(rx_stat_xoffpauseframesreceived); UPDATE_EXTEND_STAT(tx_stat_outxonsent); UPDATE_EXTEND_STAT(tx_stat_outxoffsent); UPDATE_EXTEND_STAT(tx_stat_flowcontroldone); UPDATE_EXTEND_STAT(tx_stat_etherstatscollisions); UPDATE_EXTEND_STAT(tx_stat_dot3statssinglecollisionframes); UPDATE_EXTEND_STAT(tx_stat_dot3statsmultiplecollisionframes); UPDATE_EXTEND_STAT(tx_stat_dot3statsdeferredtransmissions); UPDATE_EXTEND_STAT(tx_stat_dot3statsexcessivecollisions); UPDATE_EXTEND_STAT(tx_stat_dot3statslatecollisions); UPDATE_EXTEND_STAT(tx_stat_etherstatspkts64octets); UPDATE_EXTEND_STAT(tx_stat_etherstatspkts65octetsto127octets); UPDATE_EXTEND_STAT(tx_stat_etherstatspkts128octetsto255octets); UPDATE_EXTEND_STAT(tx_stat_etherstatspkts256octetsto511octets); UPDATE_EXTEND_STAT(tx_stat_etherstatspkts512octetsto1023octets); UPDATE_EXTEND_STAT(tx_stat_etherstatspkts1024octetsto1522octets); UPDATE_EXTEND_STAT(tx_stat_etherstatspktsover1522octets); UPDATE_EXTEND_STAT(tx_stat_dot3statsinternalmactransmiterrors); estats->pause_frames_received_hi = pstats->mac_stx[1].rx_stat_xonpauseframesreceived_hi; estats->pause_frames_received_lo = pstats->mac_stx[1].rx_stat_xonpauseframesreceived_lo; ADD_64(estats->pause_frames_received_hi, pstats->mac_stx[1].rx_stat_xoffpauseframesreceived_hi, estats->pause_frames_received_lo, pstats->mac_stx[1].rx_stat_xoffpauseframesreceived_lo); estats->pause_frames_sent_hi = pstats->mac_stx[1].tx_stat_outxonsent_hi; estats->pause_frames_sent_lo = pstats->mac_stx[1].tx_stat_outxonsent_lo; ADD_64(estats->pause_frames_sent_hi, pstats->mac_stx[1].tx_stat_outxoffsent_hi, estats->pause_frames_sent_lo, pstats->mac_stx[1].tx_stat_outxoffsent_lo); DBEXIT(BXE_INSANE_STATS); } /* * Returns: * 0 = Success, !0 = Failure. */ static int bxe_stats_hw_update(struct bxe_softc *sc) { struct nig_stats *new, *old; struct host_port_stats *pstats; struct bxe_port_stats *estats; struct regpair diff; uint32_t nig_timer_max; int rc; DBENTER(BXE_INSANE_STATS); rc = 0; new = BXE_SP(sc, nig_stats); old = &(sc->port.old_nig_stats); pstats = BXE_SP(sc, port_stats); estats = &sc->eth_stats; /* Update statistics for the active MAC. */ if (sc->link_vars.mac_type == MAC_TYPE_BMAC) bxe_stats_bmac_update(sc); else if (sc->link_vars.mac_type == MAC_TYPE_EMAC) bxe_stats_emac_update(sc); else { DBPRINT(sc, BXE_WARN, "%s(): Statistics updated by DMAE but no MAC is active!\n", __FUNCTION__); rc = EINVAL; goto bxe_stats_hw_update_exit; } /* Now update the hardware (NIG) statistics. */ ADD_EXTEND_64(pstats->brb_drop_hi, pstats->brb_drop_lo, new->brb_discard - old->brb_discard); ADD_EXTEND_64(estats->brb_truncate_hi, estats->brb_truncate_lo, new->brb_truncate - old->brb_truncate); UPDATE_STAT64_NIG(egress_mac_pkt0, etherstatspkts1024octetsto1522octets); UPDATE_STAT64_NIG(egress_mac_pkt1, etherstatspktsover1522octets); memcpy(old, new, sizeof(struct nig_stats)); memcpy(&(estats->rx_stat_ifhcinbadoctets_hi), &(pstats->mac_stx[1]), sizeof(struct mac_stx)); estats->brb_drop_hi = pstats->brb_drop_hi; estats->brb_drop_lo = pstats->brb_drop_lo; pstats->host_port_stats_start = ++pstats->host_port_stats_end; if (!NOMCP(sc)) { nig_timer_max = SHMEM_RD(sc, port_mb[BP_PORT(sc)].stat_nig_timer); if (nig_timer_max != estats->nig_timer_max) { estats->nig_timer_max = nig_timer_max; DBPRINT(sc, BXE_WARN, "%s(): NIG timer reached max value (%u)!\n", __FUNCTION__, estats->nig_timer_max); } } bxe_stats_hw_update_exit: DBEXIT(BXE_INSANE_STATS); return (rc); } /* * Returns: * 0 = Success, !0 = Failure. */ // DRC - Done static int bxe_stats_storm_update(struct bxe_softc *sc) { int rc, i, cl_id; struct eth_stats_query *stats; struct bxe_port_stats *estats; struct host_func_stats *fstats; struct bxe_q_stats *qstats; struct tstorm_per_port_stats *tport; struct tstorm_per_client_stats *tclient; struct ustorm_per_client_stats *uclient; struct xstorm_per_client_stats *xclient; struct tstorm_per_client_stats *old_tclient; struct ustorm_per_client_stats *old_uclient; struct xstorm_per_client_stats *old_xclient; struct bxe_fastpath * fp; uint32_t diff; DBENTER(BXE_INSANE_STATS); rc = 0; diff = 0; stats = BXE_SP(sc, fw_stats); tport = &stats->tstorm_common.port_statistics; fstats = BXE_SP(sc, func_stats); memcpy(&(fstats->total_bytes_received_hi), &(BXE_SP(sc, func_stats_base)->total_bytes_received_hi), sizeof(struct host_func_stats) - 2 * sizeof(uint32_t)); estats = &sc->eth_stats; estats->no_buff_discard_hi = 0; estats->no_buff_discard_lo = 0; estats->error_bytes_received_hi = 0; estats->error_bytes_received_lo = 0; estats->etherstatsoverrsizepkts_hi = 0; estats->etherstatsoverrsizepkts_lo = 0; for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; cl_id = fp->cl_id; tclient = &stats->tstorm_common.client_statistics[cl_id]; old_tclient = &fp->old_tclient; uclient = &stats->ustorm_common.client_statistics[cl_id]; old_uclient = &fp->old_uclient; xclient = &stats->xstorm_common.client_statistics[cl_id]; old_xclient = &fp->old_xclient; qstats = &fp->eth_q_stats; /* Are TSTORM statistics valid? */ if ((uint16_t)(le16toh(tclient->stats_counter) + 1) != sc->stats_counter) { DBPRINT(sc, BXE_WARN, "%s(): Stats not updated by TSTORM " "(tstorm counter (%d) != stats_counter (%d))!\n", __FUNCTION__, tclient->stats_counter, sc->stats_counter); rc = 1; goto bxe_stats_storm_update_exit; } /* Are USTORM statistics valid? */ if ((uint16_t)(le16toh(uclient->stats_counter) + 1) != sc->stats_counter) { DBPRINT(sc, BXE_WARN, "%s(): Stats not updated by USTORM " "(ustorm counter (%d) != stats_counter (%d))!\n", __FUNCTION__, uclient->stats_counter, sc->stats_counter); rc = 2; goto bxe_stats_storm_update_exit; } /* Are XSTORM statistics valid? */ if ((uint16_t)(le16toh(xclient->stats_counter) + 1) != sc->stats_counter) { DBPRINT(sc, BXE_WARN, "%s(): Stats not updated by XSTORM " "(xstorm counter (%d) != stats_counter (%d))!\n", __FUNCTION__, xclient->stats_counter, sc->stats_counter); rc = 3; goto bxe_stats_storm_update_exit; } qstats->total_bytes_received_hi = (tclient->rcv_broadcast_bytes.hi); qstats->total_bytes_received_lo = le32toh(tclient->rcv_broadcast_bytes.lo); ADD_64(qstats->total_bytes_received_hi, le32toh(tclient->rcv_multicast_bytes.hi), qstats->total_bytes_received_lo, le32toh(tclient->rcv_multicast_bytes.lo)); ADD_64(qstats->total_bytes_received_hi, le32toh(tclient->rcv_unicast_bytes.hi), qstats->total_bytes_received_lo, le32toh(tclient->rcv_unicast_bytes.lo)); SUB_64(qstats->total_bytes_received_hi, le32toh(uclient->bcast_no_buff_bytes.hi), qstats->total_bytes_received_lo, le32toh(uclient->bcast_no_buff_bytes.lo)); SUB_64(qstats->total_bytes_received_hi, le32toh(uclient->mcast_no_buff_bytes.hi), qstats->total_bytes_received_lo, le32toh(uclient->mcast_no_buff_bytes.lo)); SUB_64(qstats->total_bytes_received_hi, le32toh(uclient->ucast_no_buff_bytes.hi), qstats->total_bytes_received_lo, le32toh(uclient->ucast_no_buff_bytes.lo)); qstats->valid_bytes_received_hi = qstats->total_bytes_received_hi; qstats->valid_bytes_received_lo = qstats->total_bytes_received_lo; qstats->error_bytes_received_hi = le32toh(tclient->rcv_error_bytes.hi); qstats->error_bytes_received_lo = le32toh(tclient->rcv_error_bytes.lo); ADD_64(qstats->total_bytes_received_hi, qstats->error_bytes_received_hi, qstats->total_bytes_received_lo, qstats->error_bytes_received_lo); UPDATE_EXTEND_TSTAT(rcv_unicast_pkts, total_unicast_packets_received); UPDATE_EXTEND_TSTAT(rcv_multicast_pkts, total_multicast_packets_received); UPDATE_EXTEND_TSTAT(rcv_broadcast_pkts, total_broadcast_packets_received); UPDATE_EXTEND_TSTAT(packets_too_big_discard, etherstatsoverrsizepkts); UPDATE_EXTEND_TSTAT(no_buff_discard, no_buff_discard); SUB_EXTEND_USTAT(ucast_no_buff_pkts, total_unicast_packets_received); SUB_EXTEND_USTAT(mcast_no_buff_pkts, total_multicast_packets_received); SUB_EXTEND_USTAT(bcast_no_buff_pkts, total_broadcast_packets_received); UPDATE_EXTEND_USTAT(ucast_no_buff_pkts, no_buff_discard); UPDATE_EXTEND_USTAT(mcast_no_buff_pkts, no_buff_discard); UPDATE_EXTEND_USTAT(bcast_no_buff_pkts, no_buff_discard); qstats->total_bytes_transmitted_hi = le32toh(xclient->unicast_bytes_sent.hi); qstats->total_bytes_transmitted_lo = le32toh(xclient->unicast_bytes_sent.lo); ADD_64(qstats->total_bytes_transmitted_hi, le32toh(xclient->multicast_bytes_sent.hi), qstats->total_bytes_transmitted_lo, le32toh(xclient->multicast_bytes_sent.lo)); ADD_64(qstats->total_bytes_transmitted_hi, le32toh(xclient->broadcast_bytes_sent.hi), qstats->total_bytes_transmitted_lo, le32toh(xclient->broadcast_bytes_sent.lo)); UPDATE_EXTEND_XSTAT(unicast_pkts_sent, total_unicast_packets_transmitted); UPDATE_EXTEND_XSTAT(multicast_pkts_sent, total_multicast_packets_transmitted); UPDATE_EXTEND_XSTAT(broadcast_pkts_sent, total_broadcast_packets_transmitted); old_tclient->checksum_discard = tclient->checksum_discard; old_tclient->ttl0_discard = tclient->ttl0_discard; ADD_64(fstats->total_bytes_received_hi, qstats->total_bytes_received_hi, fstats->total_bytes_received_lo, qstats->total_bytes_received_lo); ADD_64(fstats->total_bytes_transmitted_hi, qstats->total_bytes_transmitted_hi, fstats->total_bytes_transmitted_lo, qstats->total_bytes_transmitted_lo); ADD_64(fstats->total_unicast_packets_received_hi, qstats->total_unicast_packets_received_hi, fstats->total_unicast_packets_received_lo, qstats->total_unicast_packets_received_lo); ADD_64(fstats->total_multicast_packets_received_hi, qstats->total_multicast_packets_received_hi, fstats->total_multicast_packets_received_lo, qstats->total_multicast_packets_received_lo); ADD_64(fstats->total_broadcast_packets_received_hi, qstats->total_broadcast_packets_received_hi, fstats->total_broadcast_packets_received_lo, qstats->total_broadcast_packets_received_lo); ADD_64(fstats->total_unicast_packets_transmitted_hi, qstats->total_unicast_packets_transmitted_hi, fstats->total_unicast_packets_transmitted_lo, qstats->total_unicast_packets_transmitted_lo); ADD_64(fstats->total_multicast_packets_transmitted_hi, qstats->total_multicast_packets_transmitted_hi, fstats->total_multicast_packets_transmitted_lo, qstats->total_multicast_packets_transmitted_lo); ADD_64(fstats->total_broadcast_packets_transmitted_hi, qstats->total_broadcast_packets_transmitted_hi, fstats->total_broadcast_packets_transmitted_lo, qstats->total_broadcast_packets_transmitted_lo); ADD_64(fstats->valid_bytes_received_hi, qstats->valid_bytes_received_hi, fstats->valid_bytes_received_lo, qstats->valid_bytes_received_lo); ADD_64(estats->error_bytes_received_hi, qstats->error_bytes_received_hi, estats->error_bytes_received_lo, qstats->error_bytes_received_lo); ADD_64(estats->etherstatsoverrsizepkts_hi, qstats->etherstatsoverrsizepkts_hi, estats->etherstatsoverrsizepkts_lo, qstats->etherstatsoverrsizepkts_lo); ADD_64(estats->no_buff_discard_hi, qstats->no_buff_discard_hi, estats->no_buff_discard_lo, qstats->no_buff_discard_lo); } ADD_64(fstats->total_bytes_received_hi, estats->rx_stat_ifhcinbadoctets_hi, fstats->total_bytes_received_lo, estats->rx_stat_ifhcinbadoctets_lo); memcpy(estats, &(fstats->total_bytes_received_hi), sizeof(struct host_func_stats) - 2 * sizeof(uint32_t)); ADD_64(estats->etherstatsoverrsizepkts_hi, estats->rx_stat_dot3statsframestoolong_hi, estats->etherstatsoverrsizepkts_lo, estats->rx_stat_dot3statsframestoolong_lo); ADD_64(estats->error_bytes_received_hi, estats->rx_stat_ifhcinbadoctets_hi, estats->error_bytes_received_lo, estats->rx_stat_ifhcinbadoctets_lo); if (sc->port.pmf) { estats->mac_filter_discard = le32toh(tport->mac_filter_discard); estats->xxoverflow_discard = le32toh(tport->xxoverflow_discard); estats->brb_truncate_discard = le32toh(tport->brb_truncate_discard); estats->mac_discard = le32toh(tport->mac_discard); } fstats->host_func_stats_start = ++fstats->host_func_stats_end; sc->stats_pending = 0; bxe_stats_storm_update_exit: DBEXIT(BXE_INSANE_STATS); return (rc); } /* * Copy the controller maintained statistics over to the OS. * * Returns: * None. */ static void bxe_stats_net_update(struct bxe_softc *sc) { struct tstorm_per_client_stats *old_tclient; struct bxe_port_stats *estats; struct ifnet *ifp; DBENTER(BXE_INSANE_STATS); old_tclient = &sc->fp[0].old_tclient; estats = &sc->eth_stats; ifp = sc->bxe_ifp; /* * Update the OS interface statistics from * the hardware statistics. */ ifp->if_collisions = (u_long) estats->tx_stat_dot3statssinglecollisionframes_lo + (u_long) estats->tx_stat_dot3statsmultiplecollisionframes_lo + (u_long) estats->tx_stat_dot3statslatecollisions_lo + (u_long) estats->tx_stat_dot3statsexcessivecollisions_lo; ifp->if_ierrors = (u_long) old_tclient->checksum_discard + (u_long) estats->no_buff_discard_lo + (u_long) estats->mac_discard + (u_long) estats->rx_stat_etherstatsundersizepkts_lo + (u_long) estats->brb_drop_lo + (u_long) estats->brb_truncate_discard + (u_long) estats->rx_stat_dot3statsfcserrors_lo + (u_long) estats->rx_stat_dot3statsalignmenterrors_lo + (u_long) estats->xxoverflow_discard; ifp->if_oerrors = (u_long) estats->tx_stat_dot3statslatecollisions_lo + (u_long) estats->tx_stat_dot3statsexcessivecollisions_lo + (u_long) estats->tx_stat_dot3statsinternalmactransmiterrors_lo; ifp->if_ipackets = bxe_hilo(&estats->total_unicast_packets_received_hi) + bxe_hilo(&estats->total_multicast_packets_received_hi) + bxe_hilo(&estats->total_broadcast_packets_received_hi); ifp->if_opackets = bxe_hilo(&estats->total_unicast_packets_transmitted_hi) + bxe_hilo(&estats->total_multicast_packets_transmitted_hi) + bxe_hilo(&estats->total_broadcast_packets_transmitted_hi); DBEXIT(BXE_INSANE_STATS); } /* * * Returns: * None. */ static void bxe_stats_update(struct bxe_softc *sc) { uint32_t *stats_comp; int update; DBENTER(BXE_INSANE_STATS); stats_comp = BXE_SP(sc, stats_comp); update = 0; /* Make sure the statistics DMAE update has completed. */ if (*stats_comp != DMAE_COMP_VAL) goto bxe_stats_update_exit; /* Check for any hardware statistics updates. */ if (sc->port.pmf == 1) update = (bxe_stats_hw_update(sc) == 0); /* Check for any STORM statistics updates. */ update |= (bxe_stats_storm_update(sc) == 0); /* If we got updated hardware statistics then update the OS. */ if (update) bxe_stats_net_update(sc); else { /* Check if any statistics updates are pending. */ if (sc->stats_pending) { /* The update hasn't completed, keep waiting. */ sc->stats_pending++; /* Have we been waiting for too long? */ if (sc->stats_pending >= 3) { BXE_PRINTF( "%s(%d): Failed to get statistics after " "3 tries!\n", __FILE__, __LINE__); bxe_panic_dump(sc); goto bxe_stats_update_exit; } } } /* Kickoff the next statistics request. */ bxe_stats_hw_post(sc); bxe_stats_storm_post(sc); bxe_stats_update_exit: DBEXIT(BXE_INSANE_STATS); } /* * * Returns: * None. */ static void bxe_stats_port_stop(struct bxe_softc *sc) { struct dmae_command *dmae; uint32_t opcode, *stats_comp; int loader_idx; DBENTER(BXE_VERBOSE_STATS); stats_comp = BXE_SP(sc, stats_comp); loader_idx = PMF_DMAE_C(sc); sc->executer_idx = 0; opcode = (DMAE_CMD_SRC_PCI | DMAE_CMD_DST_GRC | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (BP_PORT(sc) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (BP_E1HVN(sc) << DMAE_CMD_E1HVN_SHIFT)); if (sc->port.port_stx) { dmae = BXE_SP(sc, dmae[sc->executer_idx++]); if (sc->func_stx) dmae->opcode = (opcode | DMAE_CMD_C_DST_GRC); else dmae->opcode = (opcode | DMAE_CMD_C_DST_PCI); dmae->src_addr_lo = U64_LO(BXE_SP_MAPPING(sc, port_stats)); dmae->src_addr_hi = U64_HI(BXE_SP_MAPPING(sc, port_stats)); dmae->dst_addr_lo = sc->port.port_stx >> 2; dmae->dst_addr_hi = 0; dmae->len = sizeof(struct host_port_stats) >> 2; if (sc->func_stx) { dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; } else { dmae->comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_val = DMAE_COMP_VAL; *stats_comp = 0; } } if (sc->func_stx) { dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = (opcode | DMAE_CMD_C_DST_PCI); dmae->src_addr_lo = U64_LO(BXE_SP_MAPPING(sc, func_stats)); dmae->src_addr_hi = U64_HI(BXE_SP_MAPPING(sc, func_stats)); dmae->dst_addr_lo = sc->func_stx >> 2; dmae->dst_addr_hi = 0; dmae->len = sizeof(struct host_func_stats) >> 2; dmae->comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_val = DMAE_COMP_VAL; *stats_comp = 0; } DBEXIT(BXE_VERBOSE_STATS); } /* * Returns: * None. */ static void bxe_stats_stop(struct bxe_softc *sc) { int update; DBENTER(BXE_VERBOSE_STATS); update = 0; /* Wait for any pending completions. */ bxe_stats_comp(sc); if (sc->port.pmf == 1) update = (bxe_stats_hw_update(sc) == 0); update |= (bxe_stats_storm_update(sc) == 0); if (update) { bxe_stats_net_update(sc); if (sc->port.pmf == 1) bxe_stats_port_stop(sc); bxe_stats_hw_post(sc); bxe_stats_comp(sc); } DBEXIT(BXE_VERBOSE_STATS); } /* * A dummy function to fill in the statistics state transition table. * * Returns: * None. */ static void bxe_stats_do_nothing(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_STATS); DBEXIT(BXE_VERBOSE_STATS); } static const struct { void (*action)(struct bxe_softc *sc); enum bxe_stats_state next_state; } bxe_stats_stm[STATS_STATE_MAX][STATS_EVENT_MAX] = { /* State Event */ { /* DISABLED PMF */ {bxe_stats_pmf_update, STATS_STATE_DISABLED}, /* LINK_UP */ {bxe_stats_start, STATS_STATE_ENABLED}, /* UPDATE */ {bxe_stats_do_nothing, STATS_STATE_DISABLED}, /* STOP */ {bxe_stats_do_nothing, STATS_STATE_DISABLED} }, { /* ENABLED PMF */ {bxe_stats_pmf_start, STATS_STATE_ENABLED}, /* LINK_UP */ {bxe_stats_restart, STATS_STATE_ENABLED}, /* UPDATE */ {bxe_stats_update, STATS_STATE_ENABLED}, /* STOP */ {bxe_stats_stop, STATS_STATE_DISABLED} } }; /* * Move to the next state of the statistics state machine. * * Returns: * None. */ static void bxe_stats_handle(struct bxe_softc *sc, enum bxe_stats_event event) { enum bxe_stats_state state; DBENTER(BXE_EXTREME_STATS); state = sc->stats_state; #ifdef BXE_DEBUG if (event != STATS_EVENT_UPDATE) DBPRINT(sc, BXE_VERBOSE_STATS, "%s(): Current state = %d, event = %d.\n", __FUNCTION__, state, event); #endif bxe_stats_stm[state][event].action(sc); sc->stats_state = bxe_stats_stm[state][event].next_state; #ifdef BXE_DEBUG if (event != STATS_EVENT_UPDATE) DBPRINT(sc, BXE_VERBOSE_STATS, "%s(): New state = %d.\n", __FUNCTION__, sc->stats_state); #endif DBEXIT(BXE_EXTREME_STATS); } /* * bxe_chktso_window() * Checks to ensure the 13 bd sliding window is >= MSS for TSO. * Check that (13 total bds - 3bds) = 10 bd window >= MSS. * The window: 3 bds are = 1 (for headers BD) + 2 (for PBD and last BD) * The headers comes in a seperate bd in FreeBSD. So 13-3=10. * * Returns: * 0 if OK to send, 1 if packet needs further defragmentation. */ static int bxe_chktso_window(struct bxe_softc* sc, int nsegs, bus_dma_segment_t *segs, struct mbuf *m0) { uint32_t num_wnds, wnd_size, wnd_sum; int32_t frag_idx, wnd_idx; unsigned short lso_mss; int defrag; defrag = 0; wnd_sum = 0; wnd_size = 10; num_wnds = nsegs - wnd_size; lso_mss = htole16(m0->m_pkthdr.tso_segsz); /* * Total Header lengths Eth+IP+TCP in 1st FreeBSD mbuf so * calculate the first window sum of data skip the first * assuming it is the header in FreeBSD. */ for (frag_idx = 1; (frag_idx <= wnd_size); frag_idx++) wnd_sum += htole16(segs[frag_idx].ds_len); /* Chk the first 10 bd window size */ if (wnd_sum < lso_mss) return (defrag = 1); /* Run through the windows */ for (wnd_idx = 0; wnd_idx < num_wnds; wnd_idx++, frag_idx++) { /* Subtract the 1st mbuf->m_len of the last wndw(-header). */ wnd_sum -= htole16(segs[wnd_idx+1].ds_len); /* Add the next mbuf len to the len of our new window. */ wnd_sum += htole16(segs[frag_idx].ds_len); if (wnd_sum < lso_mss) { defrag = 1; break; } } return (defrag); } /* * Encapsultes an mbuf cluster into the tx_bd chain structure and * makes the memory visible to the controller. * * If an mbuf is submitted to this routine and cannot be given to the * controller (e.g. it has too many fragments) then the function may free * the mbuf and return to the caller. * * Returns: * 0 = Success, !0 = Failure * Note the side effect that an mbuf may be freed if it causes a problem. */ static int bxe_tx_encap(struct bxe_fastpath *fp, struct mbuf **m_head) { bus_dma_segment_t segs[32]; bus_dmamap_t map; struct mbuf *m0; struct eth_tx_parse_bd *tx_parse_bd; struct eth_tx_bd *tx_data_bd; struct eth_tx_bd *tx_total_pkt_size_bd; struct eth_tx_start_bd *tx_start_bd; uint16_t etype, sw_tx_bd_prod, sw_pkt_prod, total_pkt_size; // uint16_t bd_index, pkt_index; uint8_t mac_type; int i, defragged, e_hlen, error, nsegs, rc, nbds, vlan_off, ovlan; struct bxe_softc *sc; sc = fp->sc; DBENTER(BXE_VERBOSE_SEND); DBRUN(M_ASSERTPKTHDR(*m_head)); m0 = *m_head; rc = defragged = nbds = ovlan = vlan_off = total_pkt_size = 0; tx_start_bd = NULL; tx_data_bd = NULL; tx_parse_bd = NULL; tx_total_pkt_size_bd = NULL; /* Get the H/W pointer (0 to 65535) for packets and BD's. */ sw_pkt_prod = fp->tx_pkt_prod; sw_tx_bd_prod = fp->tx_bd_prod; /* Create the S/W index (0 to MAX_TX_BD) for packets and BD's. */ // pkt_index = TX_BD(sw_pkt_prod); // bd_index = TX_BD(sw_tx_bd_prod); mac_type = UNICAST_ADDRESS; /* Map the mbuf into the next open DMAable memory. */ map = fp->tx_mbuf_map[TX_BD(sw_pkt_prod)]; error = bus_dmamap_load_mbuf_sg(fp->tx_mbuf_tag, map, m0, segs, &nsegs, BUS_DMA_NOWAIT); /* Handle any mapping errors. */ if(__predict_false(error != 0)){ fp->tx_dma_mapping_failure++; if (error == ENOMEM) { /* Resource issue, try again later. */ rc = ENOMEM; } else if (error == EFBIG) { /* Possibly recoverable with defragmentation. */ fp->mbuf_defrag_attempts++; m0 = m_defrag(*m_head, M_DONTWAIT); if (m0 == NULL) { fp->mbuf_defrag_failures++; rc = ENOBUFS; } else { /* Defrag successful, try mapping again.*/ *m_head = m0; error = bus_dmamap_load_mbuf_sg( fp->tx_mbuf_tag, map, m0, segs, &nsegs, BUS_DMA_NOWAIT); if (error) { fp->tx_dma_mapping_failure++; rc = error; } } } else { /* Unknown, unrecoverable mapping error. */ DBPRINT(sc, BXE_WARN_SEND, "%s(): Unknown TX mapping error! " "rc = %d.\n", __FUNCTION__, error); DBRUN(bxe_dump_mbuf(sc, m0)); rc = error; } goto bxe_tx_encap_continue; } /* Make sure there's enough room in the send queue. */ if (__predict_false((nsegs + 2) > (USABLE_TX_BD - fp->tx_bd_used))) { /* Recoverable, try again later. */ fp->tx_hw_queue_full++; bus_dmamap_unload(fp->tx_mbuf_tag, map); rc = ENOMEM; goto bxe_tx_encap_continue; } /* Capture the current H/W TX chain high watermark. */ if (__predict_false(fp->tx_hw_max_queue_depth < fp->tx_bd_used)) fp->tx_hw_max_queue_depth = fp->tx_bd_used; /* Now make sure it fits in the packet window. */ if (__predict_false(nsegs > 12)) { /* * The mbuf may be to big for the controller * to handle. If the frame is a TSO frame * we'll need to do an additional check. */ if(m0->m_pkthdr.csum_flags & CSUM_TSO){ if (bxe_chktso_window(sc,nsegs,segs,m0) == 0) /* OK to send. */ goto bxe_tx_encap_continue; else fp->tx_window_violation_tso++; } else fp->tx_window_violation_std++; /* No sense trying to defrag again, we'll drop the frame. */ if (defragged > 0) rc = ENODEV; } bxe_tx_encap_continue: /* Check for errors */ if (rc){ if(rc == ENOMEM){ /* Recoverable try again later */ }else{ fp->tx_soft_errors++; fp->tx_mbuf_alloc--; m_freem(*m_head); *m_head = NULL; } goto bxe_tx_encap_exit; } /* Save the mbuf and mapping. */ fp->tx_mbuf_ptr[TX_BD(sw_pkt_prod)] = m0; fp->tx_mbuf_map[TX_BD(sw_pkt_prod)] = map; /* Set flag according to packet type (UNICAST_ADDRESS is default). */ if (m0->m_flags & M_BCAST) mac_type = BROADCAST_ADDRESS; else if (m0->m_flags & M_MCAST) mac_type = MULTICAST_ADDRESS; /* Prepare the first transmit (Start) BD for the mbuf. */ tx_start_bd = &fp->tx_chain[TX_BD(sw_tx_bd_prod)].start_bd; tx_start_bd->addr_lo = htole32(U64_LO(segs[0].ds_addr)); tx_start_bd->addr_hi = htole32(U64_HI(segs[0].ds_addr)); tx_start_bd->nbytes = htole16(segs[0].ds_len); total_pkt_size += tx_start_bd->nbytes; tx_start_bd->bd_flags.as_bitfield = ETH_TX_BD_FLAGS_START_BD; tx_start_bd->general_data = (mac_type << ETH_TX_START_BD_ETH_ADDR_TYPE_SHIFT); tx_start_bd->general_data |= (1 << ETH_TX_START_BD_HDR_NBDS_SHIFT); /* All frames have at least Start BD + Parsing BD. */ nbds = nsegs + 1; tx_start_bd->nbd = htole16(nbds); if (m0->m_flags & M_VLANTAG) { tx_start_bd->bd_flags.as_bitfield |= ETH_TX_BD_FLAGS_VLAN_TAG; tx_start_bd->vlan = htole16(m0->m_pkthdr.ether_vtag); } else /* * In cases where the VLAN tag is not used the firmware * expects to see a packet counter in the VLAN tag field * Failure to do so will cause an assertion which will * stop the controller. */ tx_start_bd->vlan = htole16(fp->tx_pkt_prod); /* * Add a parsing BD from the chain. The parsing BD is always added, * however, it is only used for TSO & chksum. */ sw_tx_bd_prod = NEXT_TX_BD(sw_tx_bd_prod); tx_parse_bd = (struct eth_tx_parse_bd *) &fp->tx_chain[TX_BD(sw_tx_bd_prod)].parse_bd; memset(tx_parse_bd, 0, sizeof(struct eth_tx_parse_bd)); /* Gather all info about the packet and add to tx_parse_bd */ if (m0->m_pkthdr.csum_flags) { struct ether_vlan_header *eh; struct ip *ip = NULL; struct tcphdr *th = NULL; uint16_t flags = 0; struct udphdr *uh = NULL; /* Map Ethernet header to find type & header length. */ eh = mtod(m0, struct ether_vlan_header *); /* Handle VLAN encapsulation if present. */ if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { etype = ntohs(eh->evl_proto); e_hlen = ETHER_HDR_LEN + vlan_off; } else { etype = ntohs(eh->evl_encap_proto); e_hlen = ETHER_HDR_LEN; } /* Set the Ethernet header length in 16 bit words. */ tx_parse_bd->global_data = (e_hlen + ovlan) >> 1; tx_parse_bd->global_data |= ((m0->m_flags & M_VLANTAG) << ETH_TX_PARSE_BD_LLC_SNAP_EN_SHIFT); switch (etype) { case ETHERTYPE_IP: /* If mbuf len < 20bytes, IP header is in next mbuf. */ if (m0->m_len < sizeof(struct ip)) ip = (struct ip *) m0->m_next->m_data; else ip = (struct ip *) (m0->m_data + e_hlen); /* Calculate IP header length (16 bit words). */ tx_parse_bd->ip_hlen = (ip->ip_hl << 1); /* Calculate enet + IP header length (16 bit words). */ tx_parse_bd->total_hlen = tx_parse_bd->ip_hlen + (e_hlen >> 1); if (m0->m_pkthdr.csum_flags & CSUM_IP) { fp->tx_offload_frames_csum_ip++; flags |= ETH_TX_BD_FLAGS_IP_CSUM; } /* Handle any checksums requested by the stack. */ if ((m0->m_pkthdr.csum_flags & CSUM_TCP)|| (m0->m_pkthdr.csum_flags & CSUM_TSO)){ /* Get the TCP header. */ th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2)); /* Add the TCP checksum offload flag. */ flags |= ETH_TX_BD_FLAGS_L4_CSUM; fp->tx_offload_frames_csum_tcp++; /* Update the enet + IP + TCP header length. */ tx_parse_bd->total_hlen += (uint16_t)(th->th_off << 1); /* Get the pseudo header checksum. */ tx_parse_bd->tcp_pseudo_csum = ntohs(th->th_sum); } else if (m0->m_pkthdr.csum_flags & CSUM_UDP) { /* * The hardware doesn't actually support UDP * checksum offload but we can fake it by * doing TCP checksum offload and factoring * out the extra bytes that are different * between the TCP header and the UDP header. * * Calculation will begin 10 bytes before the * actual start of the UDP header. To work * around this we need to calculate the * checksum of the 10 bytes before the UDP * header and factor that out of the UDP * pseudo header checksum before asking the * H/W to calculate the full UDP checksum. */ uint16_t tmp_csum; uint32_t *tmp_uh; /* This value is 10. */ uint8_t fix = (uint8_t) (offsetof(struct tcphdr, th_sum) - (int) offsetof(struct udphdr, uh_sum)); /* * Add the TCP checksum offload flag for * UDP frames too.* */ flags |= ETH_TX_BD_FLAGS_L4_CSUM; fp->tx_offload_frames_csum_udp++; tx_parse_bd->global_data |= ETH_TX_PARSE_BD_UDP_CS_FLG; /* Get a pointer to the UDP header. */ uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2)); /* Set pointer 10 bytes before UDP header. */ tmp_uh = (uint32_t *)((uint8_t *)uh - fix); /* * Calculate a pseudo header checksum over * the 10 bytes before the UDP header. */ tmp_csum = in_pseudo(ntohl(*tmp_uh), ntohl(*(tmp_uh + 1)), ntohl((*(tmp_uh + 2)) & 0x0000FFFF)); /* Update the enet + IP + UDP header length. */ tx_parse_bd->total_hlen += (sizeof(struct udphdr) >> 1); tx_parse_bd->tcp_pseudo_csum = ~in_addword(uh->uh_sum, ~tmp_csum); } /* Update the offload flags. */ tx_start_bd->bd_flags.as_bitfield |= flags; break; case ETHERTYPE_IPV6: fp->tx_unsupported_tso_request_ipv6++; /* ToDo: Add IPv6 support. */ break; default: fp->tx_unsupported_tso_request_not_tcp++; /* ToDo - How to handle this error? */ } /* Setup the Parsing BD with TSO specific info */ if (m0->m_pkthdr.csum_flags & CSUM_TSO) { uint16_t hdr_len = tx_parse_bd->total_hlen << 1; tx_start_bd->bd_flags.as_bitfield |= ETH_TX_BD_FLAGS_SW_LSO; fp->tx_offload_frames_tso++; /* ToDo: Does this really help? */ if (__predict_false(tx_start_bd->nbytes > hdr_len)) { fp->tx_header_splits++; /* * Split the first BD into 2 BDs to make the * firmwares job easy... */ tx_start_bd->nbd++; DBPRINT(sc, BXE_EXTREME_SEND, "%s(): TSO split headr size is %d (%x:%x) nbds %d\n", __FUNCTION__, tx_start_bd->nbytes, tx_start_bd->addr_hi, tx_start_bd->addr_lo, nbds); sw_tx_bd_prod = NEXT_TX_BD(sw_tx_bd_prod); /* New transmit BD (after the tx_parse_bd). */ tx_data_bd = &fp->tx_chain[TX_BD(sw_tx_bd_prod)].reg_bd; tx_data_bd->addr_hi = htole32(U64_HI(segs[0].ds_addr + hdr_len)); tx_data_bd->addr_lo = htole32(U64_LO(segs[0].ds_addr + hdr_len)); tx_data_bd->nbytes = htole16(segs[0].ds_len) - hdr_len; if (tx_total_pkt_size_bd == NULL) tx_total_pkt_size_bd = tx_data_bd; } /* * The controller needs the following info for TSO: * MSS, tcp_send_seq, ip_id, and tcp_pseudo_csum. */ tx_parse_bd->lso_mss = htole16(m0->m_pkthdr.tso_segsz); tx_parse_bd->tcp_send_seq = ntohl(th->th_seq); tx_parse_bd->tcp_flags = th->th_flags; tx_parse_bd->ip_id = ntohs(ip->ip_id); tx_parse_bd->tcp_pseudo_csum = ntohs(in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htons(IPPROTO_TCP))); tx_parse_bd->global_data |= ETH_TX_PARSE_BD_PSEUDO_CS_WITHOUT_LEN; } } /* Prepare remaining BDs. Start_tx_bd contains first seg (frag). */ for (i = 1; i < nsegs ; i++) { sw_tx_bd_prod = NEXT_TX_BD(sw_tx_bd_prod); tx_data_bd = &fp->tx_chain[TX_BD(sw_tx_bd_prod)].reg_bd; tx_data_bd->addr_lo = htole32(U64_LO(segs[i].ds_addr)); tx_data_bd->addr_hi = htole32(U64_HI(segs[i].ds_addr)); tx_data_bd->nbytes = htole16(segs[i].ds_len); if (tx_total_pkt_size_bd == NULL) tx_total_pkt_size_bd = tx_data_bd; total_pkt_size += tx_data_bd->nbytes; } if(tx_total_pkt_size_bd != NULL) tx_total_pkt_size_bd->total_pkt_bytes = total_pkt_size; /* Update TX BD producer index value for next TX */ sw_tx_bd_prod = NEXT_TX_BD(sw_tx_bd_prod); /* Update the used TX BD counter. */ fp->tx_bd_used += nbds; /* * If the chain of tx_bd's describing this frame * is adjacent to or spans an eth_tx_next_bd element * then we need to increment the nbds value. */ if(TX_IDX(sw_tx_bd_prod) < nbds) nbds++; /* Don't allow reordering of writes for nbd and packets. */ mb(); fp->tx_db.data.prod += nbds; /* Producer points to the next free tx_bd at this point. */ fp->tx_pkt_prod++; fp->tx_bd_prod = sw_tx_bd_prod; DOORBELL(sc, fp->index, fp->tx_db.raw); fp->tx_pkts++; /* Prevent speculative reads from getting ahead of the status block. */ bus_space_barrier(sc->bxe_btag, sc->bxe_bhandle, 0, 0, BUS_SPACE_BARRIER_READ); /* Prevent speculative reads from getting ahead of the doorbell. */ bus_space_barrier(sc->bxe_db_btag, sc->bxe_db_bhandle, 0, 0, BUS_SPACE_BARRIER_READ); bxe_tx_encap_exit: DBEXIT(BXE_VERBOSE_SEND); return (rc); } /* * Legacy (non-RSS) dispatch routine. * * Returns: * Nothing. */ static void bxe_tx_start(struct ifnet *ifp) { struct bxe_softc *sc; struct bxe_fastpath *fp; sc = ifp->if_softc; DBENTER(BXE_EXTREME_SEND); /* Exit if the transmit queue is full or link down. */ if (((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) || !sc->link_vars.link_up) { DBPRINT(sc, BXE_WARN, "%s(): No link or TX queue full, ignoring " "transmit request.\n", __FUNCTION__); goto bxe_tx_start_exit; } /* Set the TX queue for the frame. */ fp = &sc->fp[0]; BXE_FP_LOCK(fp); bxe_tx_start_locked(ifp, fp); BXE_FP_UNLOCK(fp); bxe_tx_start_exit: DBEXIT(BXE_EXTREME_SEND); } /* * Legacy (non-RSS) transmit routine. * * Returns: * Nothing. */ static void bxe_tx_start_locked(struct ifnet *ifp, struct bxe_fastpath *fp) { struct bxe_softc *sc; struct mbuf *m = NULL; int tx_count = 0; sc = fp->sc; DBENTER(BXE_EXTREME_SEND); BXE_FP_LOCK_ASSERT(fp); /* Keep adding entries while there are frames to send. */ while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { /* Check for any frames to send. */ IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (__predict_false(m == NULL)) break; /* The transmit mbuf now belongs to us, keep track of it. */ fp->tx_mbuf_alloc++; /* * Pack the data into the transmit ring. If we * don't have room, place the mbuf back at the * head of the TX queue, set the OACTIVE flag, * and wait for the NIC to drain the chain. */ if (__predict_false(bxe_tx_encap(fp, &m))) { fp->tx_encap_failures++; /* Very Bad Frames(tm) may have been dropped. */ if (m != NULL) { /* * Mark the TX queue as full and return * the frame. */ ifp->if_drv_flags |= IFF_DRV_OACTIVE; IFQ_DRV_PREPEND(&ifp->if_snd, m); fp->tx_mbuf_alloc--; fp->tx_queue_xoff++; } else { } /* Stop looking for more work. */ break; } /* The transmit frame was enqueued successfully. */ tx_count++; /* Send a copy of the frame to any BPF listeners. */ BPF_MTAP(ifp, m); } /* No TX packets were dequeued. */ if (tx_count > 0) /* Reset the TX watchdog timeout timer. */ fp->watchdog_timer = BXE_TX_TIMEOUT; DBEXIT(BXE_EXTREME_SEND); } #if __FreeBSD_version >= 800000 /* * Multiqueue (RSS) dispatch routine. * * Returns: * 0 if transmit succeeds, !0 otherwise. */ static int bxe_tx_mq_start(struct ifnet *ifp, struct mbuf *m) { struct bxe_softc *sc; struct bxe_fastpath *fp; int fp_index, rc; sc = ifp->if_softc; DBENTER(BXE_EXTREME_SEND); fp_index = 0; /* If using flow ID, assign the TX queue based on the flow ID. */ if ((m->m_flags & M_FLOWID) != 0) fp_index = m->m_pkthdr.flowid % sc->num_queues; /* Select the fastpath TX queue for the frame. */ fp = &sc->fp[fp_index]; /* Skip H/W enqueue if transmit queue is full or link down. */ if (((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) || !sc->link_vars.link_up) { /* Stash the mbuf if we can. */ rc = drbr_enqueue(ifp, fp->br, m); goto bxe_tx_mq_start_exit; } BXE_FP_LOCK(fp); rc = bxe_tx_mq_start_locked(ifp, fp, m); BXE_FP_UNLOCK(fp); bxe_tx_mq_start_exit: DBEXIT(BXE_EXTREME_SEND); return (rc); } /* * Multiqueue (TSS) transmit routine. This routine is responsible * for adding a frame to the hardware's transmit queue. * * Returns: * 0 if transmit succeeds, !0 otherwise. */ static int bxe_tx_mq_start_locked(struct ifnet *ifp, struct bxe_fastpath *fp, struct mbuf *m) { struct bxe_softc *sc; struct mbuf *next; int depth, rc, tx_count; sc = fp->sc; DBENTER(BXE_EXTREME_SEND); rc = tx_count = 0; /* Fetch the depth of the driver queue. */ depth = drbr_inuse(ifp, fp->br); if (depth > fp->tx_max_drbr_queue_depth) fp->tx_max_drbr_queue_depth = depth; BXE_FP_LOCK_ASSERT(fp); if (m == NULL) { /* No new work, check for pending frames. */ next = drbr_dequeue(ifp, fp->br); } else if (drbr_needs_enqueue(ifp, fp->br)) { /* Both new and pending work, maintain packet order. */ rc = drbr_enqueue(ifp, fp->br, m); if (rc != 0) { fp->tx_soft_errors++; goto bxe_tx_mq_start_locked_exit; } next = drbr_dequeue(ifp, fp->br); } else /* New work only, nothing pending. */ next = m; /* Keep adding entries while there are frames to send. */ while (next != NULL) { /* The transmit mbuf now belongs to us, keep track of it. */ fp->tx_mbuf_alloc++; /* * Pack the data into the transmit ring. If we * don't have room, place the mbuf back at the * head of the TX queue, set the OACTIVE flag, * and wait for the NIC to drain the chain. */ rc = bxe_tx_encap(fp, &next); if (__predict_false(rc != 0)) { fp->tx_encap_failures++; /* Very Bad Frames(tm) may have been dropped. */ if (next != NULL) { /* * Mark the TX queue as full and save * the frame. */ ifp->if_drv_flags |= IFF_DRV_OACTIVE; fp->tx_frame_deferred++; /* This may reorder frame. */ rc = drbr_enqueue(ifp, fp->br, next); fp->tx_mbuf_alloc--; } /* Stop looking for more work. */ break; } /* The transmit frame was enqueued successfully. */ tx_count++; /* Update stats */ ifp->if_obytes += next->m_pkthdr.len; if (next->m_flags & M_MCAST) ifp->if_omcasts++; /* Send a copy of the frame to any BPF listeners. */ BPF_MTAP(ifp, next); /* Handle any completions if we're running low. */ if (fp->tx_bd_used >= BXE_TX_CLEANUP_THRESHOLD) bxe_txeof(fp); /* Close TX since there's so little room left. */ if (fp->tx_bd_used >= BXE_TX_CLEANUP_THRESHOLD) { ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; break; } next = drbr_dequeue(ifp, fp->br); } /* No TX packets were dequeued. */ if (tx_count > 0) /* Reset the TX watchdog timeout timer. */ fp->watchdog_timer = BXE_TX_TIMEOUT; bxe_tx_mq_start_locked_exit: DBEXIT(BXE_EXTREME_SEND); return (rc); } static void bxe_mq_flush(struct ifnet *ifp) { struct bxe_softc *sc; struct bxe_fastpath *fp; struct mbuf *m; int i; sc = ifp->if_softc; DBENTER(BXE_VERBOSE_UNLOAD); for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; if (fp->br != NULL) { DBPRINT(sc, BXE_VERBOSE_UNLOAD, "%s(): Clearing fp[%02d]...\n", __FUNCTION__, fp->index); BXE_FP_LOCK(fp); while ((m = buf_ring_dequeue_sc(fp->br)) != NULL) m_freem(m); BXE_FP_UNLOCK(fp); } } if_qflush(ifp); DBEXIT(BXE_VERBOSE_UNLOAD); } #endif /* FreeBSD_version >= 800000 */ /* * Handles any IOCTL calls from the operating system. * * Returns: * 0 for success, positive value for failure. */ static int bxe_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct bxe_softc *sc; struct ifreq *ifr; int error, mask, reinit; sc = ifp->if_softc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_MISC); ifr = (struct ifreq *)data; error = 0; reinit = 0; switch (command) { case SIOCSIFMTU: /* Set the MTU. */ DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Received SIOCSIFMTU\n", __FUNCTION__); /* Check that the MTU setting is supported. */ if ((ifr->ifr_mtu < BXE_MIN_MTU) || (ifr->ifr_mtu > BXE_JUMBO_MTU)) { error = EINVAL; break; } BXE_CORE_LOCK(sc); ifp->if_mtu = ifr->ifr_mtu; BXE_CORE_UNLOCK(sc); reinit = 1; break; case SIOCSIFFLAGS: /* Toggle the interface state up or down. */ DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Received SIOCSIFFLAGS\n", __FUNCTION__); BXE_CORE_LOCK(sc); /* Check if the interface is up. */ if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { /* Set promiscuous/multicast flags. */ bxe_set_rx_mode(sc); } else { /* Start the HW */ bxe_init_locked(sc, LOAD_NORMAL); } } else { /* Bring down the interface. */ if (ifp->if_drv_flags & IFF_DRV_RUNNING) bxe_stop_locked(sc, UNLOAD_NORMAL); } BXE_CORE_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: /* Add/Delete multicast addresses. */ DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Received SIOCADDMULTI/SIOCDELMULTI\n", __FUNCTION__); BXE_CORE_LOCK(sc); /* Check if the interface is up. */ if (ifp->if_drv_flags & IFF_DRV_RUNNING) /* Set receive mode flags. */ bxe_set_rx_mode(sc); BXE_CORE_UNLOCK(sc); break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: /* Set/Get Interface media */ DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Received SIOCSIFMEDIA/SIOCGIFMEDIA\n", __FUNCTION__); error = ifmedia_ioctl(ifp, ifr, &sc->bxe_ifmedia, command); break; case SIOCSIFCAP: /* Set interface capability */ /* Find out which capabilities have changed. */ mask = ifr->ifr_reqcap ^ ifp->if_capenable; DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Received SIOCSIFCAP (mask = 0x%08X)\n", __FUNCTION__, (uint32_t)mask); BXE_CORE_LOCK(sc); /* Toggle the LRO capabilites enable flag. */ if (mask & IFCAP_LRO) { ifp->if_capenable ^= IFCAP_LRO; sc->bxe_flags ^= BXE_TPA_ENABLE_FLAG; DBPRINT(sc, BXE_INFO_MISC, "%s(): Toggling LRO (bxe_flags = " "0x%08X).\n", __FUNCTION__, sc->bxe_flags); /* LRO requires different buffer setup. */ reinit = 1; } /* Toggle the TX checksum capabilites enable flag. */ if (mask & IFCAP_TXCSUM) { DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Toggling IFCAP_TXCSUM.\n", __FUNCTION__); ifp->if_capenable ^= IFCAP_TXCSUM; if (IFCAP_TXCSUM & ifp->if_capenable) ifp->if_hwassist = BXE_IF_HWASSIST; else ifp->if_hwassist = 0; } /* Toggle the RX checksum capabilities enable flag. */ if (mask & IFCAP_RXCSUM) { DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Toggling IFCAP_RXCSUM.\n", __FUNCTION__); ifp->if_capenable ^= IFCAP_RXCSUM; if (IFCAP_RXCSUM & ifp->if_capenable) ifp->if_hwassist = BXE_IF_HWASSIST; else ifp->if_hwassist = 0; } /* Toggle VLAN_MTU capabilities enable flag. */ if (mask & IFCAP_VLAN_MTU) { /* ToDo: Is this really true? */ BXE_PRINTF("%s(%d): Changing VLAN_MTU not supported.\n", __FILE__, __LINE__); error = EINVAL; } /* Toggle VLANHWTAG capabilities enabled flag. */ if (mask & IFCAP_VLAN_HWTAGGING) { /* ToDo: Is this really true? */ BXE_PRINTF( "%s(%d): Changing VLAN_HWTAGGING not supported!\n", __FILE__, __LINE__); error = EINVAL; } /* Toggle TSO4 capabilities enabled flag. */ if (mask & IFCAP_TSO4) { DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Toggling IFCAP_TSO4.\n", __FUNCTION__); ifp->if_capenable ^= IFCAP_TSO4; } /* Toggle TSO6 capabilities enabled flag. */ if (mask & IFCAP_TSO6) { /* ToDo: Add TSO6 support. */ BXE_PRINTF( "%s(%d): Changing TSO6 not supported!\n", __FILE__, __LINE__); } BXE_CORE_UNLOCK(sc); /* * ToDo: Look into supporting: * VLAN_HWFILTER * VLAN_HWCSUM * VLAN_HWTSO * POLLING * WOL[_UCAST|_MCAST|_MAGIC] * */ break; default: /* We don't know how to handle the IOCTL, pass it on. */ error = ether_ioctl(ifp, command, data); break; } /* Restart the controller with the new capabilities. */ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && (reinit != 0)) { BXE_CORE_LOCK(sc); bxe_stop_locked(sc, UNLOAD_NORMAL); bxe_init_locked(sc, LOAD_NORMAL); BXE_CORE_UNLOCK(sc); } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_MISC); return (error); } /* * Gets the current value of the RX Completion Consumer index * from the fastpath status block, updates it as necessary if * it is pointing to a "Next Page" entry, and returns it to the * caller. * * Returns: * The adjusted value of *fp->rx_cons_sb. */ static __inline uint16_t bxe_rx_cq_cons(struct bxe_fastpath *fp) { volatile uint16_t rx_cq_cons_sb = 0; rmb(); rx_cq_cons_sb = (volatile uint16_t) le16toh(*fp->rx_cq_cons_sb); /* * It is valid for the hardware's copy of the completion * consumer index to be pointing at a "Next Page" entry in * the completion chain but the driver prefers to assume * that it is pointing at the next available CQE so we * need to adjust the value accordingly. */ if ((rx_cq_cons_sb & USABLE_RCQ_ENTRIES_PER_PAGE) == USABLE_RCQ_ENTRIES_PER_PAGE) rx_cq_cons_sb++; return (rx_cq_cons_sb); } static __inline int bxe_has_tx_work(struct bxe_fastpath *fp) { rmb(); return (((fp->tx_pkt_prod != le16toh(*fp->tx_pkt_cons_sb)) || \ (fp->tx_pkt_prod != fp->tx_pkt_cons))); } /* * Checks if there are any received frames to process on the * completion queue. * * Returns: * 0 = No received frames pending, !0 = Received frames * pending */ static __inline int bxe_has_rx_work(struct bxe_fastpath *fp) { rmb(); return (bxe_rx_cq_cons(fp) != fp->rx_cq_cons); } /* * Slowpath task entry point. * * Returns: * None */ static void bxe_task_sp(void *xsc, int pending) { struct bxe_softc *sc; uint32_t sp_status; sc = xsc; DBPRINT(sc, BXE_EXTREME_INTR, "%s(): pending = %d.\n", __FUNCTION__, pending); /* Check for the source of the interrupt. */ sp_status = bxe_update_dsb_idx(sc); /* Handle any hardware attentions. */ if (sp_status & 0x1) { bxe_attn_int(sc); sp_status &= ~0x1; } /* CSTORM event asserted (query_stats, port delete ramrod, etc.). */ if (sp_status & 0x2) { sc->stats_pending = 0; sp_status &= ~0x2; } /* Check for other weirdness. */ if (sp_status != 0) { DBPRINT(sc, BXE_WARN, "%s(): Unexpected slowpath interrupt " "(sp_status = 0x%04X)!\n", __FUNCTION__, sp_status); } /* Acknowledge the xSTORM tags and enable slowpath interrupts. */ bxe_ack_sb(sc, DEF_SB_ID, ATTENTION_ID, le16toh(sc->def_att_idx), IGU_INT_NOP, 1); bxe_ack_sb(sc, DEF_SB_ID, USTORM_ID, le16toh(sc->def_u_idx), IGU_INT_NOP, 1); bxe_ack_sb(sc, DEF_SB_ID, CSTORM_ID, le16toh(sc->def_c_idx), IGU_INT_NOP, 1); bxe_ack_sb(sc, DEF_SB_ID, XSTORM_ID, le16toh(sc->def_x_idx), IGU_INT_NOP, 1); bxe_ack_sb(sc, DEF_SB_ID, TSTORM_ID, le16toh(sc->def_t_idx), IGU_INT_ENABLE, 1); } /* * Legacy interrupt entry point. * * Verifies that the controller generated the interrupt and * then calls a separate routine to handle the various * interrupt causes: link, RX, and TX. * * Returns: * None */ static void bxe_intr_legacy(void *xsc) { struct bxe_softc *sc; struct bxe_fastpath *fp; uint32_t mask, fp_status; sc = xsc; fp = &sc->fp[0]; /* Don't handle any interrupts if we're not ready. */ if (__predict_false(sc->intr_sem != 0)) goto bxe_intr_legacy_exit; /* Bail out if the interrupt wasn't generated by our hardware. */ fp_status = bxe_ack_int(sc); if (fp_status == 0) goto bxe_intr_legacy_exit; /* Handle the fastpath interrupt. */ /* * sb_id = 0 for ustorm, 1 for cstorm. * The bits returned from ack_int() are 0-15, * bit 0=attention status block * bit 1=fast path status block * A mask of 0x2 or more = tx/rx event * A mask of 1 = slow path event */ mask = (0x2 << fp->sb_id); DBPRINT(sc, BXE_INSANE_INTR, "%s(): fp_status = 0x%08X, mask = " "0x%08X\n", __FUNCTION__, fp_status, mask); /* CSTORM event means fastpath completion. */ if (fp_status & mask) { /* This interrupt must be ours, disable further interrupts. */ bxe_ack_sb(sc, fp->sb_id, USTORM_ID, 0, IGU_INT_DISABLE, 0); #ifdef BXE_TASK taskqueue_enqueue(fp->tq, &fp->task); #else bxe_task_fp((void *)fp, 0); #endif /* Clear this event from the status flags. */ fp_status &= ~mask; } /* Handle all slow path interrupts and attentions */ if (fp_status & 0x1) { /* Acknowledge and disable further slowpath interrupts. */ bxe_ack_sb(sc, DEF_SB_ID, TSTORM_ID, 0, IGU_INT_DISABLE, 0); #ifdef BXE_TASK /* Schedule the slowpath task. */ taskqueue_enqueue(sc->tq, &sc->task); #else bxe_task_sp(xsc, 0); #endif /* Clear this event from the status flags. */ fp_status &= ~0x1; } #ifdef BXE_DEBUG if (fp_status) { DBPRINT(sc, BXE_WARN, "%s(): Unexpected fastpath status (fp_status = 0x%08X)!\n", __FUNCTION__, fp_status); } #endif DBEXIT(BXE_EXTREME_INTR); bxe_intr_legacy_exit: return; } /* * Slowpath interrupt entry point. * * Acknowledge the interrupt and schedule a slowpath task. * * Returns: * None */ static void bxe_intr_sp(void *xsc) { struct bxe_softc *sc; sc = xsc; DBPRINT(sc, BXE_INSANE_INTR, "%s(%d): Slowpath interrupt.\n", __FUNCTION__, curcpu); /* Don't handle any interrupts if we're not ready. */ if (__predict_false(sc->intr_sem != 0)) goto bxe_intr_sp_exit; /* Acknowledge and disable further slowpath interrupts. */ bxe_ack_sb(sc, DEF_SB_ID, TSTORM_ID, 0, IGU_INT_DISABLE, 0); #ifdef BXE_TASK /* Schedule the slowpath task. */ taskqueue_enqueue(sc->tq, &sc->task); #else bxe_task_sp(xsc, 0); #endif bxe_intr_sp_exit: return; } /* * Fastpath interrupt entry point. * * Acknowledge the interrupt and schedule a fastpath task. * * Returns: * None */ static void bxe_intr_fp (void *xfp) { struct bxe_fastpath *fp; struct bxe_softc *sc; fp = xfp; sc = fp->sc; DBPRINT(sc, BXE_INSANE_INTR, "%s(%d): fp[%02d].sb_id = %d interrupt.\n", __FUNCTION__, curcpu, fp->index, fp->sb_id); /* Don't handle any interrupts if we're not ready. */ if (__predict_false(sc->intr_sem != 0)) goto bxe_intr_fp_exit; /* Disable further interrupts. */ bxe_ack_sb(sc, fp->sb_id, USTORM_ID, 0, IGU_INT_DISABLE, 0); #ifdef BXE_TASK taskqueue_enqueue(fp->tq, &fp->task); #else bxe_task_fp (xfp, 0); #endif bxe_intr_fp_exit: return; } /* * Fastpath task entry point. * * Handle any pending transmit or receive events. * * Returns: * None */ static void bxe_task_fp (void *xfp, int pending) { struct bxe_fastpath *fp; struct bxe_softc *sc; fp = xfp; sc = fp->sc; DBPRINT(sc, BXE_EXTREME_INTR, "%s(%d): Fastpath task on fp[%02d]" ".sb_id = %d\n", __FUNCTION__, curcpu, fp->index, fp->sb_id); /* Update the fast path indices */ bxe_update_fpsb_idx(fp); /* Service any completed TX frames. */ if (bxe_has_tx_work(fp)) { BXE_FP_LOCK(fp); bxe_txeof(fp); BXE_FP_UNLOCK(fp); } /* Service any completed RX frames. */ rmb(); bxe_rxeof(fp); /* Acknowledge the fastpath status block indices. */ bxe_ack_sb(sc, fp->sb_id, USTORM_ID, fp->fp_u_idx, IGU_INT_NOP, 1); bxe_ack_sb(sc, fp->sb_id, CSTORM_ID, fp->fp_c_idx, IGU_INT_ENABLE, 1); } /* * Clears the fastpath (per-queue) status block. * * Returns: * None */ static void bxe_zero_sb(struct bxe_softc *sc, int sb_id) { int port; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR); port = BP_PORT(sc); /* "CSTORM" */ bxe_init_fill(sc, CSEM_REG_FAST_MEMORY + CSTORM_SB_HOST_STATUS_BLOCK_U_OFFSET(port, sb_id), 0, CSTORM_SB_STATUS_BLOCK_U_SIZE / 4); bxe_init_fill(sc, CSEM_REG_FAST_MEMORY + CSTORM_SB_HOST_STATUS_BLOCK_C_OFFSET(port, sb_id), 0, CSTORM_SB_STATUS_BLOCK_C_SIZE / 4); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR); } /* * Initialize the fastpath (per queue) status block. * * Returns: * None */ static void bxe_init_sb(struct bxe_softc *sc, struct host_status_block *sb, bus_addr_t mapping, int sb_id) { uint64_t section; int func, index, port; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR); port = BP_PORT(sc); func = BP_FUNC(sc); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR), "%s(): Initializing sb_id = %d on port %d, function %d.\n", __FUNCTION__, sb_id, port, func); /* Setup the USTORM status block. */ section = ((uint64_t)mapping) + offsetof(struct host_status_block, u_status_block); sb->u_status_block.status_block_id = sb_id; REG_WR(sc, BAR_CSTORM_INTMEM + CSTORM_SB_HOST_SB_ADDR_U_OFFSET(port, sb_id), U64_LO(section)); REG_WR(sc, BAR_CSTORM_INTMEM + ((CSTORM_SB_HOST_SB_ADDR_U_OFFSET(port, sb_id)) + 4), U64_HI(section)); REG_WR8(sc, BAR_CSTORM_INTMEM + FP_USB_FUNC_OFF + CSTORM_SB_HOST_STATUS_BLOCK_U_OFFSET(port, sb_id), func); for (index = 0; index < HC_USTORM_SB_NUM_INDICES; index++) REG_WR16(sc, BAR_CSTORM_INTMEM + CSTORM_SB_HC_DISABLE_U_OFFSET(port, sb_id, index), 0x1); /* Setup the CSTORM status block. */ section = ((uint64_t)mapping) + offsetof(struct host_status_block, c_status_block); sb->c_status_block.status_block_id = sb_id; /* Write the status block address to CSTORM. Order is important! */ REG_WR(sc, BAR_CSTORM_INTMEM + CSTORM_SB_HOST_SB_ADDR_C_OFFSET(port, sb_id), U64_LO(section)); REG_WR(sc, BAR_CSTORM_INTMEM + ((CSTORM_SB_HOST_SB_ADDR_C_OFFSET(port, sb_id)) + 4), U64_HI(section)); REG_WR8(sc, BAR_CSTORM_INTMEM + FP_CSB_FUNC_OFF + CSTORM_SB_HOST_STATUS_BLOCK_C_OFFSET(port, sb_id), func); for (index = 0; index < HC_CSTORM_SB_NUM_INDICES; index++) REG_WR16(sc, BAR_CSTORM_INTMEM + CSTORM_SB_HC_DISABLE_C_OFFSET(port, sb_id, index), 0x1); /* Enable interrupts. */ bxe_ack_sb(sc, sb_id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR); } /* * Clears the default status block. * * Returns: * None */ static void bxe_zero_def_sb(struct bxe_softc *sc) { int func; func = BP_FUNC(sc); DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR), "%s(): Clearing default status block on function %d.\n", __FUNCTION__, func); /* Fill the STORM's copy of the default status block with 0. */ bxe_init_fill(sc, TSEM_REG_FAST_MEMORY + TSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), 0, sizeof(struct tstorm_def_status_block) / 4); bxe_init_fill(sc, CSEM_REG_FAST_MEMORY + CSTORM_DEF_SB_HOST_STATUS_BLOCK_U_OFFSET(func), 0, sizeof(struct cstorm_def_status_block_u) / 4); bxe_init_fill(sc, CSEM_REG_FAST_MEMORY + CSTORM_DEF_SB_HOST_STATUS_BLOCK_C_OFFSET(func), 0, sizeof(struct cstorm_def_status_block_c) / 4); bxe_init_fill(sc, XSEM_REG_FAST_MEMORY + XSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), 0, sizeof(struct xstorm_def_status_block) / 4); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR); } /* * Initialize default status block. * * Returns: * None */ static void bxe_init_def_sb(struct bxe_softc *sc, struct host_def_status_block *def_sb, bus_addr_t mapping, int sb_id) { uint64_t section; int func, index, port, reg_offset, val; port = BP_PORT(sc); func = BP_FUNC(sc); DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR), "%s(): Initializing default status block on port %d, function %d.\n", __FUNCTION__, port, func); /* Setup the default status block (DSB). */ section = ((uint64_t)mapping) + offsetof(struct host_def_status_block, atten_status_block); def_sb->atten_status_block.status_block_id = sb_id; sc->attn_state = 0; sc->def_att_idx = 0; /* * Read routing configuration for attn signal * output of groups. Currently, only groups * 0 through 3 are wired. */ reg_offset = port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0 : MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0; for (index = 0; index < MAX_DYNAMIC_ATTN_GRPS; index++) { sc->attn_group[index].sig[0] = REG_RD(sc, reg_offset + 0x10 * index); sc->attn_group[index].sig[1] = REG_RD(sc, reg_offset + 0x10 * index + 0x4); sc->attn_group[index].sig[2] = REG_RD(sc, reg_offset + 0x10 * index + 0x8); sc->attn_group[index].sig[3] = REG_RD(sc, reg_offset + 0x10 * index + 0xc); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR), "%s(): attn_group[%d] = 0x%08X 0x%08X 0x%08x 0X%08x\n", __FUNCTION__, index, sc->attn_group[index].sig[0], sc->attn_group[index].sig[1], sc->attn_group[index].sig[2], sc->attn_group[index].sig[3]); } reg_offset = port ? HC_REG_ATTN_MSG1_ADDR_L : HC_REG_ATTN_MSG0_ADDR_L; REG_WR(sc, reg_offset, U64_LO(section)); REG_WR(sc, reg_offset + 4, U64_HI(section)); reg_offset = port ? HC_REG_ATTN_NUM_P1 : HC_REG_ATTN_NUM_P0; val = REG_RD(sc, reg_offset); val |= sb_id; REG_WR(sc, reg_offset, val); /* USTORM */ section = ((uint64_t)mapping) + offsetof(struct host_def_status_block, u_def_status_block); def_sb->u_def_status_block.status_block_id = sb_id; sc->def_u_idx = 0; REG_WR(sc, BAR_CSTORM_INTMEM + CSTORM_DEF_SB_HOST_SB_ADDR_U_OFFSET(func), U64_LO(section)); REG_WR(sc, BAR_CSTORM_INTMEM + ((CSTORM_DEF_SB_HOST_SB_ADDR_U_OFFSET(func)) + 4), U64_HI(section)); REG_WR8(sc, BAR_CSTORM_INTMEM + DEF_USB_FUNC_OFF + CSTORM_DEF_SB_HOST_STATUS_BLOCK_U_OFFSET(func), func); for (index = 0; index < HC_USTORM_DEF_SB_NUM_INDICES; index++) REG_WR16(sc, BAR_CSTORM_INTMEM + CSTORM_DEF_SB_HC_DISABLE_U_OFFSET(func, index), 1); /* CSTORM */ section = ((uint64_t)mapping) + offsetof(struct host_def_status_block, c_def_status_block); def_sb->c_def_status_block.status_block_id = sb_id; sc->def_c_idx = 0; REG_WR(sc, BAR_CSTORM_INTMEM + CSTORM_DEF_SB_HOST_SB_ADDR_C_OFFSET(func), U64_LO(section)); REG_WR(sc, BAR_CSTORM_INTMEM + ((CSTORM_DEF_SB_HOST_SB_ADDR_C_OFFSET(func)) + 4), U64_HI(section)); REG_WR8(sc, BAR_CSTORM_INTMEM + DEF_CSB_FUNC_OFF + CSTORM_DEF_SB_HOST_STATUS_BLOCK_C_OFFSET(func), func); for (index = 0; index < HC_CSTORM_DEF_SB_NUM_INDICES; index++) REG_WR16(sc, BAR_CSTORM_INTMEM + CSTORM_DEF_SB_HC_DISABLE_C_OFFSET(func, index), 1); /* TSTORM */ section = ((uint64_t)mapping) + offsetof(struct host_def_status_block, t_def_status_block); def_sb->t_def_status_block.status_block_id = sb_id; sc->def_t_idx = 0; REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func), U64_LO(section)); REG_WR(sc, BAR_TSTORM_INTMEM + ((TSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func)) + 4), U64_HI(section)); REG_WR8(sc, BAR_TSTORM_INTMEM + DEF_TSB_FUNC_OFF + TSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), func); for (index = 0; index < HC_TSTORM_DEF_SB_NUM_INDICES; index++) REG_WR16(sc, BAR_TSTORM_INTMEM + TSTORM_DEF_SB_HC_DISABLE_OFFSET(func, index), 1); /* XSTORM */ section = ((uint64_t)mapping) + offsetof(struct host_def_status_block, x_def_status_block); def_sb->x_def_status_block.status_block_id = sb_id; sc->def_x_idx = 0; REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func), U64_LO(section)); REG_WR(sc, BAR_XSTORM_INTMEM + ((XSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func)) + 4), U64_HI(section)); REG_WR8(sc, BAR_XSTORM_INTMEM + DEF_XSB_FUNC_OFF + XSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), func); for (index = 0; index < HC_XSTORM_DEF_SB_NUM_INDICES; index++) REG_WR16(sc, BAR_XSTORM_INTMEM + XSTORM_DEF_SB_HC_DISABLE_OFFSET(func, index), 1); sc->stats_pending = 0; sc->set_mac_pending = 0; bxe_ack_sb(sc, sb_id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR); } /* * Update interrupt coalescing parameters. * * Returns: * None */ static void bxe_update_coalesce(struct bxe_softc *sc) { int i, port, sb_id; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); port = BP_PORT(sc); /* Cycle through each fastpath queue and set the coalescing values. */ for (i = 0; i < sc->num_queues; i++) { sb_id = sc->fp[i].sb_id; /* Receive interrupt coalescing is done on USTORM. */ REG_WR8(sc, BAR_CSTORM_INTMEM + CSTORM_SB_HC_TIMEOUT_U_OFFSET(port, sb_id, U_SB_ETH_RX_CQ_INDEX), sc->rx_ticks / (BXE_BTR * 4)); REG_WR16(sc, BAR_CSTORM_INTMEM + CSTORM_SB_HC_DISABLE_U_OFFSET(port, sb_id, U_SB_ETH_RX_CQ_INDEX), (sc->rx_ticks / (BXE_BTR * 4)) ? 0 : 1); /* Transmit interrupt coalescing is done on CSTORM. */ REG_WR8(sc, BAR_CSTORM_INTMEM + CSTORM_SB_HC_TIMEOUT_C_OFFSET(port, sb_id, C_SB_ETH_TX_CQ_INDEX), sc->tx_ticks / (BXE_BTR * 4)); REG_WR16(sc, BAR_CSTORM_INTMEM + CSTORM_SB_HC_DISABLE_C_OFFSET(port, sb_id, C_SB_ETH_TX_CQ_INDEX), (sc->tx_ticks / (BXE_BTR * 4)) ? 0 : 1); } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Allocate an mbuf and assign it to the TPA pool. * * Returns: * 0 = Success, !0 = Failure * * Modifies: * fp->tpa_mbuf_ptr[queue] * fp->tpa_mbuf_map[queue] * fp->tpa_mbuf_segs[queue] */ static int bxe_alloc_tpa_mbuf(struct bxe_fastpath *fp, int queue) { struct bxe_softc *sc; bus_dma_segment_t segs[1]; bus_dmamap_t map; struct mbuf *m; int nsegs, rc; sc = fp->sc; DBENTER(BXE_INSANE_TPA); rc = 0; DBRUNIF((fp->disable_tpa == TRUE), BXE_PRINTF("%s(): fp[%02d] TPA disabled!\n", __FUNCTION__, fp->index)); #ifdef BXE_DEBUG /* Simulate an mbuf allocation failure. */ if (DB_RANDOMTRUE(bxe_debug_mbuf_allocation_failure)) { sc->debug_sim_mbuf_alloc_failed++; fp->mbuf_tpa_alloc_failed++; rc = ENOMEM; goto bxe_alloc_tpa_mbuf_exit; } #endif /* Allocate the new TPA mbuf. */ m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, sc->mbuf_alloc_size); if (__predict_false(m == NULL)) { fp->mbuf_tpa_alloc_failed++; rc = ENOBUFS; goto bxe_alloc_tpa_mbuf_exit; } DBRUN(fp->tpa_mbuf_alloc++); /* Initialize the mbuf buffer length. */ m->m_pkthdr.len = m->m_len = sc->mbuf_alloc_size; #ifdef BXE_DEBUG /* Simulate an mbuf mapping failure. */ if (DB_RANDOMTRUE(bxe_debug_dma_map_addr_failure)) { sc->debug_sim_mbuf_map_failed++; fp->mbuf_tpa_mapping_failed++; m_freem(m); DBRUN(fp->tpa_mbuf_alloc--); rc = ENOMEM; goto bxe_alloc_tpa_mbuf_exit; } #endif /* Map the TPA mbuf into non-paged pool. */ rc = bus_dmamap_load_mbuf_sg(fp->rx_mbuf_tag, fp->tpa_mbuf_spare_map, m, segs, &nsegs, BUS_DMA_NOWAIT); if (__predict_false(rc != 0)) { fp->mbuf_tpa_mapping_failed++; m_free(m); DBRUN(fp->tpa_mbuf_alloc--); goto bxe_alloc_tpa_mbuf_exit; } /* All mubfs must map to a single segment. */ KASSERT(nsegs == 1, ("%s(): Too many segments (%d) returned!", __FUNCTION__, nsegs)); /* Release any existing TPA mbuf mapping. */ if (fp->tpa_mbuf_map[queue] != NULL) { bus_dmamap_sync(fp->rx_mbuf_tag, fp->tpa_mbuf_map[queue], BUS_DMASYNC_POSTREAD); bus_dmamap_unload(fp->rx_mbuf_tag, fp->tpa_mbuf_map[queue]); } /* Save the mbuf and mapping info for the TPA mbuf. */ map = fp->tpa_mbuf_map[queue]; fp->tpa_mbuf_map[queue] = fp->tpa_mbuf_spare_map; fp->tpa_mbuf_spare_map = map; bus_dmamap_sync(fp->rx_mbuf_tag, fp->tpa_mbuf_map[queue], BUS_DMASYNC_PREREAD); fp->tpa_mbuf_ptr[queue] = m; fp->tpa_mbuf_segs[queue] = segs[0]; bxe_alloc_tpa_mbuf_exit: DBEXIT(BXE_INSANE_TPA); return (rc); } /* * Allocate mbufs for a fastpath TPA pool. * * Returns: * 0 = Success, !0 = Failure. * * Modifies: * fp->tpa_state[] * fp->disable_tpa */ static int bxe_fill_tpa_pool(struct bxe_fastpath *fp) { struct bxe_softc *sc; int max_agg_queues, queue, rc; sc = fp->sc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); rc = 0; if (!TPA_ENABLED(sc)) { fp->disable_tpa = TRUE; goto bxe_fill_tpa_pool_exit; } max_agg_queues = CHIP_IS_E1(sc) ? ETH_MAX_AGGREGATION_QUEUES_E1 : ETH_MAX_AGGREGATION_QUEUES_E1H; /* Assume the fill operation worked. */ fp->disable_tpa = FALSE; /* Fill the TPA pool. */ for (queue = 0; queue < max_agg_queues; queue++) { rc = bxe_alloc_tpa_mbuf(fp, queue); if (rc != 0) { BXE_PRINTF( "%s(%d): fp[%02d] TPA disabled!\n", __FILE__, __LINE__, fp->index); fp->disable_tpa = TRUE; break; } fp->tpa_state[queue] = BXE_TPA_STATE_STOP; } bxe_fill_tpa_pool_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Free all mbufs from a fastpath TPA pool. * * Returns: * None * * Modifies: * fp->tpa_mbuf_ptr[] * fp->tpa_mbuf_map[] * fp->tpa_mbuf_alloc */ static void bxe_free_tpa_pool(struct bxe_fastpath *fp) { struct bxe_softc *sc; int i, max_agg_queues; sc = fp->sc; DBENTER(BXE_INSANE_LOAD | BXE_INSANE_UNLOAD | BXE_INSANE_TPA); if (fp->rx_mbuf_tag == NULL) goto bxe_free_tpa_pool_exit; max_agg_queues = CHIP_IS_E1H(sc) ? ETH_MAX_AGGREGATION_QUEUES_E1H : ETH_MAX_AGGREGATION_QUEUES_E1; /* Release all mbufs and and all DMA maps in the TPA pool. */ for (i = 0; i < max_agg_queues; i++) { if (fp->tpa_mbuf_map[i] != NULL) { bus_dmamap_sync(fp->rx_mbuf_tag, fp->tpa_mbuf_map[i], BUS_DMASYNC_POSTREAD); bus_dmamap_unload(fp->rx_mbuf_tag, fp->tpa_mbuf_map[i]); } if (fp->tpa_mbuf_ptr[i] != NULL) { m_freem(fp->tpa_mbuf_ptr[i]); DBRUN(fp->tpa_mbuf_alloc--); fp->tpa_mbuf_ptr[i] = NULL; } } bxe_free_tpa_pool_exit: DBEXIT(BXE_INSANE_LOAD | BXE_INSANE_UNLOAD | BXE_INSANE_TPA); } /* * Allocate an mbuf and assign it to the receive scatter gather chain. * The caller must take care to save a copy of the existing mbuf in the * SG mbuf chain. * * Returns: * 0 = Success, !0= Failure. * * Modifies: * fp->sg_chain[index] * fp->rx_sge_buf_ptr[index] * fp->rx_sge_buf_map[index] * fp->rx_sge_spare_map */ static int bxe_alloc_rx_sge_mbuf(struct bxe_fastpath *fp, uint16_t index) { struct bxe_softc *sc; struct eth_rx_sge *sge; bus_dma_segment_t segs[1]; bus_dmamap_t map; struct mbuf *m; int nsegs, rc; sc = fp->sc; DBENTER(BXE_INSANE_TPA); rc = 0; #ifdef BXE_DEBUG /* Simulate an mbuf allocation failure. */ if (DB_RANDOMTRUE(bxe_debug_mbuf_allocation_failure)) { sc->debug_sim_mbuf_alloc_failed++; fp->mbuf_sge_alloc_failed++; rc = ENOMEM; goto bxe_alloc_rx_sge_mbuf_exit; } #endif /* Allocate a new SGE mbuf. */ m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, SGE_PAGE_SIZE); if (__predict_false(m == NULL)) { fp->mbuf_sge_alloc_failed++; rc = ENOMEM; goto bxe_alloc_rx_sge_mbuf_exit; } DBRUN(fp->sge_mbuf_alloc++); /* Initialize the mbuf buffer length. */ m->m_pkthdr.len = m->m_len = SGE_PAGE_SIZE; #ifdef BXE_DEBUG /* Simulate an mbuf mapping failure. */ if (DB_RANDOMTRUE(bxe_debug_dma_map_addr_failure)) { sc->debug_sim_mbuf_map_failed++; fp->mbuf_sge_mapping_failed++; m_freem(m); DBRUN(fp->sge_mbuf_alloc--); rc = ENOMEM; goto bxe_alloc_rx_sge_mbuf_exit; } #endif /* Map the SGE mbuf into non-paged pool. */ rc = bus_dmamap_load_mbuf_sg(fp->rx_sge_buf_tag, fp->rx_sge_spare_map, m, segs, &nsegs, BUS_DMA_NOWAIT); if (__predict_false(rc != 0)) { fp->mbuf_sge_mapping_failed++; m_freem(m); DBRUN(fp->sge_mbuf_alloc--); goto bxe_alloc_rx_sge_mbuf_exit; } /* All mubfs must map to a single segment. */ KASSERT(nsegs == 1, ("%s(): Too many segments (%d) returned!", __FUNCTION__, nsegs)); /* Unload any existing SGE mbuf mapping. */ if (fp->rx_sge_buf_map[index] != NULL) { bus_dmamap_sync(fp->rx_sge_buf_tag, fp->rx_sge_buf_map[index], BUS_DMASYNC_POSTREAD); bus_dmamap_unload(fp->rx_sge_buf_tag, fp->rx_sge_buf_map[index]); } /* Add the new SGE mbuf to the SGE ring. */ map = fp->rx_sge_buf_map[index]; fp->rx_sge_buf_map[index] = fp->rx_sge_spare_map; fp->rx_sge_spare_map = map; bus_dmamap_sync(fp->rx_sge_buf_tag, fp->rx_sge_buf_map[index], BUS_DMASYNC_PREREAD); fp->rx_sge_buf_ptr[index] = m; sge = &fp->sg_chain[index]; sge->addr_hi = htole32(U64_HI(segs[0].ds_addr)); sge->addr_lo = htole32(U64_LO(segs[0].ds_addr)); bxe_alloc_rx_sge_mbuf_exit: DBEXIT(BXE_INSANE_TPA); return (rc); } /* * Allocate mbufs for a SGE chain. * * Returns: * 0 = Success, !0 = Failure. * * Modifies: * fp->disable_tpa * fp->rx_sge_prod */ static int bxe_fill_sg_chain(struct bxe_fastpath *fp) { struct bxe_softc *sc; uint16_t index; int i, rc; sc = fp->sc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); rc = 0; if (!TPA_ENABLED(sc)) { fp->disable_tpa = TRUE; goto bxe_fill_sg_chain_exit; } /* Assume the fill operation works. */ fp->disable_tpa = FALSE; /* Fill the RX SGE chain. */ index = 0; for (i = 0; i < USABLE_RX_SGE; i++) { rc = bxe_alloc_rx_sge_mbuf(fp, index); if (rc != 0) { BXE_PRINTF( "%s(%d): fp[%02d] SGE memory allocation failure!\n", __FILE__, __LINE__, fp->index); index = 0; fp->disable_tpa = TRUE; break; } index = NEXT_SGE_IDX(index); } /* Update the driver's copy of the RX SGE producer index. */ fp->rx_sge_prod = index; bxe_fill_sg_chain_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Free all elements from the receive scatter gather chain. * * Returns: * None * * Modifies: * fp->rx_sge_buf_ptr[] * fp->rx_sge_buf_map[] * fp->sge_mbuf_alloc */ static void bxe_free_sg_chain(struct bxe_fastpath *fp) { struct bxe_softc *sc; int i; sc = fp->sc; DBENTER(BXE_INSANE_TPA); if (fp->rx_sge_buf_tag == NULL) goto bxe_free_sg_chain_exit; /* Free all mbufs and unload all maps. */ for (i = 0; i < TOTAL_RX_SGE; i++) { /* Free the map and the mbuf if they're allocated. */ if (fp->rx_sge_buf_map[i] != NULL) { bus_dmamap_sync(fp->rx_sge_buf_tag, fp->rx_sge_buf_map[i], BUS_DMASYNC_POSTREAD); bus_dmamap_unload(fp->rx_sge_buf_tag, fp->rx_sge_buf_map[i]); } if (fp->rx_sge_buf_ptr[i] != NULL) { m_freem(fp->rx_sge_buf_ptr[i]); DBRUN(fp->sge_mbuf_alloc--); fp->rx_sge_buf_ptr[i] = NULL; } } bxe_free_sg_chain_exit: DBEXIT(BXE_INSANE_TPA); } /* * Allocate an mbuf, if necessary, and add it to the receive chain. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_alloc_rx_bd_mbuf(struct bxe_fastpath *fp, uint16_t index) { struct bxe_softc *sc; struct eth_rx_bd *rx_bd; bus_dma_segment_t segs[1]; bus_dmamap_t map; struct mbuf *m; int nsegs, rc; sc = fp->sc; DBENTER(BXE_INSANE_LOAD | BXE_INSANE_RESET | BXE_INSANE_RECV); rc = 0; #ifdef BXE_DEBUG /* Simulate an mbuf allocation failure. */ if (DB_RANDOMTRUE(bxe_debug_mbuf_allocation_failure)) { sc->debug_sim_mbuf_alloc_failed++; fp->mbuf_rx_bd_alloc_failed++; rc = ENOMEM; goto bxe_alloc_rx_bd_mbuf_exit; } #endif /* Allocate the new RX BD mbuf. */ m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, sc->mbuf_alloc_size); if (__predict_false(m == NULL)) { fp->mbuf_rx_bd_alloc_failed++; rc = ENOBUFS; goto bxe_alloc_rx_bd_mbuf_exit; } DBRUN(fp->rx_mbuf_alloc++); /* Initialize the mbuf buffer length. */ m->m_pkthdr.len = m->m_len = sc->mbuf_alloc_size; #ifdef BXE_DEBUG /* Simulate an mbuf mapping failure. */ if (DB_RANDOMTRUE(bxe_debug_dma_map_addr_failure)) { sc->debug_sim_mbuf_map_failed++; fp->mbuf_rx_bd_mapping_failed++; m_freem(m); DBRUN(fp->rx_mbuf_alloc--); rc = ENOMEM; goto bxe_alloc_rx_bd_mbuf_exit; } #endif /* Map the TPA mbuf into non-paged pool. */ rc = bus_dmamap_load_mbuf_sg(fp->rx_mbuf_tag, fp->rx_mbuf_spare_map, m, segs, &nsegs, BUS_DMA_NOWAIT); if (__predict_false(rc != 0)) { fp->mbuf_rx_bd_mapping_failed++; m_freem(m); DBRUN(fp->rx_mbuf_alloc--); goto bxe_alloc_rx_bd_mbuf_exit; } /* All mubfs must map to a single segment. */ KASSERT(nsegs == 1, ("%s(): Too many segments (%d) returned!", __FUNCTION__, nsegs)); /* Release any existing RX BD mbuf mapping. */ if (fp->rx_mbuf_map[index] != NULL) { bus_dmamap_sync(fp->rx_mbuf_tag, fp->rx_mbuf_map[index], BUS_DMASYNC_POSTREAD); bus_dmamap_unload(fp->rx_mbuf_tag, fp->rx_mbuf_map[index]); } /* Save the mbuf and mapping info. */ map = fp->rx_mbuf_map[index]; fp->rx_mbuf_map[index] = fp->rx_mbuf_spare_map; fp->rx_mbuf_spare_map = map; bus_dmamap_sync(fp->rx_mbuf_tag, fp->rx_mbuf_map[index], BUS_DMASYNC_PREREAD); fp->rx_mbuf_ptr[index] = m; rx_bd = &fp->rx_chain[index]; rx_bd->addr_hi = htole32(U64_HI(segs[0].ds_addr)); rx_bd->addr_lo = htole32(U64_LO(segs[0].ds_addr)); bxe_alloc_rx_bd_mbuf_exit: DBEXIT(BXE_INSANE_LOAD | BXE_INSANE_RESET | BXE_INSANE_RECV); return (rc); } /* * Allocate mbufs for a receive chain. * * Returns: * 0 = Success, !0 = Failure. * * Modifies: * fp->rx_bd_prod */ static int bxe_fill_rx_bd_chain(struct bxe_fastpath *fp) { struct bxe_softc *sc; uint16_t index; int i, rc; sc = fp->sc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); rc = index = 0; /* Allocate buffers for all the RX BDs in RX BD Chain. */ for (i = 0; i < USABLE_RX_BD; i++) { rc = bxe_alloc_rx_bd_mbuf(fp, index); if (rc != 0) { BXE_PRINTF( "%s(%d): Memory allocation failure! Cannot fill fp[%02d] RX chain.\n", __FILE__, __LINE__, fp->index); index = 0; break; } index = NEXT_RX_BD(index); } fp->rx_bd_prod = index; DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Free all buffers from the receive chain. * * Returns: * None * * Modifies: * fp->rx_mbuf_ptr[] * fp->rx_mbuf_map[] * fp->rx_mbuf_alloc */ static void bxe_free_rx_bd_chain(struct bxe_fastpath *fp) { struct bxe_softc *sc; int i; sc = fp->sc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); if (fp->rx_mbuf_tag == NULL) goto bxe_free_rx_bd_chain_exit; /* Free all mbufs and unload all maps. */ for (i = 0; i < TOTAL_RX_BD; i++) { if (fp->rx_mbuf_map[i] != NULL) { bus_dmamap_sync(fp->rx_mbuf_tag, fp->rx_mbuf_map[i], BUS_DMASYNC_POSTREAD); bus_dmamap_unload(fp->rx_mbuf_tag, fp->rx_mbuf_map[i]); } if (fp->rx_mbuf_ptr[i] != NULL) { m_freem(fp->rx_mbuf_ptr[i]); DBRUN(fp->rx_mbuf_alloc--); fp->rx_mbuf_ptr[i] = NULL; } } bxe_free_rx_bd_chain_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Setup mutexes used by the driver. * * Returns: * None. */ static void bxe_mutexes_alloc(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i; DBENTER(BXE_VERBOSE_LOAD); BXE_CORE_LOCK_INIT(sc, device_get_nameunit(sc->dev)); BXE_SP_LOCK_INIT(sc, "bxe_sp_lock"); BXE_DMAE_LOCK_INIT(sc, "bxe_dmae_lock"); BXE_PHY_LOCK_INIT(sc, "bxe_phy_lock"); BXE_FWMB_LOCK_INIT(sc, "bxe_fwmb_lock"); BXE_PRINT_LOCK_INIT(sc, "bxe_print_lock"); /* Allocate one mutex for each fastpath structure. */ for (i = 0; i < sc->num_queues; i++ ) { fp = &sc->fp[i]; /* Allocate per fastpath mutexes. */ snprintf(fp->mtx_name, sizeof(fp->mtx_name), "%s:fp[%02d]", device_get_nameunit(sc->dev), fp->index); mtx_init(&fp->mtx, fp->mtx_name, NULL, MTX_DEF); } DBEXIT(BXE_VERBOSE_LOAD); } /* * Free mutexes used by the driver. * * Returns: * None. */ static void bxe_mutexes_free(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i; DBENTER(BXE_VERBOSE_UNLOAD); for (i = 0; i < sc->num_queues; i++ ) { fp = &sc->fp[i]; /* Release per fastpath mutexes. */ if (mtx_initialized(&fp->mtx)) mtx_destroy(&fp->mtx); } BXE_PRINT_LOCK_DESTROY(sc); BXE_FWMB_LOCK_DESTROY(sc); BXE_PHY_LOCK_DESTROY(sc); BXE_DMAE_LOCK_DESTROY(sc); BXE_SP_LOCK_DESTROY(sc); BXE_CORE_LOCK_DESTROY(sc); DBEXIT(BXE_VERBOSE_UNLOAD); } /* * Free memory and clear the RX data structures. * * Returns: * Nothing. */ static void bxe_clear_rx_chains(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i; DBENTER(BXE_VERBOSE_RESET); for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; /* Free all RX buffers. */ bxe_free_rx_bd_chain(fp); bxe_free_tpa_pool(fp); bxe_free_sg_chain(fp); /* Check if any mbufs lost in the process. */ DBRUNIF((fp->tpa_mbuf_alloc), DBPRINT(sc, BXE_FATAL, "%s(): Memory leak! Lost %d mbufs from fp[%02d] TPA pool!\n", __FUNCTION__, fp->tpa_mbuf_alloc, fp->index)); DBRUNIF((fp->sge_mbuf_alloc), DBPRINT(sc, BXE_FATAL, "%s(): Memory leak! Lost %d mbufs from fp[%02d] SGE chain!\n", __FUNCTION__, fp->sge_mbuf_alloc, fp->index)); DBRUNIF((fp->rx_mbuf_alloc), DBPRINT(sc, BXE_FATAL, "%s(): Memory leak! Lost %d mbufs from fp[%02d] RX chain!\n", __FUNCTION__, fp->rx_mbuf_alloc, fp->index)); } DBEXIT(BXE_VERBOSE_RESET); } /* * Initialize the receive rings. * * Returns: * None. */ static int bxe_init_rx_chains(struct bxe_softc *sc) { struct bxe_fastpath *fp; int func, i, rc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); rc = 0; func = BP_FUNC(sc); /* Allocate memory for RX and CQ chains. */ for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Initializing fp[%02d] RX chain.\n", __FUNCTION__, i); fp->rx_bd_cons = fp->rx_bd_prod = 0; fp->rx_cq_cons = fp->rx_cq_prod = 0; /* Pointer to status block's CQ consumer index. */ fp->rx_cq_cons_sb = &fp->status_block-> u_status_block.index_values[HC_INDEX_U_ETH_RX_CQ_CONS]; /* Pointer to status block's receive consumer index. */ fp->rx_bd_cons_sb = &fp->status_block-> u_status_block.index_values[HC_INDEX_U_ETH_RX_BD_CONS]; fp->rx_cq_prod = TOTAL_RCQ_ENTRIES; fp->rx_pkts = fp->rx_tpa_pkts = fp->rx_soft_errors = 0; /* Allocate memory for the receive chain. */ rc = bxe_fill_rx_bd_chain(fp); if (rc != 0) goto bxe_init_rx_chains_exit; /* Allocate memory for TPA pool. */ rc = bxe_fill_tpa_pool(fp); if (rc != 0) goto bxe_init_rx_chains_exit; /* Allocate memory for scatter-gather chain. */ rc = bxe_fill_sg_chain(fp); if (rc != 0) goto bxe_init_rx_chains_exit; /* Prepare the receive BD and CQ buffers for DMA access. */ bus_dmamap_sync(fp->rx_dma.tag, fp->rx_dma.map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); bus_dmamap_sync(fp->rcq_dma.tag, fp->rcq_dma.map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); /* * Tell the controller that we have rx_bd's and CQE's * available. Warning! this will generate an interrupt * (to the TSTORM). This must only be done when the * controller is initialized. */ bxe_update_rx_prod(sc, fp, fp->rx_bd_prod, fp->rx_cq_prod, fp->rx_sge_prod); /* ToDo - Move to dma_alloc(). */ /* * Tell controller where the receive CQ * chains start in physical memory. */ if (i == 0) { REG_WR(sc, BAR_USTORM_INTMEM + USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(func), U64_LO(fp->rcq_dma.paddr)); REG_WR(sc, BAR_USTORM_INTMEM + USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(func) + 4, U64_HI(fp->rcq_dma.paddr)); } } bxe_init_rx_chains_exit: /* Release memory if an error occurred. */ if (rc != 0) bxe_clear_rx_chains(sc); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Free memory and clear the TX data structures. * * Returns: * Nothing. */ static void bxe_clear_tx_chains(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i, j; DBENTER(BXE_VERBOSE_RESET); for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; /* Free all mbufs and unload all maps. */ if (fp->tx_mbuf_tag) { for (j = 0; j < TOTAL_TX_BD; j++) { if (fp->tx_mbuf_ptr[j] != NULL) { bus_dmamap_sync(fp->tx_mbuf_tag, fp->tx_mbuf_map[j], BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(fp->tx_mbuf_tag, fp->tx_mbuf_map[j]); m_freem(fp->tx_mbuf_ptr[j]); fp->tx_mbuf_alloc--; fp->tx_mbuf_ptr[j] = NULL; } } } /* Check if we lost any mbufs in the process. */ DBRUNIF((fp->tx_mbuf_alloc), DBPRINT(sc, BXE_FATAL, "%s(): Memory leak! Lost %d mbufs from fp[%02d] TX chain!\n", __FUNCTION__, fp->tx_mbuf_alloc, fp->index)); } DBEXIT(BXE_VERBOSE_RESET); } /* * Initialize the transmit chain. * * Returns: * None. */ static void bxe_init_tx_chains(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i, j; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; /* Initialize transmit doorbell. */ fp->tx_db.data.header.header = DOORBELL_HDR_DB_TYPE; fp->tx_db.data.zero_fill1 = 0; fp->tx_db.data.prod = 0; /* Initialize tranmsit producer/consumer indices. */ fp->tx_pkt_prod = fp->tx_pkt_cons = 0; fp->tx_bd_prod = fp->tx_bd_cons = 0; fp->tx_bd_used = 0; /* Pointer to TX packet consumer in status block. */ fp->tx_pkt_cons_sb = &fp->status_block->c_status_block.index_values[C_SB_ETH_TX_CQ_INDEX]; /* Soft TX counters. */ fp->tx_pkts = 0; fp->tx_soft_errors = 0; fp->tx_offload_frames_csum_ip = 0; fp->tx_offload_frames_csum_tcp = 0; fp->tx_offload_frames_csum_udp = 0; fp->tx_offload_frames_tso = 0; fp->tx_header_splits = 0; fp->tx_encap_failures = 0; fp->tx_hw_queue_full = 0; fp->tx_hw_max_queue_depth = 0; fp->tx_dma_mapping_failure = 0; fp->tx_max_drbr_queue_depth = 0; fp->tx_window_violation_std = 0; fp->tx_window_violation_tso = 0; fp->tx_unsupported_tso_request_ipv6 = 0; fp->tx_unsupported_tso_request_not_tcp = 0; fp->tx_chain_lost_mbuf = 0; fp->tx_frame_deferred = 0; fp->tx_queue_xoff = 0; /* Clear all TX mbuf pointers. */ for (j = 0; j < TOTAL_TX_BD; j++) { fp->tx_mbuf_ptr[j] = NULL; } } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Initialize the slowpath ring. * * Returns: * None. */ static void bxe_init_sp_ring(struct bxe_softc *sc) { int func; func = BP_FUNC(sc); DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); bzero((char *)sc->slowpath, BXE_SLOWPATH_SZ); /* When the producer equals the consumer the chain is empty. */ sc->spq_left = MAX_SPQ_PENDING; sc->spq_prod_idx = 0; sc->dsb_sp_prod = BXE_SP_DSB_INDEX; sc->spq_prod_bd = sc->spq; sc->spq_last_bd = sc->spq_prod_bd + MAX_SP_DESC_CNT; /* Tell the controller the address of the slowpath ring. */ REG_WR(sc, XSEM_REG_FAST_MEMORY + XSTORM_SPQ_PAGE_BASE_OFFSET(func), U64_LO(sc->spq_dma.paddr)); REG_WR(sc, XSEM_REG_FAST_MEMORY + XSTORM_SPQ_PAGE_BASE_OFFSET(func) + 4, U64_HI(sc->spq_dma.paddr)); REG_WR(sc, XSEM_REG_FAST_MEMORY + XSTORM_SPQ_PROD_OFFSET(func), sc->spq_prod_idx); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Initialize STORM processor context. * * Returns: * None. */ static void bxe_init_context(struct bxe_softc *sc) { struct eth_context *context; struct bxe_fastpath *fp; uint8_t sb_id; uint8_t cl_id; int i; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); for (i = 0; i < sc->num_queues; i++) { context = BXE_SP(sc, context[i].eth); fp = &sc->fp[i]; sb_id = fp->sb_id; cl_id = fp->cl_id; /* Update the USTORM context. */ context->ustorm_st_context.common.sb_index_numbers = BXE_RX_SB_INDEX_NUM; context->ustorm_st_context.common.clientId = cl_id; context->ustorm_st_context.common.status_block_id = sb_id; /* Enable packet alignment/pad and statistics. */ context->ustorm_st_context.common.flags = USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_MC_ALIGNMENT; if (sc->stats_enable == TRUE) context->ustorm_st_context.common.flags |= USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_STATISTICS; context->ustorm_st_context.common.statistics_counter_id=cl_id; /* * Set packet alignment boundary. * (Must be >= 4 (i.e. 16 bytes).) */ context->ustorm_st_context.common.mc_alignment_log_size = 8; /* Set the size of the receive buffers. */ context->ustorm_st_context.common.bd_buff_size = sc->mbuf_alloc_size; /* Set the address of the receive chain base page. */ context->ustorm_st_context.common.bd_page_base_hi = U64_HI(fp->rx_dma.paddr); context->ustorm_st_context.common.bd_page_base_lo = U64_LO(fp->rx_dma.paddr); if (TPA_ENABLED(sc) && (fp->disable_tpa == FALSE)) { /* Enable TPA and SGE chain support. */ context->ustorm_st_context.common.flags |= USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_TPA; /* Set the size of the SGE buffer. */ context->ustorm_st_context.common.sge_buff_size = (uint16_t) (SGE_PAGE_SIZE * PAGES_PER_SGE); /* Set the address of the SGE chain base page. */ context->ustorm_st_context.common.sge_page_base_hi = U64_HI(fp->sg_dma.paddr); context->ustorm_st_context.common.sge_page_base_lo = U64_LO(fp->sg_dma.paddr); DBPRINT(sc, BXE_VERBOSE_TPA, "%s(): MTU = %d\n", __FUNCTION__, (int) sc->bxe_ifp->if_mtu); /* Describe MTU to SGE alignment. */ context->ustorm_st_context.common.max_sges_for_packet = SGE_PAGE_ALIGN(sc->bxe_ifp->if_mtu) >> SGE_PAGE_SHIFT; context->ustorm_st_context.common.max_sges_for_packet = ((context->ustorm_st_context.common. max_sges_for_packet + PAGES_PER_SGE - 1) & (~(PAGES_PER_SGE - 1))) >> PAGES_PER_SGE_SHIFT; DBPRINT(sc, BXE_VERBOSE_TPA, "%s(): max_sges_for_packet = %d\n", __FUNCTION__, context->ustorm_st_context.common.max_sges_for_packet); } /* Update USTORM context. */ context->ustorm_ag_context.cdu_usage = CDU_RSRVD_VALUE_TYPE_A(HW_CID(sc, i), CDU_REGION_NUMBER_UCM_AG, ETH_CONNECTION_TYPE); /* Update XSTORM context. */ context->xstorm_ag_context.cdu_reserved = CDU_RSRVD_VALUE_TYPE_A(HW_CID(sc, i), CDU_REGION_NUMBER_XCM_AG, ETH_CONNECTION_TYPE); /* Set the address of the transmit chain base page. */ context->xstorm_st_context.tx_bd_page_base_hi = U64_HI(fp->tx_dma.paddr); context->xstorm_st_context.tx_bd_page_base_lo = U64_LO(fp->tx_dma.paddr); /* Enable XSTORM statistics. */ context->xstorm_st_context.statistics_data = (cl_id | XSTORM_ETH_ST_CONTEXT_STATISTICS_ENABLE); /* Update CSTORM status block configuration. */ context->cstorm_st_context.sb_index_number = C_SB_ETH_TX_CQ_INDEX; context->cstorm_st_context.status_block_id = sb_id; } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Initialize indirection table. * * Returns: * None. */ static void bxe_init_ind_table(struct bxe_softc *sc) { int func, i; func = BP_FUNC(sc); DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); if (sc->multi_mode == ETH_RSS_MODE_DISABLED) return; /* Initialize the indirection table. */ for (i = 0; i < TSTORM_INDIRECTION_TABLE_SIZE; i++) REG_WR8(sc, BAR_TSTORM_INTMEM + TSTORM_INDIRECTION_TABLE_OFFSET(func) + i, sc->fp->cl_id + (i % sc->num_queues)); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Set client configuration. * * Returns: * None. */ static void bxe_set_client_config(struct bxe_softc *sc) { struct tstorm_eth_client_config tstorm_client = {0}; int i, port; port = BP_PORT(sc); DBENTER(BXE_VERBOSE_MISC); tstorm_client.mtu = sc->bxe_ifp->if_mtu; /* ETHERMTU */ tstorm_client.config_flags = (TSTORM_ETH_CLIENT_CONFIG_STATSITICS_ENABLE | TSTORM_ETH_CLIENT_CONFIG_E1HOV_REM_ENABLE); /* Unconditionally enable VLAN tag stripping. */ if (sc->rx_mode) { tstorm_client.config_flags |= TSTORM_ETH_CLIENT_CONFIG_VLAN_REM_ENABLE; DBPRINT(sc, BXE_VERBOSE, "%s(): VLAN tag stripping enabled.\n", __FUNCTION__); } /* Initialize the receive mode for each receive queue. */ for (i = 0; i < sc->num_queues; i++) { tstorm_client.statistics_counter_id = sc->fp[i].cl_id; REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_CLIENT_CONFIG_OFFSET(port, sc->fp[i].cl_id), ((uint32_t *) &tstorm_client)[0]); REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_CLIENT_CONFIG_OFFSET(port, sc->fp[i].cl_id) + 4, ((uint32_t *) &tstorm_client)[1]); } DBEXIT(BXE_VERBOSE_MISC); } /* * Set receive mode. * * Programs the MAC according to the type of unicast/broadcast/multicast * packets it should receive. * * Returns: * None. */ static void bxe_set_storm_rx_mode(struct bxe_softc *sc) { struct tstorm_eth_mac_filter_config tstorm_mac_filter = {0}; uint32_t llh_mask; int mode, mask; int func, i , port; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); mode = sc->rx_mode; mask = 1 << BP_L_ID(sc); func = BP_FUNC(sc); port = BP_PORT(sc); /* All but management unicast packets should pass to the host as well */ llh_mask = NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_BRCST | NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_MLCST | NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_VLAN | NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_NO_VLAN; /* Set the individual accept/drop flags based on the receive mode. */ switch (mode) { case BXE_RX_MODE_NONE: /* Drop everything. */ DBPRINT(sc, BXE_VERBOSE, "%s(): Setting RX_MODE_NONE for function %d.\n", __FUNCTION__, func); tstorm_mac_filter.ucast_drop_all = mask; tstorm_mac_filter.mcast_drop_all = mask; tstorm_mac_filter.bcast_drop_all = mask; break; case BXE_RX_MODE_NORMAL: /* Accept all broadcast frames. */ DBPRINT(sc, BXE_VERBOSE, "%s(): Setting RX_MODE_NORMAL for function %d.\n", __FUNCTION__, func); tstorm_mac_filter.bcast_accept_all = mask; break; case BXE_RX_MODE_ALLMULTI: /* Accept all broadcast and multicast frames. */ DBPRINT(sc, BXE_VERBOSE, "%s(): Setting RX_MODE_ALLMULTI for function %d.\n", __FUNCTION__, func); tstorm_mac_filter.mcast_accept_all = mask; tstorm_mac_filter.bcast_accept_all = mask; break; case BXE_RX_MODE_PROMISC: /* Accept all frames (promiscuous mode). */ DBPRINT(sc, BXE_VERBOSE, "%s(): Setting RX_MODE_PROMISC for function %d.\n", __FUNCTION__, func); tstorm_mac_filter.ucast_accept_all = mask; tstorm_mac_filter.mcast_accept_all = mask; tstorm_mac_filter.bcast_accept_all = mask; llh_mask |= NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_UNCST; break; default: BXE_PRINTF( "%s(%d): Tried to set unknown receive mode (0x%08X)!\n", __FILE__, __LINE__, mode); } REG_WR(sc, port ? NIG_REG_LLH1_BRB1_DRV_MASK : NIG_REG_LLH0_BRB1_DRV_MASK, llh_mask); /* Write the RX mode filter to the TSTORM. */ for (i = 0; i < sizeof(struct tstorm_eth_mac_filter_config) / 4; i++) REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_MAC_FILTER_CONFIG_OFFSET(func) + (i * 4), ((uint32_t *) &tstorm_mac_filter)[i]); if (mode != BXE_RX_MODE_NONE) bxe_set_client_config(sc); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Initialize common internal resources. (Applies to both ports and * functions.) * * Returns: * Nothing. */ static void bxe_init_internal_common(struct bxe_softc *sc) { int i; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); /* * Zero this manually as its initialization is currently not * handled through block initialization. */ for (i = 0; i < (USTORM_AGG_DATA_SIZE >> 2); i++) REG_WR(sc, BAR_USTORM_INTMEM + USTORM_AGG_DATA_OFFSET + i * 4, 0); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Initialize port specific internal resources. * * Returns: * Nothing. */ static void bxe_init_internal_port(struct bxe_softc *sc) { int port = BP_PORT(sc); port = BP_PORT(sc); DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Port %d internal initialization.\n", __FUNCTION__, port); /* * Each SDM timer tick is 4us. Configure host coalescing * basic timer resolution (BTR) to 12us (3 * 4us). */ REG_WR(sc, BAR_CSTORM_INTMEM + CSTORM_HC_BTR_U_OFFSET(port), BXE_BTR); REG_WR(sc, BAR_CSTORM_INTMEM + CSTORM_HC_BTR_C_OFFSET(port), BXE_BTR); REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_HC_BTR_OFFSET(port), BXE_BTR); REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_HC_BTR_OFFSET(port), BXE_BTR); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Initialize function specific internal resources. * * Returns: * Nothing. */ static void bxe_init_internal_func(struct bxe_softc *sc) { struct tstorm_eth_function_common_config tstorm_config = {0}; struct stats_indication_flags stats_flags = {0}; struct ustorm_eth_rx_pause_data_e1h rx_pause = {0}; struct bxe_fastpath *fp; struct eth_rx_cqe_next_page *nextpg; uint32_t offset, size; uint16_t max_agg_size; uint8_t cl_id; int func, i, j, port; port = BP_PORT(sc); func = BP_FUNC(sc); DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Port %d, function %d internal initialization.\n", __FUNCTION__, port, func); /* * Configure which fields the controller looks at when * distributing incoming frames for RSS/multi-queue operation. */ if (sc->num_queues > 1) { tstorm_config.config_flags = MULTI_FLAGS(sc); tstorm_config.rss_result_mask = MULTI_MASK; } /* Enable TPA if needed */ if (TPA_ENABLED(sc)) tstorm_config.config_flags |= TSTORM_ETH_FUNCTION_COMMON_CONFIG_ENABLE_TPA; if (IS_E1HMF(sc)) tstorm_config.config_flags |= TSTORM_ETH_FUNCTION_COMMON_CONFIG_E1HOV_IN_CAM; tstorm_config.leading_client_id = BP_L_ID(sc); REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_FUNCTION_COMMON_CONFIG_OFFSET(func), (*(uint32_t *)&tstorm_config)); /* Don't receive anything until the link is up. */ sc->rx_mode = BXE_RX_MODE_NONE; sc->rx_mode_cl_mask = (1 << BP_L_ID(sc)); bxe_set_storm_rx_mode(sc); for (i = 0; i < sc->num_queues; i++) { cl_id = sc->fp[i].cl_id; /* Reset XSTORM per client statistics. */ size = sizeof(struct xstorm_per_client_stats) / 4; offset = BAR_XSTORM_INTMEM + XSTORM_PER_COUNTER_ID_STATS_OFFSET(port, cl_id); for (j = 0; j < size; j++) REG_WR(sc, offset +(j * 4), 0); /* Reset TSTORM per client statistics. */ size = sizeof(struct tstorm_per_client_stats) / 4; offset = BAR_TSTORM_INTMEM + TSTORM_PER_COUNTER_ID_STATS_OFFSET(port, cl_id); for (j = 0; j < size; j++) REG_WR(sc, offset + (j * 4), 0); /* Reset USTORM per client statistics. */ size = sizeof(struct ustorm_per_client_stats) / 4; offset = BAR_USTORM_INTMEM + USTORM_PER_COUNTER_ID_STATS_OFFSET(port, cl_id); for (j = 0; j < size; j++) REG_WR(sc, offset + (j * 4), 0); } /* Initialize statistics related context. */ stats_flags.collect_eth = 1; REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_STATS_FLAGS_OFFSET(func), ((uint32_t *)&stats_flags)[0]); REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_STATS_FLAGS_OFFSET(func) + 4, ((uint32_t *)&stats_flags)[1]); REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_STATS_FLAGS_OFFSET(func), ((uint32_t *)&stats_flags)[0]); REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_STATS_FLAGS_OFFSET(func) + 4, ((uint32_t *)&stats_flags)[1]); REG_WR(sc, BAR_USTORM_INTMEM + USTORM_STATS_FLAGS_OFFSET(func), ((uint32_t *)&stats_flags)[0]); REG_WR(sc, BAR_USTORM_INTMEM + USTORM_STATS_FLAGS_OFFSET(func) + 4, ((uint32_t *)&stats_flags)[1]); REG_WR(sc, BAR_CSTORM_INTMEM + CSTORM_STATS_FLAGS_OFFSET(func), ((uint32_t *)&stats_flags)[0]); REG_WR(sc, BAR_CSTORM_INTMEM + CSTORM_STATS_FLAGS_OFFSET(func) + 4, ((uint32_t *)&stats_flags)[1]); REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_ETH_STATS_QUERY_ADDR_OFFSET(func), U64_LO(BXE_SP_MAPPING(sc, fw_stats))); REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_ETH_STATS_QUERY_ADDR_OFFSET(func) + 4, U64_HI(BXE_SP_MAPPING(sc, fw_stats))); REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_ETH_STATS_QUERY_ADDR_OFFSET(func), U64_LO(BXE_SP_MAPPING(sc, fw_stats))); REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_ETH_STATS_QUERY_ADDR_OFFSET(func) + 4, U64_HI(BXE_SP_MAPPING(sc, fw_stats))); REG_WR(sc, BAR_USTORM_INTMEM + USTORM_ETH_STATS_QUERY_ADDR_OFFSET(func), U64_LO(BXE_SP_MAPPING(sc, fw_stats))); REG_WR(sc, BAR_USTORM_INTMEM + USTORM_ETH_STATS_QUERY_ADDR_OFFSET(func) + 4, U64_HI(BXE_SP_MAPPING(sc, fw_stats))); /* Additional initialization for 57711/57711E. */ if (CHIP_IS_E1H(sc)) { REG_WR8(sc, BAR_XSTORM_INTMEM + XSTORM_FUNCTION_MODE_OFFSET, IS_E1HMF(sc)); REG_WR8(sc, BAR_TSTORM_INTMEM + TSTORM_FUNCTION_MODE_OFFSET, IS_E1HMF(sc)); REG_WR8(sc, BAR_CSTORM_INTMEM + CSTORM_FUNCTION_MODE_OFFSET, IS_E1HMF(sc)); REG_WR8(sc, BAR_USTORM_INTMEM + USTORM_FUNCTION_MODE_OFFSET, IS_E1HMF(sc)); /* Set the outer VLAN tag. */ REG_WR16(sc, BAR_XSTORM_INTMEM + XSTORM_E1HOV_OFFSET(func), sc->e1hov); } /* Init completion queue mapping and TPA aggregation size. */ max_agg_size = min((uint32_t)(sc->mbuf_alloc_size + (8 * BCM_PAGE_SIZE * PAGES_PER_SGE)), (uint32_t)0xffff); DBPRINT(sc, BXE_VERBOSE_TPA, "%s(): max_agg_size = 0x%08X\n", __FUNCTION__, max_agg_size); for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; nextpg = (struct eth_rx_cqe_next_page *) &fp->rcq_chain[USABLE_RCQ_ENTRIES_PER_PAGE]; /* Program the completion queue address. */ REG_WR(sc, BAR_USTORM_INTMEM + USTORM_CQE_PAGE_BASE_OFFSET(port, fp->cl_id), U64_LO(fp->rcq_dma.paddr)); REG_WR(sc, BAR_USTORM_INTMEM + USTORM_CQE_PAGE_BASE_OFFSET(port, fp->cl_id) + 4, U64_HI(fp->rcq_dma.paddr)); /* Program the first CQ next page address. */ REG_WR(sc, BAR_USTORM_INTMEM + USTORM_CQE_PAGE_NEXT_OFFSET(port, fp->cl_id), nextpg->addr_lo); REG_WR(sc, BAR_USTORM_INTMEM + USTORM_CQE_PAGE_NEXT_OFFSET(port, fp->cl_id) + 4, nextpg->addr_hi); /* Set the maximum TPA aggregation size. */ REG_WR16(sc, BAR_USTORM_INTMEM + USTORM_MAX_AGG_SIZE_OFFSET(port, fp->cl_id), max_agg_size); } /* Configure lossless flow control. */ if (CHIP_IS_E1H(sc)) { rx_pause.bd_thr_low = 250; rx_pause.cqe_thr_low = 250; rx_pause.cos = 1; rx_pause.sge_thr_low = 0; rx_pause.bd_thr_high = 350; rx_pause.cqe_thr_high = 350; rx_pause.sge_thr_high = 0; for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; if (fp->disable_tpa == FALSE) { rx_pause.sge_thr_low = 150; rx_pause.sge_thr_high = 250; } offset = BAR_USTORM_INTMEM + USTORM_ETH_RING_PAUSE_DATA_OFFSET(port, fp->cl_id); for (j = 0; j < sizeof(struct ustorm_eth_rx_pause_data_e1h) / 4; j++) REG_WR(sc, offset + (j * 4), ((uint32_t *)&rx_pause)[j]); } } memset(&(sc->cmng), 0, sizeof(struct cmng_struct_per_port)); if (IS_E1HMF(sc)) { /* * During init there is no active link. * Until link is up, assume link rate @ 10Gbps */ bxe_read_mf_cfg(sc); if (!sc->vn_wsum) DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): All MIN values are zeroes, " "fairness will be disabled.\n", __FUNCTION__); } /* Store it to internal memory */ if (sc->port.pmf) { for (i = 0; i < sizeof(struct cmng_struct_per_port) / 4; i++) REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_CMNG_PER_PORT_VARS_OFFSET(port) + i * 4, ((uint32_t *)(&sc->cmng))[i]); } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Initialize internal resources. * * Returns: * Nothing. */ static void bxe_init_internal(struct bxe_softc *sc, uint32_t load_code) { DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); switch (load_code) { case FW_MSG_CODE_DRV_LOAD_COMMON: bxe_init_internal_common(sc); /* FALLTHROUGH */ case FW_MSG_CODE_DRV_LOAD_PORT: bxe_init_internal_port(sc); /* FALLTHROUGH */ case FW_MSG_CODE_DRV_LOAD_FUNCTION: bxe_init_internal_func(sc); break; default: BXE_PRINTF( "%s(%d): Unknown load_code (0x%08X) from MCP!\n", __FILE__, __LINE__, load_code); break; } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Perform driver instance specific initialization. * * Returns: * None */ static int bxe_init_nic(struct bxe_softc *sc, uint32_t load_code) { struct bxe_fastpath *fp; int i, rc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); /* Intialize fastpath structures and the status block. */ for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; fp->disable_tpa = TRUE; bzero((char *)fp->status_block, BXE_STATUS_BLK_SZ); fp->fp_u_idx = 0; fp->fp_c_idx = 0; /* Set a pointer back to the driver instance. */ fp->sc = sc; /* Set the fastpath starting state as closed. */ fp->state = BXE_FP_STATE_CLOSED; /* Self-reference to this fastpath's instance. */ fp->index = i; /* Set the client ID beginning with the leading id. */ fp->cl_id = BP_L_ID(sc) + i; /* Set the status block ID for this fastpath instance. */ fp->sb_id = fp->cl_id; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): fp[%02d]: cl_id = %d, sb_id = %d\n", __FUNCTION__, fp->index, fp->cl_id, fp->sb_id); /* Initialize the fastpath status block. */ bxe_init_sb(sc, fp->status_block, fp->sb_dma.paddr, fp->sb_id); bxe_update_fpsb_idx(fp); } rmb(); bzero((char *)sc->def_sb, BXE_DEF_STATUS_BLK_SZ); /* Initialize the Default Status Block. */ bxe_init_def_sb(sc, sc->def_sb, sc->def_sb_dma.paddr, DEF_SB_ID); bxe_update_dsb_idx(sc); /* Initialize the coalescence parameters. */ bxe_update_coalesce(sc); /* Initialize receive chains. */ rc = bxe_init_rx_chains(sc); if (rc != 0) { goto bxe_init_nic_exit; } /* Initialize the Transmit BD Chain. */ bxe_init_tx_chains(sc); /* Initialize the Slow Path Chain. */ bxe_init_sp_ring(sc); /* Initialize STORM processor context/configuration. */ bxe_init_context(sc); /* Initialize the Context. */ bxe_init_internal(sc, load_code); /* Enable indirection table for multi-queue operation. */ bxe_init_ind_table(sc); mb(); /* Disable the interrupts from device until init is complete.*/ bxe_int_disable(sc); bxe_init_nic_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Send a loopback packet through the Network Interface Glue (NIG) block. * * Returns: * None. */ static void bxe_lb_pckt(struct bxe_softc *sc) { #ifdef BXE_USE_DMAE uint32_t wb_write[3]; #endif DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); /* Ethernet source and destination addresses. */ #ifdef BXE_USE_DMAE wb_write[0] = 0x55555555; wb_write[1] = 0x55555555; wb_write[2] = 0x20; /* SOP */ REG_WR_DMAE(sc, NIG_REG_DEBUG_PACKET_LB, wb_write, 3); #else REG_WR_IND(sc, NIG_REG_DEBUG_PACKET_LB, 0x55555555); REG_WR_IND(sc, NIG_REG_DEBUG_PACKET_LB + 4, 0x55555555); REG_WR_IND(sc, NIG_REG_DEBUG_PACKET_LB + 8, 0x20); #endif /* NON-IP protocol. */ #ifdef BXE_USE_DMAE wb_write[0] = 0x09000000; wb_write[1] = 0x55555555; wb_write[2] = 0x10; /* EOP */ REG_WR_DMAE(sc, NIG_REG_DEBUG_PACKET_LB, wb_write, 3); #else REG_WR_IND(sc, NIG_REG_DEBUG_PACKET_LB, 0x09000000); REG_WR_IND(sc, NIG_REG_DEBUG_PACKET_LB + 4, 0x55555555); REG_WR_IND(sc, NIG_REG_DEBUG_PACKET_LB + 8, 0x10); #endif DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Perform an internal memory test. * * Some internal memories are not accessible through the PCIe interface so * we send some debug packets for the test. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_int_mem_test(struct bxe_softc *sc) { uint32_t val; int count, i, rc; rc = 0; val = 0; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); /* Perform a single debug packet test. */ /* Disable inputs of parser neighbor blocks. */ REG_WR(sc, TSDM_REG_ENABLE_IN1, 0x0); REG_WR(sc, TCM_REG_PRS_IFEN, 0x0); REG_WR(sc, CFC_REG_DEBUG0, 0x1); REG_WR(sc, NIG_REG_PRS_REQ_IN_EN, 0x0); /* Write 0 to parser credits for CFC search request. */ REG_WR(sc, PRS_REG_CFC_SEARCH_INITIAL_CREDIT, 0x0); /* Send an Ethernet packet. */ bxe_lb_pckt(sc); /* Wait until NIG register shows 1 packet of size 0x10. */ count = 1000; while (count) { bxe_read_dmae(sc, NIG_REG_STAT2_BRB_OCTET, 2); val = *BXE_SP(sc, wb_data[0]); if (val == 0x10) break; DELAY(10000); count--; } if (val != 0x10) { DBPRINT(sc, BXE_FATAL, "%s(): NIG loopback test 1 timeout (val = 0x%08X)!\n", __FUNCTION__, val); rc = 1; goto bxe_int_mem_test_exit; } /* Wait until PRS register shows 1 packet */ count = 1000; while (count) { val = REG_RD(sc, PRS_REG_NUM_OF_PACKETS); if (val == 1) break; DELAY(10000); count--; } if (val != 0x1) { DBPRINT(sc, BXE_FATAL, "%s(): PRS loopback test 1 timeout (val = 0x%08X)!\n", __FUNCTION__, val); rc = 2; goto bxe_int_mem_test_exit; } /* Reset and init BRB, PRS. */ REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, 0x3); DELAY(50000); REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0x3); DELAY(50000); bxe_init_block(sc, BRB1_BLOCK, COMMON_STAGE); bxe_init_block(sc, PRS_BLOCK, COMMON_STAGE); /* Perform the test again, this time with 10 packets. */ /* Disable inputs of parser neighbor blocks. */ REG_WR(sc, TSDM_REG_ENABLE_IN1, 0x0); REG_WR(sc, TCM_REG_PRS_IFEN, 0x0); REG_WR(sc, CFC_REG_DEBUG0, 0x1); REG_WR(sc, NIG_REG_PRS_REQ_IN_EN, 0x0); /* Write 0 to parser credits for CFC search request. */ REG_WR(sc, PRS_REG_CFC_SEARCH_INITIAL_CREDIT, 0x0); /* Send 10 Ethernet packets. */ for (i = 0; i < 10; i++) bxe_lb_pckt(sc); /* Wait until NIG shows 10 + 1 packets of size 11 * 0x10 = 0xb0. */ count = 1000; while (count) { bxe_read_dmae(sc, NIG_REG_STAT2_BRB_OCTET, 2); val = *BXE_SP(sc, wb_data[0]); if (val == 0xb0) break; DELAY(10000); count--; } if (val != 0xb0) { DBPRINT(sc, BXE_FATAL, "%s(): NIG loopback test 2 timeout (val = 0x%08X)!\n", __FUNCTION__, val); rc = 3; goto bxe_int_mem_test_exit; } /* Wait until PRS register shows 2 packets. */ val = REG_RD(sc, PRS_REG_NUM_OF_PACKETS); if (val != 2) { DBPRINT(sc, BXE_FATAL, "%s(): PRS loopback test 2 timeout (val = 0x%x)!\n", __FUNCTION__, val); rc = 4; goto bxe_int_mem_test_exit; } /* Write 1 to parser credits for CFC search request. */ REG_WR(sc, PRS_REG_CFC_SEARCH_INITIAL_CREDIT, 0x1); /* Wait until PRS register shows 3 packets. */ DELAY(10000); /* Wait until NIG register shows 1 packet of size 0x10. */ val = REG_RD(sc, PRS_REG_NUM_OF_PACKETS); if (val != 3) { DBPRINT(sc, BXE_FATAL, "%s(): PRS loopback test 3 timeout (val = 0x%08X)!\n", __FUNCTION__, val); rc = 5; goto bxe_int_mem_test_exit; } /* Clear NIG end-of-packet FIFO. */ for (i = 0; i < 11; i++) REG_RD(sc, NIG_REG_INGRESS_EOP_LB_FIFO); val = REG_RD(sc, NIG_REG_INGRESS_EOP_LB_EMPTY); if (val != 1) { DBPRINT(sc, BXE_INFO, "%s(): Unable to clear NIG!\n", __FUNCTION__); rc = 6; goto bxe_int_mem_test_exit; } /* Reset and init BRB, PRS, NIG. */ REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, 0x03); DELAY(50000); REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0x03); DELAY(50000); bxe_init_block(sc, BRB1_BLOCK, COMMON_STAGE); bxe_init_block(sc, PRS_BLOCK, COMMON_STAGE); /* Set NIC mode. */ REG_WR(sc, PRS_REG_NIC_MODE, 1); /* Enable inputs of parser neighbor blocks. */ REG_WR(sc, TSDM_REG_ENABLE_IN1, 0x7fffffff); REG_WR(sc, TCM_REG_PRS_IFEN, 0x1); REG_WR(sc, CFC_REG_DEBUG0, 0x0); REG_WR(sc, NIG_REG_PRS_REQ_IN_EN, 0x1); bxe_int_mem_test_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Enable attentions from various blocks. * * Returns: * None. */ static void bxe_enable_blocks_attention(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); REG_WR(sc, PXP_REG_PXP_INT_MASK_0, 0); REG_WR(sc, PXP_REG_PXP_INT_MASK_1, 0); REG_WR(sc, DORQ_REG_DORQ_INT_MASK, 0); REG_WR(sc, CFC_REG_CFC_INT_MASK, 0); REG_WR(sc, QM_REG_QM_INT_MASK, 0); REG_WR(sc, TM_REG_TM_INT_MASK, 0); REG_WR(sc, XSDM_REG_XSDM_INT_MASK_0, 0); REG_WR(sc, XSDM_REG_XSDM_INT_MASK_1, 0); REG_WR(sc, XCM_REG_XCM_INT_MASK, 0); REG_WR(sc, USDM_REG_USDM_INT_MASK_0, 0); REG_WR(sc, USDM_REG_USDM_INT_MASK_1, 0); REG_WR(sc, UCM_REG_UCM_INT_MASK, 0); REG_WR(sc, GRCBASE_UPB + PB_REG_PB_INT_MASK, 0); REG_WR(sc, CSDM_REG_CSDM_INT_MASK_0, 0); REG_WR(sc, CSDM_REG_CSDM_INT_MASK_1, 0); REG_WR(sc, CCM_REG_CCM_INT_MASK, 0); REG_WR(sc, PXP2_REG_PXP2_INT_MASK_0, 0x480000); REG_WR(sc, TSDM_REG_TSDM_INT_MASK_0, 0); REG_WR(sc, TSDM_REG_TSDM_INT_MASK_1, 0); REG_WR(sc, TCM_REG_TCM_INT_MASK, 0); REG_WR(sc, CDU_REG_CDU_INT_MASK, 0); REG_WR(sc, DMAE_REG_DMAE_INT_MASK, 0); REG_WR(sc, PBF_REG_PBF_INT_MASK, 0X18); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * PXP Arbiter */ /* * This code configures the PCI read/write arbiter * which implements a weighted round robin * between the virtual queues in the chip. * * The values were derived for each PCI max payload and max request size. * since max payload and max request size are only known at run time, * this is done as a separate init stage. */ #define NUM_WR_Q 13 #define NUM_RD_Q 29 #define MAX_RD_ORD 3 #define MAX_WR_ORD 2 /* Configuration for one arbiter queue. */ struct arb_line { int l; int add; int ubound; }; /* Derived configuration for each read queue for each max request size. */ static const struct arb_line read_arb_data[NUM_RD_Q][MAX_RD_ORD + 1] = { /* 1 */ { {8, 64, 25}, {16, 64, 25}, {32, 64, 25}, {64, 64, 41} }, { {4, 8, 4}, {4, 8, 4}, {4, 8, 4}, {4, 8, 4} }, { {4, 3, 3}, {4, 3, 3}, {4, 3, 3}, {4, 3, 3} }, { {8, 3, 6}, {16, 3, 11}, {16, 3, 11}, {16, 3, 11} }, { {8, 64, 25}, {16, 64, 25}, {32, 64, 25}, {64, 64, 41} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {64, 3, 41} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {64, 3, 41} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {64, 3, 41} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {64, 3, 41} }, /* 10 */{ {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 64, 6}, {16, 64, 11}, {32, 64, 21}, {32, 64, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, /* 20 */{ {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 64, 25}, {16, 64, 41}, {32, 64, 81}, {64, 64, 120} } }; /* Derived configuration for each write queue for each max request size. */ static const struct arb_line write_arb_data[NUM_WR_Q][MAX_WR_ORD + 1] = { /* 1 */ { {4, 6, 3}, {4, 6, 3}, {4, 6, 3} }, { {4, 2, 3}, {4, 2, 3}, {4, 2, 3} }, { {8, 2, 6}, {16, 2, 11}, {16, 2, 11} }, { {8, 2, 6}, {16, 2, 11}, {32, 2, 21} }, { {8, 2, 6}, {16, 2, 11}, {32, 2, 21} }, { {8, 2, 6}, {16, 2, 11}, {32, 2, 21} }, { {8, 64, 25}, {16, 64, 25}, {32, 64, 25} }, { {8, 2, 6}, {16, 2, 11}, {16, 2, 11} }, { {8, 2, 6}, {16, 2, 11}, {16, 2, 11} }, /* 10 */{ {8, 9, 6}, {16, 9, 11}, {32, 9, 21} }, { {8, 47, 19}, {16, 47, 19}, {32, 47, 21} }, { {8, 9, 6}, {16, 9, 11}, {16, 9, 11} }, { {8, 64, 25}, {16, 64, 41}, {32, 64, 81} } }; /* Register addresses for read queues. */ static const struct arb_line read_arb_addr[NUM_RD_Q-1] = { /* 1 */ {PXP2_REG_RQ_BW_RD_L0, PXP2_REG_RQ_BW_RD_ADD0, PXP2_REG_RQ_BW_RD_UBOUND0}, {PXP2_REG_PSWRQ_BW_L1, PXP2_REG_PSWRQ_BW_ADD1, PXP2_REG_PSWRQ_BW_UB1}, {PXP2_REG_PSWRQ_BW_L2, PXP2_REG_PSWRQ_BW_ADD2, PXP2_REG_PSWRQ_BW_UB2}, {PXP2_REG_PSWRQ_BW_L3, PXP2_REG_PSWRQ_BW_ADD3, PXP2_REG_PSWRQ_BW_UB3}, {PXP2_REG_RQ_BW_RD_L4, PXP2_REG_RQ_BW_RD_ADD4, PXP2_REG_RQ_BW_RD_UBOUND4}, {PXP2_REG_RQ_BW_RD_L5, PXP2_REG_RQ_BW_RD_ADD5, PXP2_REG_RQ_BW_RD_UBOUND5}, {PXP2_REG_PSWRQ_BW_L6, PXP2_REG_PSWRQ_BW_ADD6, PXP2_REG_PSWRQ_BW_UB6}, {PXP2_REG_PSWRQ_BW_L7, PXP2_REG_PSWRQ_BW_ADD7, PXP2_REG_PSWRQ_BW_UB7}, {PXP2_REG_PSWRQ_BW_L8, PXP2_REG_PSWRQ_BW_ADD8, PXP2_REG_PSWRQ_BW_UB8}, /* 10 */{PXP2_REG_PSWRQ_BW_L9, PXP2_REG_PSWRQ_BW_ADD9, PXP2_REG_PSWRQ_BW_UB9}, {PXP2_REG_PSWRQ_BW_L10, PXP2_REG_PSWRQ_BW_ADD10, PXP2_REG_PSWRQ_BW_UB10}, {PXP2_REG_PSWRQ_BW_L11, PXP2_REG_PSWRQ_BW_ADD11, PXP2_REG_PSWRQ_BW_UB11}, {PXP2_REG_RQ_BW_RD_L12, PXP2_REG_RQ_BW_RD_ADD12, PXP2_REG_RQ_BW_RD_UBOUND12}, {PXP2_REG_RQ_BW_RD_L13, PXP2_REG_RQ_BW_RD_ADD13, PXP2_REG_RQ_BW_RD_UBOUND13}, {PXP2_REG_RQ_BW_RD_L14, PXP2_REG_RQ_BW_RD_ADD14, PXP2_REG_RQ_BW_RD_UBOUND14}, {PXP2_REG_RQ_BW_RD_L15, PXP2_REG_RQ_BW_RD_ADD15, PXP2_REG_RQ_BW_RD_UBOUND15}, {PXP2_REG_RQ_BW_RD_L16, PXP2_REG_RQ_BW_RD_ADD16, PXP2_REG_RQ_BW_RD_UBOUND16}, {PXP2_REG_RQ_BW_RD_L17, PXP2_REG_RQ_BW_RD_ADD17, PXP2_REG_RQ_BW_RD_UBOUND17}, {PXP2_REG_RQ_BW_RD_L18, PXP2_REG_RQ_BW_RD_ADD18, PXP2_REG_RQ_BW_RD_UBOUND18}, /* 20 */{PXP2_REG_RQ_BW_RD_L19, PXP2_REG_RQ_BW_RD_ADD19, PXP2_REG_RQ_BW_RD_UBOUND19}, {PXP2_REG_RQ_BW_RD_L20, PXP2_REG_RQ_BW_RD_ADD20, PXP2_REG_RQ_BW_RD_UBOUND20}, {PXP2_REG_RQ_BW_RD_L22, PXP2_REG_RQ_BW_RD_ADD22, PXP2_REG_RQ_BW_RD_UBOUND22}, {PXP2_REG_RQ_BW_RD_L23, PXP2_REG_RQ_BW_RD_ADD23, PXP2_REG_RQ_BW_RD_UBOUND23}, {PXP2_REG_RQ_BW_RD_L24, PXP2_REG_RQ_BW_RD_ADD24, PXP2_REG_RQ_BW_RD_UBOUND24}, {PXP2_REG_RQ_BW_RD_L25, PXP2_REG_RQ_BW_RD_ADD25, PXP2_REG_RQ_BW_RD_UBOUND25}, {PXP2_REG_RQ_BW_RD_L26, PXP2_REG_RQ_BW_RD_ADD26, PXP2_REG_RQ_BW_RD_UBOUND26}, {PXP2_REG_RQ_BW_RD_L27, PXP2_REG_RQ_BW_RD_ADD27, PXP2_REG_RQ_BW_RD_UBOUND27}, {PXP2_REG_PSWRQ_BW_L28, PXP2_REG_PSWRQ_BW_ADD28, PXP2_REG_PSWRQ_BW_UB28} }; /* Register addresses for write queues. */ static const struct arb_line write_arb_addr[NUM_WR_Q-1] = { /* 1 */ {PXP2_REG_PSWRQ_BW_L1, PXP2_REG_PSWRQ_BW_ADD1, PXP2_REG_PSWRQ_BW_UB1}, {PXP2_REG_PSWRQ_BW_L2, PXP2_REG_PSWRQ_BW_ADD2, PXP2_REG_PSWRQ_BW_UB2}, {PXP2_REG_PSWRQ_BW_L3, PXP2_REG_PSWRQ_BW_ADD3, PXP2_REG_PSWRQ_BW_UB3}, {PXP2_REG_PSWRQ_BW_L6, PXP2_REG_PSWRQ_BW_ADD6, PXP2_REG_PSWRQ_BW_UB6}, {PXP2_REG_PSWRQ_BW_L7, PXP2_REG_PSWRQ_BW_ADD7, PXP2_REG_PSWRQ_BW_UB7}, {PXP2_REG_PSWRQ_BW_L8, PXP2_REG_PSWRQ_BW_ADD8, PXP2_REG_PSWRQ_BW_UB8}, {PXP2_REG_PSWRQ_BW_L9, PXP2_REG_PSWRQ_BW_ADD9, PXP2_REG_PSWRQ_BW_UB9}, {PXP2_REG_PSWRQ_BW_L10, PXP2_REG_PSWRQ_BW_ADD10, PXP2_REG_PSWRQ_BW_UB10}, {PXP2_REG_PSWRQ_BW_L11, PXP2_REG_PSWRQ_BW_ADD11, PXP2_REG_PSWRQ_BW_UB11}, /* 10 */{PXP2_REG_PSWRQ_BW_L28, PXP2_REG_PSWRQ_BW_ADD28, PXP2_REG_PSWRQ_BW_UB28}, {PXP2_REG_RQ_BW_WR_L29, PXP2_REG_RQ_BW_WR_ADD29, PXP2_REG_RQ_BW_WR_UBOUND29}, {PXP2_REG_RQ_BW_WR_L30, PXP2_REG_RQ_BW_WR_ADD30, PXP2_REG_RQ_BW_WR_UBOUND30} }; static void bxe_init_pxp_arb(struct bxe_softc *sc, int r_order, int w_order) { uint32_t val, i; if (r_order > MAX_RD_ORD) { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Read order of %d order adjusted to %d\n", __FUNCTION__, r_order, MAX_RD_ORD); r_order = MAX_RD_ORD; } if (w_order > MAX_WR_ORD) { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Write order of %d order adjusted to %d\n", __FUNCTION__, w_order, MAX_WR_ORD); w_order = MAX_WR_ORD; } DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Read order %d, write order %d\n", __FUNCTION__, r_order, w_order); for (i = 0; i < NUM_RD_Q - 1; i++) { REG_WR(sc, read_arb_addr[i].l, read_arb_data[i][r_order].l); REG_WR(sc, read_arb_addr[i].add, read_arb_data[i][r_order].add); REG_WR(sc, read_arb_addr[i].ubound, read_arb_data[i][r_order].ubound); } for (i = 0; i < NUM_WR_Q - 1; i++) { if ((write_arb_addr[i].l == PXP2_REG_RQ_BW_WR_L29) || (write_arb_addr[i].l == PXP2_REG_RQ_BW_WR_L30)) { REG_WR(sc, write_arb_addr[i].l, write_arb_data[i][w_order].l); REG_WR(sc, write_arb_addr[i].add, write_arb_data[i][w_order].add); REG_WR(sc, write_arb_addr[i].ubound, write_arb_data[i][w_order].ubound); } else { val = REG_RD(sc, write_arb_addr[i].l); REG_WR(sc, write_arb_addr[i].l, val | (write_arb_data[i][w_order].l << 10)); val = REG_RD(sc, write_arb_addr[i].add); REG_WR(sc, write_arb_addr[i].add, val | (write_arb_data[i][w_order].add << 10)); val = REG_RD(sc, write_arb_addr[i].ubound); REG_WR(sc, write_arb_addr[i].ubound, val | (write_arb_data[i][w_order].ubound << 7)); } } val = write_arb_data[NUM_WR_Q - 1][w_order].add; val += write_arb_data[NUM_WR_Q - 1][w_order].ubound << 10; val += write_arb_data[NUM_WR_Q - 1][w_order].l << 17; REG_WR(sc, PXP2_REG_PSWRQ_BW_RD, val); val = read_arb_data[NUM_RD_Q - 1][r_order].add; val += read_arb_data[NUM_RD_Q - 1][r_order].ubound << 10; val += read_arb_data[NUM_RD_Q - 1][r_order].l << 17; REG_WR(sc, PXP2_REG_PSWRQ_BW_WR, val); REG_WR(sc, PXP2_REG_RQ_WR_MBS0, w_order); REG_WR(sc, PXP2_REG_RQ_WR_MBS1, w_order); REG_WR(sc, PXP2_REG_RQ_RD_MBS0, r_order); REG_WR(sc, PXP2_REG_RQ_RD_MBS1, r_order); if (r_order == MAX_RD_ORD) REG_WR(sc, PXP2_REG_RQ_PDR_LIMIT, 0xe00); REG_WR(sc, PXP2_REG_WR_USDMDP_TH, (0x18 << w_order)); if (CHIP_IS_E1H(sc)) { /* MPS w_order optimal TH presently TH * 128 0 0 2 * 256 1 1 3 * >=512 2 2 3 */ val = ((w_order == 0) ? 2 : 3); REG_WR(sc, PXP2_REG_WR_HC_MPS, val); REG_WR(sc, PXP2_REG_WR_USDM_MPS, val); REG_WR(sc, PXP2_REG_WR_CSDM_MPS, val); REG_WR(sc, PXP2_REG_WR_TSDM_MPS, val); REG_WR(sc, PXP2_REG_WR_XSDM_MPS, val); REG_WR(sc, PXP2_REG_WR_QM_MPS, val); REG_WR(sc, PXP2_REG_WR_TM_MPS, val); REG_WR(sc, PXP2_REG_WR_SRC_MPS, val); REG_WR(sc, PXP2_REG_WR_DBG_MPS, val); REG_WR(sc, PXP2_REG_WR_DMAE_MPS, 2); /* DMAE is special */ REG_WR(sc, PXP2_REG_WR_CDU_MPS, val); } } static void bxe_init_pxp(struct bxe_softc *sc) { uint16_t devctl; int r_order, w_order; devctl = pci_read_config(sc->dev, sc->pcie_cap + PCI_EXP_DEVCTL, 2); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Read 0x%x from devctl\n", __FUNCTION__, devctl); w_order = ((devctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5); if (sc->mrrs == -1) r_order = ((devctl & PCI_EXP_DEVCTL_READRQ) >> 12); else { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Force MRRS read order to %d\n", __FUNCTION__, sc->mrrs); r_order = sc->mrrs; } bxe_init_pxp_arb(sc, r_order, w_order); } static void bxe_setup_fan_failure_detection(struct bxe_softc *sc) { uint32_t phy_type, val; int is_required, port; is_required = 0; if (NOMCP(sc)) return; val = SHMEM_RD(sc, dev_info.shared_hw_config.config2) & SHARED_HW_CFG_FAN_FAILURE_MASK; if (val == SHARED_HW_CFG_FAN_FAILURE_ENABLED) is_required = 1; /* * The fan failure mechanism is usually related to the PHY type since * the power consumption of the board is affected by the PHY. Currently, * fan is required for most designs with SFX7101, BCM8727 and BCM8481. */ else if (val == SHARED_HW_CFG_FAN_FAILURE_PHY_TYPE) for (port = PORT_0; port < PORT_MAX; port++) { phy_type = SHMEM_RD(sc, dev_info.port_hw_config[port].external_phy_config) & PORT_HW_CFG_XGXS_EXT_PHY_TYPE_MASK; is_required |= ((phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101) || (phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727) || (phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481)); } if (is_required == 0) return; /* Fan failure is indicated by SPIO 5. */ bxe_set_spio(sc, MISC_REGISTERS_SPIO_5, MISC_REGISTERS_SPIO_INPUT_HI_Z); /* Set to active low mode. */ val = REG_RD(sc, MISC_REG_SPIO_INT); val |= ((1 << MISC_REGISTERS_SPIO_5) << MISC_REGISTERS_SPIO_INT_OLD_SET_POS); REG_WR(sc, MISC_REG_SPIO_INT, val); /* Enable interrupt to signal the IGU. */ val = REG_RD(sc, MISC_REG_SPIO_EVENT_EN); val |= (1 << MISC_REGISTERS_SPIO_5); REG_WR(sc, MISC_REG_SPIO_EVENT_EN, val); } /* * Common initialization. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_init_common(struct bxe_softc *sc) { uint32_t val; int i, rc; rc = 0; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); /* Reset all blocks within the chip except the BMAC. */ bxe_reset_common(sc); DELAY(30000); REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0xffffffff); REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, 0xfffc); DELAY(30000); bxe_init_block(sc, MISC_BLOCK, COMMON_STAGE); if (CHIP_IS_E1H(sc)) REG_WR(sc, MISC_REG_E1HMF_MODE, IS_E1HMF(sc)); REG_WR(sc, MISC_REG_LCPLL_CTRL_REG_2, 0x100); DELAY(30000); REG_WR(sc, MISC_REG_LCPLL_CTRL_REG_2, 0x0); bxe_init_block(sc, PXP_BLOCK, COMMON_STAGE); if (CHIP_IS_E1(sc)) { /* * Enable HW interrupt from PXP on USDM overflow * bit 16 on INT_MASK_0. */ REG_WR(sc, PXP_REG_PXP_INT_MASK_0, 0); } bxe_init_block(sc, PXP2_BLOCK, COMMON_STAGE); bxe_init_pxp(sc); #ifdef __BIG_ENDIAN REG_WR(sc, PXP2_REG_RQ_QM_ENDIAN_M, 1); REG_WR(sc, PXP2_REG_RQ_TM_ENDIAN_M, 1); REG_WR(sc, PXP2_REG_RQ_SRC_ENDIAN_M, 1); REG_WR(sc, PXP2_REG_RQ_CDU_ENDIAN_M, 1); REG_WR(sc, PXP2_REG_RQ_DBG_ENDIAN_M, 1); /* Make sure this value is 0. */ REG_WR(sc, PXP2_REG_RQ_HC_ENDIAN_M, 0); REG_WR(sc, PXP2_REG_RD_QM_SWAP_MODE, 1); REG_WR(sc, PXP2_REG_RD_TM_SWAP_MODE, 1); REG_WR(sc, PXP2_REG_RD_SRC_SWAP_MODE, 1); REG_WR(sc, PXP2_REG_RD_CDURD_SWAP_MODE, 1); #endif REG_WR(sc, PXP2_REG_RQ_CDU_P_SIZE, 2); /* Let the HW do it's magic ... */ DELAY(100000); /* Finish the PXP initialization. */ val = REG_RD(sc, PXP2_REG_RQ_CFG_DONE); if (val != 1) { BXE_PRINTF("%s(%d): PXP2 CFG failed!\n", __FILE__, __LINE__); rc = EBUSY; goto bxe_init_common_exit; } val = REG_RD(sc, PXP2_REG_RD_INIT_DONE); if (val != 1) { BXE_PRINTF("%s(%d): PXP2 RD_INIT failed!\n", __FILE__, __LINE__); rc = EBUSY; goto bxe_init_common_exit; } REG_WR(sc, PXP2_REG_RQ_DISABLE_INPUTS, 0); REG_WR(sc, PXP2_REG_RD_DISABLE_INPUTS, 0); bxe_init_block(sc, DMAE_BLOCK, COMMON_STAGE); sc->dmae_ready = 1; bxe_init_fill(sc, TSEM_REG_PRAM, 0, 8); bxe_init_block(sc, TCM_BLOCK, COMMON_STAGE); bxe_init_block(sc, UCM_BLOCK, COMMON_STAGE); bxe_init_block(sc, CCM_BLOCK, COMMON_STAGE); bxe_init_block(sc, XCM_BLOCK, COMMON_STAGE); bxe_read_dmae(sc, XSEM_REG_PASSIVE_BUFFER, 3); bxe_read_dmae(sc, CSEM_REG_PASSIVE_BUFFER, 3); bxe_read_dmae(sc, TSEM_REG_PASSIVE_BUFFER, 3); bxe_read_dmae(sc, USEM_REG_PASSIVE_BUFFER, 3); bxe_init_block(sc, QM_BLOCK, COMMON_STAGE); /* Soft reset pulse. */ REG_WR(sc, QM_REG_SOFT_RESET, 1); REG_WR(sc, QM_REG_SOFT_RESET, 0); bxe_init_block(sc, DQ_BLOCK, COMMON_STAGE); REG_WR(sc, DORQ_REG_DPM_CID_OFST, BCM_PAGE_SHIFT); REG_WR(sc, DORQ_REG_DORQ_INT_MASK, 0); bxe_init_block(sc, BRB1_BLOCK, COMMON_STAGE); bxe_init_block(sc, PRS_BLOCK, COMMON_STAGE); REG_WR(sc, PRS_REG_A_PRSU_20, 0xf); if (CHIP_IS_E1H(sc)) REG_WR(sc, PRS_REG_E1HOV_MODE, IS_E1HMF(sc)); bxe_init_block(sc, TSDM_BLOCK, COMMON_STAGE); bxe_init_block(sc, CSDM_BLOCK, COMMON_STAGE); bxe_init_block(sc, USDM_BLOCK, COMMON_STAGE); bxe_init_block(sc, XSDM_BLOCK, COMMON_STAGE); /* Clear STORM processor memory. */ bxe_init_fill(sc, TSEM_REG_FAST_MEMORY, 0, STORM_INTMEM_SIZE(sc)); bxe_init_fill(sc, USEM_REG_FAST_MEMORY, 0, STORM_INTMEM_SIZE(sc)); bxe_init_fill(sc, CSEM_REG_FAST_MEMORY, 0, STORM_INTMEM_SIZE(sc)); bxe_init_fill(sc, XSEM_REG_FAST_MEMORY, 0, STORM_INTMEM_SIZE(sc)); bxe_init_block(sc, TSEM_BLOCK, COMMON_STAGE); bxe_init_block(sc, USEM_BLOCK, COMMON_STAGE); bxe_init_block(sc, CSEM_BLOCK, COMMON_STAGE); bxe_init_block(sc, XSEM_BLOCK, COMMON_STAGE); /* Sync semi rtc. */ REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, 0x80000000); REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0x80000000); bxe_init_block(sc, UPB_BLOCK, COMMON_STAGE); bxe_init_block(sc, XPB_BLOCK, COMMON_STAGE); bxe_init_block(sc, PBF_BLOCK, COMMON_STAGE); REG_WR(sc, SRC_REG_SOFT_RST, 1); /* Setup RSS/multi-queue hasking keys. */ for (i = SRC_REG_KEYRSS0_0; i <= SRC_REG_KEYRSS1_9; i += 4) REG_WR(sc, i, 0xc0cac01a); bxe_init_block(sc, SRCH_BLOCK, COMMON_STAGE); REG_WR(sc, SRC_REG_SOFT_RST, 0); /* Make sure the cdu_context structure has the right size. */ if (sizeof(union cdu_context) != 1024) { BXE_PRINTF("%s(%d): Invalid size for context (%ld != 1024)!\n", __FILE__, __LINE__, (long)sizeof(union cdu_context)); rc = EBUSY; goto bxe_init_common_exit; } bxe_init_block(sc, CDU_BLOCK, COMMON_STAGE); /* * val = (num_context_in_page << 24) + * (context_waste_size << 12) + * context_line_size. */ val = (4 << 24) + (0 << 12) + 1024; REG_WR(sc, CDU_REG_CDU_GLOBAL_PARAMS, val); bxe_init_block(sc, CFC_BLOCK, COMMON_STAGE); REG_WR(sc, CFC_REG_INIT_REG, 0x7FF); /* Enable context validation interrupt from CFC. */ REG_WR(sc, CFC_REG_CFC_INT_MASK, 0); /* Set the thresholds to prevent CFC/CDU race. */ REG_WR(sc, CFC_REG_DEBUG0, 0x20020000); bxe_init_block(sc, HC_BLOCK, COMMON_STAGE); bxe_init_block(sc, MISC_AEU_BLOCK, COMMON_STAGE); bxe_init_block(sc, PXPCS_BLOCK, COMMON_STAGE); /* Clear PCIe block debug status bits. */ REG_WR(sc, 0x2814, 0xffffffff); REG_WR(sc, 0x3820, 0xffffffff); bxe_init_block(sc, EMAC0_BLOCK, COMMON_STAGE); bxe_init_block(sc, EMAC1_BLOCK, COMMON_STAGE); bxe_init_block(sc, DBU_BLOCK, COMMON_STAGE); bxe_init_block(sc, DBG_BLOCK, COMMON_STAGE); bxe_init_block(sc, NIG_BLOCK, COMMON_STAGE); if (CHIP_IS_E1H(sc)) { REG_WR(sc, NIG_REG_LLH_MF_MODE, IS_E1HMF(sc)); REG_WR(sc, NIG_REG_LLH_E1HOV_MODE, IS_E1HOV(sc)); } /* Finish CFC initialization. */ val = bxe_reg_poll(sc, CFC_REG_LL_INIT_DONE, 1, 100, 10); if (val != 1) { BXE_PRINTF("%s(%d): CFC LL_INIT failed!\n", __FILE__, __LINE__); rc = EBUSY; goto bxe_init_common_exit; } val = bxe_reg_poll(sc, CFC_REG_AC_INIT_DONE, 1, 100, 10); if (val != 1) { BXE_PRINTF("%s(%d): CFC AC_INIT failed!\n", __FILE__, __LINE__); rc = EBUSY; goto bxe_init_common_exit; } val = bxe_reg_poll(sc, CFC_REG_CAM_INIT_DONE, 1, 100, 10); if (val != 1) { BXE_PRINTF("%s(%d): CFC CAM_INIT failed!\n", __FILE__, __LINE__); rc = EBUSY; goto bxe_init_common_exit; } REG_WR(sc, CFC_REG_DEBUG0, 0); /* Read NIG statistic and check for first load since powerup. */ bxe_read_dmae(sc, NIG_REG_STAT2_BRB_OCTET, 2); val = *BXE_SP(sc, wb_data[0]); /* Do internal memory self test only after a full power cycle. */ if ((CHIP_IS_E1(sc)) && (val == 0) && bxe_int_mem_test(sc)) { BXE_PRINTF("%s(%d): Internal memory self-test failed!\n", __FILE__, __LINE__); rc = EBUSY; goto bxe_init_common_exit; } /* Handle any board specific initialization. */ switch (XGXS_EXT_PHY_TYPE(sc->link_params.ext_phy_config)) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: break; default: break; } bxe_setup_fan_failure_detection(sc); /* Clear PXP2 attentions. */ REG_RD(sc, PXP2_REG_PXP2_INT_STS_CLR_0); bxe_enable_blocks_attention(sc); if (!NOMCP(sc)) { bxe_acquire_phy_lock(sc); bxe_common_init_phy(sc, sc->common.shmem_base); bxe_release_phy_lock(sc); } else BXE_PRINTF( "%s(%d): Bootcode is missing - cannot initialize PHY!\n", __FILE__, __LINE__); bxe_init_common_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Port initialization. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_init_port(struct bxe_softc *sc) { uint32_t val, low, high; uint32_t swap_val, swap_override, aeu_gpio_mask, offset; uint32_t reg_addr; int init_stage, port; port = BP_PORT(sc); init_stage = port ? PORT1_STAGE : PORT0_STAGE; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Initializing port %d.\n", __FUNCTION__, port); REG_WR(sc, NIG_REG_MASK_INTERRUPT_PORT0 + port * 4, 0); bxe_init_block(sc, PXP_BLOCK, init_stage); bxe_init_block(sc, PXP2_BLOCK, init_stage); bxe_init_block(sc, TCM_BLOCK, init_stage); bxe_init_block(sc, UCM_BLOCK, init_stage); bxe_init_block(sc, CCM_BLOCK, init_stage); bxe_init_block(sc, XCM_BLOCK, init_stage); bxe_init_block(sc, DQ_BLOCK, init_stage); bxe_init_block(sc, BRB1_BLOCK, init_stage); /* Determine the pause threshold for the BRB */ if (IS_E1HMF(sc)) low = (sc->bxe_flags & BXE_ONE_PORT_FLAG) ? 160 : 246; else if (sc->bxe_ifp->if_mtu > 4096) { if (sc->bxe_flags & BXE_ONE_PORT_FLAG) low = 160; else { val = sc->bxe_ifp->if_mtu; /* (24*1024 + val*4)/256 */ low = 96 + (val/64) + ((val % 64) ? 1 : 0); } } else low = (sc->bxe_flags & BXE_ONE_PORT_FLAG) ? 80 : 160; high = low + 56; /* 14 * 1024 / 256 */ REG_WR(sc, BRB1_REG_PAUSE_LOW_THRESHOLD_0 + port * 4, low); REG_WR(sc, BRB1_REG_PAUSE_HIGH_THRESHOLD_0 + port * 4, high); /* Port PRS comes here. */ bxe_init_block(sc, PRS_BLOCK, init_stage); bxe_init_block(sc, TSDM_BLOCK, init_stage); bxe_init_block(sc, CSDM_BLOCK, init_stage); bxe_init_block(sc, USDM_BLOCK, init_stage); bxe_init_block(sc, XSDM_BLOCK, init_stage); bxe_init_block(sc, TSEM_BLOCK, init_stage); bxe_init_block(sc, USEM_BLOCK, init_stage); bxe_init_block(sc, CSEM_BLOCK, init_stage); bxe_init_block(sc, XSEM_BLOCK, init_stage); bxe_init_block(sc, UPB_BLOCK, init_stage); bxe_init_block(sc, XPB_BLOCK, init_stage); bxe_init_block(sc, PBF_BLOCK, init_stage); /* Configure PBF to work without pause for MTU = 9000. */ REG_WR(sc, PBF_REG_P0_PAUSE_ENABLE + port * 4, 0); /* Update threshold. */ REG_WR(sc, PBF_REG_P0_ARB_THRSH + port * 4, (9040/16)); /* Update initial credit. */ REG_WR(sc, PBF_REG_P0_INIT_CRD + port * 4, (9040/16) + 553 - 22); /* Probe changes. */ REG_WR(sc, PBF_REG_INIT_P0 + port * 4, 1); DELAY(5000); REG_WR(sc, PBF_REG_INIT_P0 + port * 4, 0); bxe_init_block(sc, CDU_BLOCK, init_stage); bxe_init_block(sc, CFC_BLOCK, init_stage); if (CHIP_IS_E1(sc)) { REG_WR(sc, HC_REG_LEADING_EDGE_0 + port * 8, 0); REG_WR(sc, HC_REG_TRAILING_EDGE_0 + port * 8, 0); } bxe_init_block(sc, HC_BLOCK, init_stage); bxe_init_block(sc, MISC_AEU_BLOCK, init_stage); /* * init aeu_mask_attn_func_0/1: * - SF mode: bits 3-7 are masked. only bits 0-2 are in use * - MF mode: bit 3 is masked. bits 0-2 are in use as in SF * bits 4-7 are used for "per vn group attention" */ REG_WR(sc, MISC_REG_AEU_MASK_ATTN_FUNC_0 + port * 4, (IS_E1HMF(sc) ? 0xF7 : 0x7)); bxe_init_block(sc, PXPCS_BLOCK, init_stage); bxe_init_block(sc, EMAC0_BLOCK, init_stage); bxe_init_block(sc, EMAC1_BLOCK, init_stage); bxe_init_block(sc, DBU_BLOCK, init_stage); bxe_init_block(sc, DBG_BLOCK, init_stage); bxe_init_block(sc, NIG_BLOCK, init_stage); REG_WR(sc, NIG_REG_XGXS_SERDES0_MODE_SEL + port * 4, 1); if (CHIP_IS_E1H(sc)) { /* Enable outer VLAN support if required. */ REG_WR(sc, NIG_REG_LLH0_BRB1_DRV_MASK_MF + port * 4, (IS_E1HOV(sc) ? 0x1 : 0x2)); } REG_WR(sc, NIG_REG_LLFC_ENABLE_0 + port * 4, 0); REG_WR(sc, NIG_REG_LLFC_OUT_EN_0 + port * 4, 0); REG_WR(sc, NIG_REG_PAUSE_ENABLE_0 + port * 4, 1); bxe_init_block(sc, MCP_BLOCK, init_stage); bxe_init_block(sc, DMAE_BLOCK, init_stage); switch (XGXS_EXT_PHY_TYPE(sc->link_params.ext_phy_config)) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: bxe_set_gpio(sc, MISC_REGISTERS_GPIO_3, MISC_REGISTERS_GPIO_INPUT_HI_Z, port); /* * The GPIO should be swapped if the swap register is * set and active. */ swap_val = REG_RD(sc, NIG_REG_PORT_SWAP); swap_override = REG_RD(sc, NIG_REG_STRAP_OVERRIDE); /* Select function upon port-swap configuration. */ if (port == 0) { offset = MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0; aeu_gpio_mask = (swap_val && swap_override) ? AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_1 : AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_0; } else { offset = MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0; aeu_gpio_mask = (swap_val && swap_override) ? AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_0 : AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_1; } val = REG_RD(sc, offset); /* Add GPIO3 to group. */ val |= aeu_gpio_mask; REG_WR(sc, offset, val); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: /* Add SPIO 5 to group 0. */ reg_addr = port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0 : MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0; val = REG_RD(sc, reg_addr); val |= AEU_INPUTS_ATTN_BITS_SPIO5; REG_WR(sc, reg_addr, val); break; default: break; } bxe__link_reset(sc); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (0); } #define ILT_PER_FUNC (768/2) #define FUNC_ILT_BASE(func) (func * ILT_PER_FUNC) /* * The phys address is shifted right 12 bits and has an added 1=valid * bit added to the 53rd bit (bit 52) then since this is a wide * register(TM) we split it into two 32 bit writes. */ #define ONCHIP_ADDR1(x) ((uint32_t)(((uint64_t)x >> 12) & 0xFFFFFFFF)) #define ONCHIP_ADDR2(x) ((uint32_t)((1 << 20) | ((uint64_t)x >> 44))) #define PXP_ONE_ILT(x) (((x) << 10) | x) #define PXP_ILT_RANGE(f, l) (((l) << 10) | f) #define CNIC_ILT_LINES 0 /* * ILT write. * * Returns: * None. */ static void bxe_ilt_wr(struct bxe_softc *sc, uint32_t index, bus_addr_t addr) { int reg; DBENTER(BXE_INSANE_LOAD | BXE_INSANE_RESET); if (CHIP_IS_E1H(sc)) reg = PXP2_REG_RQ_ONCHIP_AT_B0 + index * 8; else reg = PXP2_REG_RQ_ONCHIP_AT + index * 8; bxe_wb_wr(sc, reg, ONCHIP_ADDR1(addr), ONCHIP_ADDR2(addr)); DBEXIT(BXE_INSANE_LOAD | BXE_INSANE_RESET); } /* * Initialize a function. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_init_func(struct bxe_softc *sc) { uint32_t addr, val; int func, i, port; port = BP_PORT(sc); func = BP_FUNC(sc); DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Initializing port %d, function %d.\n", __FUNCTION__, port, func); /* Set MSI reconfigure capability. */ addr = (port ? HC_REG_CONFIG_1 : HC_REG_CONFIG_0); val = REG_RD(sc, addr); val |= HC_CONFIG_0_REG_MSI_ATTN_EN_0; REG_WR(sc, addr, val); i = FUNC_ILT_BASE(func); bxe_ilt_wr(sc, i, BXE_SP_MAPPING(sc, context)); if (CHIP_IS_E1H(sc)) { REG_WR(sc, PXP2_REG_RQ_CDU_FIRST_ILT, i); REG_WR(sc, PXP2_REG_RQ_CDU_LAST_ILT, i + CNIC_ILT_LINES); } else /* E1 */ REG_WR(sc, PXP2_REG_PSWRQ_CDU0_L2P + func * 4, PXP_ILT_RANGE(i, i + CNIC_ILT_LINES)); if (CHIP_IS_E1H(sc)) { bxe_init_block(sc, MISC_BLOCK, FUNC0_STAGE + func); bxe_init_block(sc, TCM_BLOCK, FUNC0_STAGE + func); bxe_init_block(sc, UCM_BLOCK, FUNC0_STAGE + func); bxe_init_block(sc, CCM_BLOCK, FUNC0_STAGE + func); bxe_init_block(sc, XCM_BLOCK, FUNC0_STAGE + func); bxe_init_block(sc, TSEM_BLOCK, FUNC0_STAGE + func); bxe_init_block(sc, USEM_BLOCK, FUNC0_STAGE + func); bxe_init_block(sc, CSEM_BLOCK, FUNC0_STAGE + func); bxe_init_block(sc, XSEM_BLOCK, FUNC0_STAGE + func); REG_WR(sc, NIG_REG_LLH0_FUNC_EN + port * 8, 1); REG_WR(sc, NIG_REG_LLH0_FUNC_VLAN_ID + port * 8, sc->e1hov); } /* Host Coalescing initialization per function. */ if (CHIP_IS_E1H(sc)) { REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_12 + func * 4, 0); REG_WR(sc, HC_REG_LEADING_EDGE_0 + port * 8, 0); REG_WR(sc, HC_REG_TRAILING_EDGE_0 + port * 8, 0); } bxe_init_block(sc, HC_BLOCK, FUNC0_STAGE + func); /* Reset PCIe block debug values. */ REG_WR(sc, 0x2114, 0xffffffff); REG_WR(sc, 0x2120, 0xffffffff); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (0); } /* * * Returns: * 0 = Failure, !0 = Failure. */ static int bxe_init_hw(struct bxe_softc *sc, uint32_t load_code) { int func, i, rc; rc = 0; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); sc->dmae_ready = 0; switch (load_code) { case FW_MSG_CODE_DRV_LOAD_COMMON: rc = bxe_init_common(sc); if (rc) goto bxe_init_hw_exit; /* FALLTHROUGH */ case FW_MSG_CODE_DRV_LOAD_PORT: sc->dmae_ready = 1; rc = bxe_init_port(sc); if (rc) goto bxe_init_hw_exit; /* FALLTHROUGH */ case FW_MSG_CODE_DRV_LOAD_FUNCTION: sc->dmae_ready = 1; rc = bxe_init_func(sc); if (rc) goto bxe_init_hw_exit; break; default: DBPRINT(sc, BXE_WARN, "%s(): Unknown load_code (0x%08X) from MCP!\n", __FUNCTION__, load_code); break; } /* Fetch additional config data if the bootcode is running. */ if (!NOMCP(sc)) { func = BP_FUNC(sc); /* Fetch the pulse sequence number. */ sc->fw_drv_pulse_wr_seq = (SHMEM_RD(sc, func_mb[func].drv_pulse_mb) & DRV_PULSE_SEQ_MASK); } /* Clear the default status block. */ bxe_zero_def_sb(sc); for (i = 0; i < sc->num_queues; i++) bxe_zero_sb(sc, BP_L_ID(sc) + i); bxe_init_hw_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Send a firmware command and wait for the response. * * Post a command to shared memory for the bootcode running on the MCP and * stall until the bootcode responds or a timeout occurs. * * Returns: * 0 = Failure, otherwise firmware response code (FW_MSG_CODE_*). */ static int bxe_fw_command(struct bxe_softc *sc, uint32_t command) { uint32_t cnt, rc, seq; int func; func = BP_FUNC(sc); seq = ++sc->fw_seq; rc = 0; cnt = 1; DBRUNMSG(BXE_VERBOSE, bxe_decode_mb_msgs(sc, (command | seq), 0)); BXE_FWMB_LOCK(sc); /* Write the command to the shared memory mailbox. */ SHMEM_WR(sc, func_mb[func].drv_mb_header, (command | seq)); /* Wait up to 2 seconds for a response. */ do { /* Wait 10ms for a response. */ DELAY(10000); /* Pickup the response. */ rc = SHMEM_RD(sc, func_mb[func].fw_mb_header); } while ((seq != (rc & FW_MSG_SEQ_NUMBER_MASK)) && (cnt++ < 400)); DBRUNMSG(BXE_VERBOSE, bxe_decode_mb_msgs(sc, 0, rc)); /* Make sure we read the right response. */ if (seq == (rc & FW_MSG_SEQ_NUMBER_MASK )) rc &= FW_MSG_CODE_MASK; else { BXE_PRINTF("%s(%d): Bootcode failed to respond!\n", __FILE__, __LINE__); DBRUN(bxe_dump_fw(sc)); rc = 0; } BXE_FWMB_UNLOCK(sc); return (rc); } /* * Allocate a block of memory and map it for DMA. No partial * completions allowed, release any resources acquired if we * can't acquire all resources. * * Returns: * 0 = Success, !0 = Failure * * Modifies: * dma->paddr * dma->vaddr * dma->tag * dma->map * dma->size * */ static int bxe_dma_malloc(struct bxe_softc *sc, bus_size_t size, struct bxe_dma *dma, int mapflags, const char *msg) { int rc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); DBRUNIF(dma->size > 0, BXE_PRINTF("%s(): Called for %s with size > 0 (%05d)!\n", __FUNCTION__, msg, (int) dma->size)); rc = bus_dma_tag_create( sc->parent_tag, /* parent */ BCM_PAGE_SIZE, /* alignment for segs */ BXE_DMA_BOUNDARY, /* cannot cross */ BUS_SPACE_MAXADDR, /* restricted low */ BUS_SPACE_MAXADDR, /* restricted hi */ NULL, NULL, /* filter f(), arg */ size, /* max size for this tag */ 1, /* # of discontinuities */ size, /* max seg size */ BUS_DMA_ALLOCNOW, /* flags */ NULL, NULL, /* lock f(), arg */ &dma->tag); if (rc != 0) { BXE_PRINTF("%s(%d): bus_dma_tag_create() " "failed (rc = %d) for %s!\n", __FILE__, __LINE__, rc, msg); goto bxe_dma_malloc_fail_create; } rc = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr, BUS_DMA_NOWAIT, &dma->map); if (rc != 0) { BXE_PRINTF("%s(%d): bus_dmamem_alloc() " "failed (rc = %d) for %s!\n", __FILE__, __LINE__, rc, msg); goto bxe_dma_malloc_fail_alloc; } rc = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size, bxe_dma_map_addr, &dma->paddr, mapflags | BUS_DMA_NOWAIT); if (rc != 0) { BXE_PRINTF("%s(%d): bus_dmamap_load() " "failed (rc = %d) for %s!\n", __FILE__, __LINE__, rc, msg); goto bxe_dma_malloc_fail_load; } dma->size = size; DBPRINT(sc, BXE_VERBOSE, "%s(): size=%06d, vaddr=0x%p, " "paddr=0x%jX - %s\n", __FUNCTION__, (int) dma->size, dma->vaddr, (uintmax_t) dma->paddr, msg); goto bxe_dma_malloc_exit; bxe_dma_malloc_fail_load: bus_dmamem_free(dma->tag, dma->vaddr, dma->map); bxe_dma_malloc_fail_alloc: bus_dma_tag_destroy(dma->tag); dma->vaddr = NULL; bxe_dma_malloc_fail_create: dma->map = NULL; dma->tag = NULL; dma->size = 0; bxe_dma_malloc_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Release a block of DMA memory associated tag/map. * * Returns: * None */ static void bxe_dma_free(struct bxe_softc *sc, struct bxe_dma *dma) { DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_UNLOAD); if (dma->size > 0) { bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(dma->tag, dma->map); bus_dmamem_free(dma->tag, dma->vaddr, dma->map); bus_dma_tag_destroy(dma->tag); dma->size = 0; } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_UNLOAD); } /* * Free any DMA memory owned by the driver. * * Scans through each data structre that requires DMA memory and frees * the memory if allocated. * * Returns: * Nothing. */ static void bxe_host_structures_free(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i, j, max_agg_queues; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); max_agg_queues = CHIP_IS_E1H(sc) ? ETH_MAX_AGGREGATION_QUEUES_E1H : ETH_MAX_AGGREGATION_QUEUES_E1; if (sc->parent_tag == NULL) goto bxe_host_structures_free_exit; for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; /* Trust no one! */ if (fp == NULL) break; /* Status block. */ bxe_dma_free(sc, &fp->sb_dma); /* TX chain. */ bxe_dma_free(sc, &fp->tx_dma); fp->tx_chain = NULL; /* RX chain */ bxe_dma_free(sc, &fp->rx_dma); fp->rx_chain = NULL; /* RCQ chain */ bxe_dma_free(sc, &fp->rcq_dma); fp->rcq_chain = NULL; /* SG chain */ bxe_dma_free(sc, &fp->sg_dma); fp->sg_chain = NULL; /* Unload and destroy the TX mbuf maps. */ if (fp->tx_mbuf_tag != NULL) { for (j = 0; j < TOTAL_TX_BD; j++) { if (fp->tx_mbuf_map[j] != NULL) { bus_dmamap_unload( fp->tx_mbuf_tag, fp->tx_mbuf_map[j]); bus_dmamap_destroy( fp->tx_mbuf_tag, fp->tx_mbuf_map[j]); } } bus_dma_tag_destroy(fp->tx_mbuf_tag); } /* Unload and destroy the TPA pool mbuf maps. */ if (fp->rx_mbuf_tag != NULL) { if (fp->tpa_mbuf_spare_map != NULL) { bus_dmamap_unload( fp->rx_mbuf_tag, fp->tpa_mbuf_spare_map); bus_dmamap_destroy( fp->rx_mbuf_tag, fp->tpa_mbuf_spare_map); } for (j = 0; j < max_agg_queues; j++) { if (fp->tpa_mbuf_map[j] != NULL) { bus_dmamap_unload( fp->rx_mbuf_tag, fp->tpa_mbuf_map[j]); bus_dmamap_destroy( fp->rx_mbuf_tag, fp->tpa_mbuf_map[j]); } } } /* Unload and destroy the SGE Buf maps. */ if (fp->rx_sge_buf_tag != NULL) { if (fp->rx_sge_spare_map != NULL) { bus_dmamap_unload( fp->rx_sge_buf_tag, fp->rx_sge_spare_map); bus_dmamap_destroy( fp->rx_sge_buf_tag, fp->rx_sge_spare_map); } for (j = 0; j < TOTAL_RX_SGE; j++) { if (fp->rx_sge_buf_map[j] != NULL) { bus_dmamap_unload( fp->rx_sge_buf_tag, fp->rx_sge_buf_map[j]); bus_dmamap_destroy( fp->rx_sge_buf_tag, fp->rx_sge_buf_map[j]); } } bus_dma_tag_destroy(fp->rx_sge_buf_tag); } /* Unload and destroy the RX mbuf maps. */ if (fp->rx_mbuf_tag != NULL) { if (fp->rx_mbuf_spare_map != NULL) { bus_dmamap_unload(fp->rx_mbuf_tag, fp->rx_mbuf_spare_map); bus_dmamap_destroy(fp->rx_mbuf_tag, fp->rx_mbuf_spare_map); } for (j = 0; j < TOTAL_RX_BD; j++) { if (fp->rx_mbuf_map[j] != NULL) { bus_dmamap_unload( fp->rx_mbuf_tag, fp->rx_mbuf_map[j]); bus_dmamap_destroy( fp->rx_mbuf_tag, fp->rx_mbuf_map[j]); } } bus_dma_tag_destroy(fp->rx_mbuf_tag); } } /* Destroy the default status block */ bxe_dma_free(sc, &sc->def_sb_dma); sc->def_sb = NULL; /* Destroy the statistics block */ bxe_dma_free(sc, &sc->stats_dma); sc->stats = NULL; /* Destroy the slowpath block. */ bxe_dma_free(sc, &sc->slowpath_dma); sc->slowpath = NULL; /* Destroy the slowpath queue. */ bxe_dma_free(sc, &sc->spq_dma); sc->spq = NULL; /* Destroy the slowpath queue. */ bxe_dma_free(sc, &sc->gz_dma); sc->gz = NULL; free(sc->strm, M_DEVBUF); sc->strm = NULL; bxe_host_structures_free_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } /* * Get DMA memory from the OS. * * Validates that the OS has provided DMA buffers in response to a * bus_dmamap_load call and saves the physical address of those buffers. * When the callback is used the OS will return 0 for the mapping function * (bus_dmamap_load) so we use the value of map_arg->maxsegs to pass any * failures back to the caller. * * Returns: * Nothing. */ static void bxe_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) { bus_addr_t *busaddr; busaddr = arg; /* Check for an error and signal the caller that an error occurred. */ if (error) { printf( "bxe %s(%d): DMA mapping error (error = %d, nseg = %d)!\n", __FILE__, __LINE__, error, nseg); *busaddr = 0; return; } *busaddr = segs->ds_addr; } /* * Allocate any non-paged DMA memory needed by the driver. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_host_structures_alloc(device_t dev) { struct bxe_softc *sc; struct bxe_fastpath *fp; int rc; bus_addr_t busaddr; bus_size_t max_size, max_seg_size; int i, j, max_segments; sc = device_get_softc(dev); DBENTER(BXE_VERBOSE_RESET); rc = 0; int max_agg_queues = CHIP_IS_E1H(sc) ? ETH_MAX_AGGREGATION_QUEUES_E1H : ETH_MAX_AGGREGATION_QUEUES_E1; /* * Allocate the parent bus DMA tag appropriate for PCI. */ rc = bus_dma_tag_create( bus_get_dma_tag(dev), /* PCI parent tag */ 1, /* alignment for segs */ BXE_DMA_BOUNDARY, /* cannot cross */ BUS_SPACE_MAXADDR, /* restricted low */ BUS_SPACE_MAXADDR, /* restricted hi */ NULL, /* filter f() */ NULL, /* filter f() arg */ MAXBSIZE, /* max map for this tag */ BUS_SPACE_UNRESTRICTED, /* # of discontinuities */ BUS_SPACE_MAXSIZE_32BIT, /* max seg size */ 0, /* flags */ NULL, /* lock f() */ NULL, /* lock f() arg */ &sc->parent_tag); /* dma tag */ if (rc != 0) { BXE_PRINTF("%s(%d): Could not allocate parent DMA tag!\n", __FILE__, __LINE__); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } /* Allocate DMA memory for each fastpath structure. */ for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; /* * Allocate status block* */ rc = bxe_dma_malloc(sc, BXE_STATUS_BLK_SZ, &fp->sb_dma, BUS_DMA_NOWAIT, "fp status block"); /* ToDo: Only using 32 bytes out of 4KB allocation! */ if (rc != 0) goto bxe_host_structures_alloc_exit; fp->status_block = (struct host_status_block *) fp->sb_dma.vaddr; /* * Allocate TX chain. */ rc = bxe_dma_malloc(sc, BXE_TX_CHAIN_PAGE_SZ * NUM_TX_PAGES, &fp->tx_dma, BUS_DMA_NOWAIT, "tx chain pages"); if (rc != 0) goto bxe_host_structures_alloc_exit; fp->tx_chain = (union eth_tx_bd_types *) fp->tx_dma.vaddr; /* Link the TX chain pages. */ for (j = 1; j <= NUM_TX_PAGES; j++) { struct eth_tx_next_bd *tx_n_bd = &fp->tx_chain[TOTAL_TX_BD_PER_PAGE * j - 1].next_bd; busaddr = fp->tx_dma.paddr + BCM_PAGE_SIZE * (j % NUM_TX_PAGES); tx_n_bd->addr_hi = htole32(U64_HI(busaddr)); tx_n_bd->addr_lo = htole32(U64_LO(busaddr)); } /* * Allocate RX chain. */ rc = bxe_dma_malloc(sc, BXE_RX_CHAIN_PAGE_SZ * NUM_RX_PAGES, &fp->rx_dma, BUS_DMA_NOWAIT, "rx chain pages"); if (rc != 0) goto bxe_host_structures_alloc_exit; fp->rx_chain = (struct eth_rx_bd *) fp->rx_dma.vaddr; /* Link the RX chain pages. */ for (j = 1; j <= NUM_RX_PAGES; j++) { struct eth_rx_bd *rx_bd = &fp->rx_chain[TOTAL_RX_BD_PER_PAGE * j - 2]; busaddr = fp->rx_dma.paddr + BCM_PAGE_SIZE * (j % NUM_RX_PAGES); rx_bd->addr_hi = htole32(U64_HI(busaddr)); rx_bd->addr_lo = htole32(U64_LO(busaddr)); } /* * Allocate CQ chain. */ rc = bxe_dma_malloc(sc, BXE_RX_CHAIN_PAGE_SZ * NUM_RCQ_PAGES, &fp->rcq_dma, BUS_DMA_NOWAIT, "rcq chain pages"); if (rc != 0) goto bxe_host_structures_alloc_exit; fp->rcq_chain = (union eth_rx_cqe *) fp->rcq_dma.vaddr; /* Link the CQ chain pages. */ for (j = 1; j <= NUM_RCQ_PAGES; j++) { struct eth_rx_cqe_next_page *nextpg = (struct eth_rx_cqe_next_page *) &fp->rcq_chain[TOTAL_RCQ_ENTRIES_PER_PAGE * j - 1]; busaddr = fp->rcq_dma.paddr + BCM_PAGE_SIZE * (j % NUM_RCQ_PAGES); nextpg->addr_hi = htole32(U64_HI(busaddr)); nextpg->addr_lo = htole32(U64_LO(busaddr)); } /* * Allocate SG chain. */ rc = bxe_dma_malloc(sc, BXE_RX_CHAIN_PAGE_SZ * NUM_RX_SGE_PAGES, &fp->sg_dma, BUS_DMA_NOWAIT, "sg chain pages"); if (rc != 0) goto bxe_host_structures_alloc_exit; fp->sg_chain = (struct eth_rx_sge *) fp->sg_dma.vaddr; /* Link the SG chain pages. */ for (j = 1; j <= NUM_RX_SGE_PAGES; j++) { struct eth_rx_sge *nextpg = &fp->sg_chain[TOTAL_RX_SGE_PER_PAGE * j - 2]; busaddr = fp->sg_dma.paddr + BCM_PAGE_SIZE * (j % NUM_RX_SGE_PAGES); nextpg->addr_hi = htole32(U64_HI(busaddr)); nextpg->addr_lo = htole32(U64_LO(busaddr)); } /* * Check required size before mapping to conserve resources. */ if (sc->tso_enable == TRUE) { max_size = BXE_TSO_MAX_SIZE; max_segments = BXE_TSO_MAX_SEGMENTS; max_seg_size = BXE_TSO_MAX_SEG_SIZE; } else { max_size = MCLBYTES * BXE_MAX_SEGMENTS; max_segments = BXE_MAX_SEGMENTS; max_seg_size = MCLBYTES; } /* Create a DMA tag for TX mbufs. */ if (bus_dma_tag_create(sc->parent_tag, 1, /* alignment for segs */ BXE_DMA_BOUNDARY, /* cannot cross */ BUS_SPACE_MAXADDR, /* restricted low */ BUS_SPACE_MAXADDR, /* restricted hi */ NULL, /* filter f() */ NULL, /* filter f() arg */ max_size, /* max map for this tag */ max_segments, /* # of discontinuities */ max_seg_size, /* max seg size */ 0, /* flags */ NULL, /* lock f() */ NULL, /* lock f() arg */ &fp->tx_mbuf_tag)) { BXE_PRINTF( "%s(%d): Could not allocate fp[%d] " "TX mbuf DMA tag!\n", __FILE__, __LINE__, i); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } /* Create DMA maps for each the TX mbuf cluster(ext buf). */ for (j = 0; j < TOTAL_TX_BD; j++) { if (bus_dmamap_create(fp->tx_mbuf_tag, BUS_DMA_NOWAIT, &fp->tx_mbuf_map[j])) { BXE_PRINTF( "%s(%d): Unable to create fp[%02d]." "tx_mbuf_map[%d] DMA map!\n", __FILE__, __LINE__, i, j); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } } /* * Create a DMA tag for RX mbufs. */ if (bus_dma_tag_create(sc->parent_tag, 1, /* alignment for segs */ BXE_DMA_BOUNDARY, /* cannot cross */ BUS_SPACE_MAXADDR, /* restricted low */ BUS_SPACE_MAXADDR, /* restricted hi */ NULL, /* filter f() */ NULL, /* filter f() arg */ MJUM9BYTES, /* max map for this tag */ 1, /* # of discontinuities */ MJUM9BYTES, /* max seg size */ 0, /* flags */ NULL, /* lock f() */ NULL, /* lock f() arg */ &fp->rx_mbuf_tag)) { BXE_PRINTF( "%s(%d): Could not allocate fp[%02d] " "RX mbuf DMA tag!\n", __FILE__, __LINE__, i); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } /* Create DMA maps for the RX mbuf clusters. */ if (bus_dmamap_create(fp->rx_mbuf_tag, BUS_DMA_NOWAIT, &fp->rx_mbuf_spare_map)) { BXE_PRINTF( "%s(%d): Unable to create fp[%02d]." "rx_mbuf_spare_map DMA map!\n", __FILE__, __LINE__, i); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } for (j = 0; j < TOTAL_RX_BD; j++) { if (bus_dmamap_create(fp->rx_mbuf_tag, BUS_DMA_NOWAIT, &fp->rx_mbuf_map[j])) { BXE_PRINTF( "%s(%d): Unable to create fp[%02d]." "rx_mbuf_map[%d] DMA map!\n", __FILE__, __LINE__, i, j); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } } /* * Create a DMA tag for RX SGE bufs. */ if (bus_dma_tag_create(sc->parent_tag, 1, BXE_DMA_BOUNDARY, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, PAGE_SIZE, 1, PAGE_SIZE, 0, NULL, NULL, &fp->rx_sge_buf_tag)) { BXE_PRINTF( "%s(%d): Could not allocate fp[%02d] " "RX SGE mbuf DMA tag!\n", __FILE__, __LINE__, i); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } /* Create DMA maps for the SGE mbuf clusters. */ if (bus_dmamap_create(fp->rx_sge_buf_tag, BUS_DMA_NOWAIT, &fp->rx_sge_spare_map)) { BXE_PRINTF( "%s(%d): Unable to create fp[%02d]." "rx_sge_spare_map DMA map!\n", __FILE__, __LINE__, i); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } for (j = 0; j < TOTAL_RX_SGE; j++) { if (bus_dmamap_create(fp->rx_sge_buf_tag, BUS_DMA_NOWAIT, &fp->rx_sge_buf_map[j])) { BXE_PRINTF( "%s(%d): Unable to create fp[%02d]." "rx_sge_buf_map[%d] DMA map!\n", __FILE__, __LINE__, i, j); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } } /* Create DMA maps for the TPA pool mbufs. */ if (bus_dmamap_create(fp->rx_mbuf_tag, BUS_DMA_NOWAIT, &fp->tpa_mbuf_spare_map)) { BXE_PRINTF( "%s(%d): Unable to create fp[%02d]." "tpa_mbuf_spare_map DMA map!\n", __FILE__, __LINE__, i); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } for (j = 0; j < max_agg_queues; j++) { if (bus_dmamap_create(fp->rx_mbuf_tag, BUS_DMA_NOWAIT, &fp->tpa_mbuf_map[j])) { BXE_PRINTF( "%s(%d): Unable to create fp[%02d]." "tpa_mbuf_map[%d] DMA map!\n", __FILE__, __LINE__, i, j); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } } bxe_init_sge_ring_bit_mask(fp); } /* * Allocate default status block. */ rc = bxe_dma_malloc(sc, BXE_DEF_STATUS_BLK_SZ, &sc->def_sb_dma, BUS_DMA_NOWAIT, "default status block"); if (rc != 0) goto bxe_host_structures_alloc_exit; sc->def_sb = (struct host_def_status_block *) sc->def_sb_dma.vaddr; /* * Allocate statistics block. */ rc = bxe_dma_malloc(sc, BXE_STATS_BLK_SZ, &sc->stats_dma, BUS_DMA_NOWAIT, "statistics block"); if (rc != 0) goto bxe_host_structures_alloc_exit; sc->stats = (struct statistics_block *) sc->stats_dma.vaddr; /* * Allocate slowpath block. */ rc = bxe_dma_malloc(sc, BXE_SLOWPATH_SZ, &sc->slowpath_dma, BUS_DMA_NOWAIT, "slowpath block"); if (rc != 0) goto bxe_host_structures_alloc_exit; sc->slowpath = (struct bxe_slowpath *) sc->slowpath_dma.vaddr; /* * Allocate slowpath queue. */ rc = bxe_dma_malloc(sc, BXE_SPQ_SZ, &sc->spq_dma, BUS_DMA_NOWAIT, "slowpath queue"); if (rc != 0) goto bxe_host_structures_alloc_exit; sc->spq = (struct eth_spe *) sc->spq_dma.vaddr; /* * Allocate firmware decompression buffer. */ rc = bxe_dma_malloc(sc, BXE_FW_BUF_SIZE, &sc->gz_dma, BUS_DMA_NOWAIT, "gunzip buffer"); if (rc != 0) goto bxe_host_structures_alloc_exit; sc->gz = sc->gz_dma.vaddr; if (sc->strm == NULL) { goto bxe_host_structures_alloc_exit; } sc->strm = malloc(sizeof(*sc->strm), M_DEVBUF, M_NOWAIT); bxe_host_structures_alloc_exit: DBEXIT(BXE_VERBOSE_RESET); return (rc); } /* * Program the MAC address for 57710 controllers. * * Returns: * Nothing. */ static void bxe_set_mac_addr_e1(struct bxe_softc *sc, int set) { struct mac_configuration_cmd *config; struct mac_configuration_entry *config_table; uint8_t *eaddr; int port; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); config = BXE_SP(sc, mac_config); port = BP_PORT(sc); /* * CAM allocation: * Port 0 Unicast Addresses: 32 Perfect Match Filters (31-0) * Port 1 Unicast Addresses: 32 Perfect Match Filters (63-32) * Port 0 Multicast Addresses: 128 Hashes (127-64) * Port 1 Multicast Addresses: 128 Hashes (191-128) */ config->hdr.length = 2; config->hdr.offset = port ? 32 : 0; config->hdr.client_id = BP_CL_ID(sc); config->hdr.reserved1 = 0; /* Program the primary MAC address. */ config_table = &config->config_table[0]; eaddr = sc->link_params.mac_addr; config_table->cam_entry.msb_mac_addr = eaddr[0] << 8 | eaddr[1]; config_table->cam_entry.middle_mac_addr = eaddr[2] << 8 | eaddr[3]; config_table->cam_entry.lsb_mac_addr = eaddr[4] << 8 | eaddr[5]; config_table->cam_entry.flags = htole16(port); if (set) config_table->target_table_entry.flags = 0; else CAM_INVALIDATE(config_table); config_table->target_table_entry.vlan_id = 0; DBPRINT(sc, BXE_VERBOSE, "%s(): %s MAC (%04x:%04x:%04x)\n", __FUNCTION__, (set ? "Setting" : "Clearing"), config_table->cam_entry.msb_mac_addr, config_table->cam_entry.middle_mac_addr, config_table->cam_entry.lsb_mac_addr); /* Program the broadcast MAC address. */ config_table = &config->config_table[1]; config_table->cam_entry.msb_mac_addr = 0xffff; config_table->cam_entry.middle_mac_addr = 0xffff; config_table->cam_entry.lsb_mac_addr = 0xffff; config_table->cam_entry.flags = htole16(port); if (set) config_table->target_table_entry.flags = TSTORM_CAM_TARGET_TABLE_ENTRY_BROADCAST; else CAM_INVALIDATE(config_table); config_table->target_table_entry.vlan_id = 0; /* Post the command to slow path queue. */ bxe_sp_post(sc, RAMROD_CMD_ID_ETH_SET_MAC, 0, U64_HI(BXE_SP_MAPPING(sc, mac_config)), U64_LO(BXE_SP_MAPPING(sc, mac_config)), 0); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } /* * Program the MAC address for 57711/57711E controllers. * * Returns: * Nothing. */ static void bxe_set_mac_addr_e1h(struct bxe_softc *sc, int set) { struct mac_configuration_cmd_e1h *config; struct mac_configuration_entry_e1h *config_table; uint8_t *eaddr; int func, port; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); config = (struct mac_configuration_cmd_e1h *)BXE_SP(sc, mac_config); port = BP_PORT(sc); func = BP_FUNC(sc); if (set && (sc->state != BXE_STATE_OPEN)) { DBPRINT(sc, BXE_VERBOSE, "%s(): Can't set E1H MAC in state 0x%08X!\n", __FUNCTION__, sc->state); goto bxe_set_mac_addr_e1h_exit; } /* * CAM allocation: * Function 0-7 Unicast Addresses: 8 Perfect Match Filters * Multicast Addresses: 20 + FUNC * 20, 20 each (???) */ config->hdr.length = 1; config->hdr.offset = func; config->hdr.client_id = 0xff; config->hdr.reserved1 = 0; /* Program the primary MAC address. */ config_table = &config->config_table[0]; eaddr = sc->link_params.mac_addr; config_table->msb_mac_addr = eaddr[0] << 8 | eaddr[1]; config_table->middle_mac_addr = eaddr[2] << 8 | eaddr[3]; config_table->lsb_mac_addr = eaddr[4] << 8 | eaddr[5]; config_table->clients_bit_vector = htole32(1 << sc->fp->cl_id); config_table->vlan_id = 0; config_table->e1hov_id = htole16(sc->e1hov); if (set) config_table->flags = port; else config_table->flags = MAC_CONFIGURATION_ENTRY_E1H_ACTION_TYPE; DBPRINT(sc, BXE_VERBOSE, "%s(): %s MAC (%04x:%04x:%04x), E1HOV = %d, CLID = %d\n", __FUNCTION__, (set ? "Setting" : "Clearing"), config_table->msb_mac_addr, config_table->middle_mac_addr, config_table->lsb_mac_addr, sc->e1hov, BP_L_ID(sc)); bxe_sp_post(sc, RAMROD_CMD_ID_ETH_SET_MAC, 0, U64_HI(BXE_SP_MAPPING(sc, mac_config)), U64_LO(BXE_SP_MAPPING(sc, mac_config)), 0); bxe_set_mac_addr_e1h_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } /* * Programs the various packet receive modes (broadcast and multicast). * * Returns: * Nothing. */ static void bxe_set_rx_mode(struct bxe_softc *sc) { struct ifnet *ifp; struct ifmultiaddr *ifma; struct mac_configuration_cmd *config; struct mac_configuration_entry *config_table; uint32_t mc_filter[MC_HASH_SIZE]; uint8_t *maddr; uint32_t crc, bit, regidx, rx_mode; int i, old, offset, port; BXE_CORE_LOCK_ASSERT(sc); rx_mode = BXE_RX_MODE_NORMAL; port = BP_PORT(sc); DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); if (sc->state != BXE_STATE_OPEN) { DBPRINT(sc, BXE_WARN, "%s(): State (0x%08X) is not open!\n", __FUNCTION__, sc->state); goto bxe_set_rx_mode_exit; } ifp = sc->bxe_ifp; /* * Check for promiscuous, all multicast, or selected * multicast address filtering. */ if (ifp->if_flags & IFF_PROMISC) { /* Enable promiscuous mode. */ rx_mode = BXE_RX_MODE_PROMISC; } else if (ifp->if_flags & IFF_ALLMULTI || ifp->if_amcount > BXE_MAX_MULTICAST) { /* Enable all multicast addresses. */ rx_mode = BXE_RX_MODE_ALLMULTI; } else { /* Enable selective multicast mode. */ if (CHIP_IS_E1(sc)) { i = 0; config = BXE_SP(sc, mcast_config); if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; maddr = (uint8_t *)LLADDR( (struct sockaddr_dl *)ifma->ifma_addr); config_table = &config->config_table[i]; config_table->cam_entry.msb_mac_addr = maddr[0] << 8 | maddr[1]; config_table->cam_entry.middle_mac_addr = maddr[2] << 8 | maddr[3]; config_table->cam_entry.lsb_mac_addr = maddr[4] << 8 | maddr[5]; config_table->cam_entry.flags = htole16(port); config_table->target_table_entry.flags = 0; config_table->target_table_entry. clients_bit_vector = htole32(1 << BP_L_ID(sc)); config_table->target_table_entry.vlan_id = 0; i++; DBPRINT(sc, BXE_INFO, "%s(): Setting MCAST[%d] (%04X:%04X:%04X)\n", __FUNCTION__, i, config_table->cam_entry.msb_mac_addr, config_table->cam_entry.middle_mac_addr, config_table->cam_entry.lsb_mac_addr); } if_maddr_runlock(ifp); old = config->hdr.length; /* Invalidate any extra MC entries in the CAM. */ if (old > i) { for (; i < old; i++) { config_table = &config->config_table[i]; if (CAM_IS_INVALID(config_table)) break; /* Invalidate */ CAM_INVALIDATE(config_table); } } offset = BXE_MAX_MULTICAST * (1 + port); config->hdr.length = i; config->hdr.offset = offset; config->hdr.client_id = sc->fp->cl_id; config->hdr.reserved1 = 0; wmb(); bxe_sp_post(sc, RAMROD_CMD_ID_ETH_SET_MAC, 0, U64_HI(BXE_SP_MAPPING(sc, mcast_config)), U64_LO(BXE_SP_MAPPING(sc, mcast_config)), 0); } else { /* E1H */ /* Accept one or more multicasts */ memset(mc_filter, 0, 4 * MC_HASH_SIZE); if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; crc = ether_crc32_le(ifma->ifma_addr->sa_data, ETHER_ADDR_LEN); bit = (crc >> 24) & 0xff; regidx = bit >> 5; bit &= 0x1f; mc_filter[regidx] |= (1 << bit); } if_maddr_runlock(ifp); for (i = 0; i < MC_HASH_SIZE; i++) REG_WR(sc, MC_HASH_OFFSET(sc, i), mc_filter[i]); } } DBPRINT(sc, BXE_VERBOSE, "%s(): Enabling new receive mode: 0x%08X\n", __FUNCTION__, rx_mode); sc->rx_mode = rx_mode; bxe_set_storm_rx_mode(sc); bxe_set_rx_mode_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Function specific controller reset. * * Returns: * Nothing. */ static void bxe_reset_func(struct bxe_softc *sc) { int base, func, i, port; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); port = BP_PORT(sc); func = BP_FUNC(sc); /* Configure IGU. */ REG_WR(sc, HC_REG_LEADING_EDGE_0 + port * 8, 0); REG_WR(sc, HC_REG_TRAILING_EDGE_0 + port * 8, 0); REG_WR(sc, HC_REG_CONFIG_0 + (port * 4), 0x1000); /* Clear ILT. */ base = FUNC_ILT_BASE(func); for (i = base; i < base + ILT_PER_FUNC; i++) bxe_ilt_wr(sc, i, 0); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } /* * Port specific controller reset. * * Returns: * Nothing. */ static void bxe_reset_port(struct bxe_softc *sc) { uint32_t val; int port; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); port = BP_PORT(sc); REG_WR(sc, NIG_REG_MASK_INTERRUPT_PORT0 + port * 4, 0); /* Do not receive packets to BRB. */ REG_WR(sc, NIG_REG_LLH0_BRB1_DRV_MASK + port * 4, 0x0); /* Do not direct receive packets that are not for MCP to the BRB. */ REG_WR(sc, port ? NIG_REG_LLH1_BRB1_NOT_MCP : NIG_REG_LLH0_BRB1_NOT_MCP, 0x0); /* Configure AEU. */ REG_WR(sc, MISC_REG_AEU_MASK_ATTN_FUNC_0 + port * 4, 0); DELAY(100000); /* Check for BRB port occupancy. */ val = REG_RD(sc, BRB1_REG_PORT_NUM_OCC_BLOCKS_0 + port * 4); if (val) DBPRINT(sc, BXE_VERBOSE, "%s(): BRB1 is not empty (%d blocks are occupied)!\n", __FUNCTION__, val); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } /* * Common controller reset. * * Returns: * Nothing. */ static void bxe_reset_common(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, 0xd3ffff7f); REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, 0x1403); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } /* * Reset the controller. * * Returns: * Nothing. */ static void bxe_reset_chip(struct bxe_softc *sc, uint32_t reset_code) { DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); switch (reset_code) { case FW_MSG_CODE_DRV_UNLOAD_COMMON: bxe_reset_port(sc); bxe_reset_func(sc); bxe_reset_common(sc); break; case FW_MSG_CODE_DRV_UNLOAD_PORT: bxe_reset_port(sc); bxe_reset_func(sc); break; case FW_MSG_CODE_DRV_UNLOAD_FUNCTION: bxe_reset_func(sc); break; default: BXE_PRINTF("%s(%d): Unknown reset code (0x%08X) from MCP!\n", __FILE__, __LINE__, reset_code); break; } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } /* * Called by the OS to set media options (link, speed, etc.) * when the user specifies "ifconfig bxe media XXX" or * "ifconfig bxe mediaopt XXX". * * Returns: * 0 = Success, !0 = Failure */ static int bxe_ifmedia_upd(struct ifnet *ifp) { struct bxe_softc *sc; struct ifmedia *ifm; int rc; sc = ifp->if_softc; DBENTER(BXE_VERBOSE_PHY); ifm = &sc->bxe_ifmedia; rc = 0; /* We only support Ethernet media type. */ if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) { rc = EINVAL; goto bxe_ifmedia_upd_exit; } switch (IFM_SUBTYPE(ifm->ifm_media)) { case IFM_AUTO: /* ToDo: What to do here? */ /* Doing nothing translates to success here. */ break; case IFM_10G_CX4: /* Fall-through */ case IFM_10G_SR: /* Fall-through */ case IFM_10G_T: /* Fall-through */ case IFM_10G_TWINAX: /* Fall-through */ default: /* We don't support channging the media type. */ DBPRINT(sc, BXE_WARN, "%s(): Invalid media type!\n", __FUNCTION__); rc = EINVAL; } bxe_ifmedia_upd_exit: DBENTER(BXE_VERBOSE_PHY); return (rc); } /* * Called by the OS to report current media status * (link, speed, etc.). * * Returns: * Nothing. */ static void bxe_ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr) { struct bxe_softc *sc; sc = ifp->if_softc; DBENTER(BXE_EXTREME_LOAD | BXE_EXTREME_RESET); /* Report link down if the driver isn't running. */ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { ifmr->ifm_active |= IFM_NONE; goto bxe_ifmedia_status_exit; } /* Setup the default interface info. */ ifmr->ifm_status = IFM_AVALID; ifmr->ifm_active = IFM_ETHER; if (sc->link_vars.link_up) ifmr->ifm_status |= IFM_ACTIVE; else { ifmr->ifm_active |= IFM_NONE; goto bxe_ifmedia_status_exit; } ifmr->ifm_active |= sc->media; if (sc->link_vars.duplex == MEDIUM_FULL_DUPLEX) ifmr->ifm_active |= IFM_FDX; else ifmr->ifm_active |= IFM_HDX; bxe_ifmedia_status_exit: DBEXIT(BXE_EXTREME_LOAD | BXE_EXTREME_RESET); } /* * Update last maximum scatter gather entry. * * Returns: * None. */ static __inline void bxe_update_last_max_sge(struct bxe_fastpath *fp, uint16_t index) { uint16_t last_max; last_max = fp->last_max_sge; if (SUB_S16(index, last_max) > 0) fp->last_max_sge = index; } /* * Clear scatter gather mask next elements. * * Returns: * None */ static void bxe_clear_sge_mask_next_elems(struct bxe_fastpath *fp) { int i, index, j; for (i = 0; i < NUM_RX_SGE_PAGES; i++) { index = i * TOTAL_RX_SGE_PER_PAGE + USABLE_RX_SGE_PER_PAGE; for (j = 0; j < 2; j++) { SGE_MASK_CLEAR_BIT(fp, index); index++; } } } /* * Update SGE producer. * * Returns: * None. */ static void bxe_update_sge_prod(struct bxe_fastpath *fp, struct eth_fast_path_rx_cqe *fp_cqe) { struct bxe_softc *sc; uint16_t delta, first_elem, last_max, last_elem, sge_len; int i; sc = fp->sc; DBENTER(BXE_EXTREME_RECV); delta = 0; sge_len = SGE_PAGE_ALIGN(le16toh(fp_cqe->pkt_len) - le16toh(fp_cqe->len_on_bd)) >> SGE_PAGE_SHIFT; if (!sge_len) goto bxe_update_sge_prod_exit; /* First mark all used pages. */ for (i = 0; i < sge_len; i++) SGE_MASK_CLEAR_BIT(fp, RX_SGE(le16toh(fp_cqe->sgl[i]))); /* Assume that the last SGE index is the biggest. */ bxe_update_last_max_sge(fp, le16toh(fp_cqe->sgl[sge_len - 1])); last_max = RX_SGE(fp->last_max_sge); last_elem = last_max >> RX_SGE_MASK_ELEM_SHIFT; first_elem = RX_SGE(fp->rx_sge_prod) >> RX_SGE_MASK_ELEM_SHIFT; /* If ring is not full. */ if (last_elem + 1 != first_elem) last_elem++; /* Now update the producer index. */ for (i = first_elem; i != last_elem; i = NEXT_SGE_MASK_ELEM(i)) { if (fp->rx_sge_mask[i]) break; fp->rx_sge_mask[i] = RX_SGE_MASK_ELEM_ONE_MASK; delta += RX_SGE_MASK_ELEM_SZ; } if (delta > 0) { fp->rx_sge_prod += delta; /* clear page-end entries */ bxe_clear_sge_mask_next_elems(fp); } bxe_update_sge_prod_exit: DBEXIT(BXE_EXTREME_RECV); } /* * Initialize scatter gather ring bitmask. * * Each entry in the SGE is associated with an aggregation in process. * Since there is no guarantee that all Ethernet frames associated with * a partciular TCP flow will arrive at the adapter and be placed into * the SGE chain contiguously, we maintain a bitmask for each SGE element * that identifies which aggregation an Ethernet frame belongs to. * * Returns: * None */ static __inline void bxe_init_sge_ring_bit_mask(struct bxe_fastpath *fp) { /* Set the mask to all 1s, it's faster to compare to 0 than to 0xf. */ memset(fp->rx_sge_mask, 0xff, (TOTAL_RX_SGE >> RX_SGE_MASK_ELEM_SHIFT) * sizeof(uint64_t)); /* * The SGE chain is formatted just like the RX chain. * The last two elements are reserved as a "next page pointer" * to the next page of SGE elements. Clear the last two * elements in each SGE chain page since they will never be * used to track an aggregation. */ bxe_clear_sge_mask_next_elems(fp); } /* * The current mbuf is part of an aggregation. Swap the mbuf into the TPA * aggregation queue, swap an empty mbuf back onto the receive chain, and * mark the current aggregation queue as in-progress. * * Returns: * None. */ static void bxe_tpa_start(struct bxe_fastpath *fp, uint16_t queue, uint16_t cons, uint16_t prod) { struct bxe_softc *sc; struct mbuf *m_temp; struct eth_rx_bd *rx_bd; bus_dmamap_t map_temp; int max_agg_queues; sc = fp->sc; DBENTER(BXE_INSANE_RECV | BXE_INSANE_TPA); DBPRINT(sc, BXE_EXTREME_TPA, "%s(): fp[%02d].tpa[%02d], cons=0x%04X, prod=0x%04X\n", __FUNCTION__, fp->index, queue, cons, prod); max_agg_queues = CHIP_IS_E1(sc) ? ETH_MAX_AGGREGATION_QUEUES_E1 : ETH_MAX_AGGREGATION_QUEUES_E1H; DBRUNIF((queue > max_agg_queues), BXE_PRINTF("%s(): fp[%02d] illegal aggregation (%d > %d)!\n", __FUNCTION__, fp->index, queue, max_agg_queues)); DBRUNIF((fp->tpa_state[queue] != BXE_TPA_STATE_STOP), BXE_PRINTF("%s(): Starting aggregation on " "fp[%02d].tpa[%02d] even though queue is not in the " "TPA_STOP state!\n", __FUNCTION__, fp->index, queue)); /* Remove the existing mbuf and mapping from the TPA pool. */ m_temp = fp->tpa_mbuf_ptr[queue]; map_temp = fp->tpa_mbuf_map[queue]; /* Only the paranoid survive! */ if(m_temp == NULL) { BXE_PRINTF("%s(%d): fp[%02d].tpa[%02d] not allocated!\n", __FILE__, __LINE__, fp->index, queue); /* ToDo: Additional error handling! */ goto bxe_tpa_start_exit; } /* Move received mbuf and mapping to TPA pool. */ fp->tpa_mbuf_ptr[queue] = fp->rx_mbuf_ptr[cons]; fp->tpa_mbuf_map[queue] = fp->rx_mbuf_map[cons]; /* Place the TPA bin into the START state. */ fp->tpa_state[queue] = BXE_TPA_STATE_START; DBRUN(fp->tpa_queue_used |= (1 << queue)); /* Get the rx_bd for the next open entry on the receive chain. */ rx_bd = &fp->rx_chain[prod]; /* Update the rx_bd with the empty mbuf from the TPA pool. */ rx_bd->addr_hi = htole32(U64_HI(fp->tpa_mbuf_segs[queue].ds_addr)); rx_bd->addr_lo = htole32(U64_LO(fp->tpa_mbuf_segs[queue].ds_addr)); fp->rx_mbuf_ptr[prod] = m_temp; fp->rx_mbuf_map[prod] = map_temp; bxe_tpa_start_exit: DBEXIT(BXE_INSANE_RECV | BXE_INSANE_TPA); } /* * When a TPA aggregation is completed, loop through the individual mbufs * of the aggregation, combining them into a single mbuf which will be sent * up the stack. Refill all freed SGEs with mbufs as we go along. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_fill_frag_mbuf(struct bxe_softc *sc, struct bxe_fastpath *fp, struct mbuf *m, struct eth_fast_path_rx_cqe *fp_cqe, uint16_t cqe_idx) { struct mbuf *m_frag; uint32_t frag_len, frag_size, pages, i; uint16_t sge_idx, len_on_bd; int j, rc; DBENTER(BXE_EXTREME_RECV | BXE_EXTREME_TPA); rc = 0; len_on_bd = le16toh(fp_cqe->len_on_bd); frag_size = le16toh(fp_cqe->pkt_len) - len_on_bd; pages = SGE_PAGE_ALIGN(frag_size) >> SGE_PAGE_SHIFT; DBPRINT(sc, BXE_VERBOSE_TPA, "%s(): len_on_bd=%d, frag_size=%d, pages=%d\n", __FUNCTION__, len_on_bd, frag_size, pages); /* Make sure the aggregated frame is not too big to handle. */ if (pages > 8 * PAGES_PER_SGE) { DBPRINT(sc, BXE_FATAL, "%s(): fp[%02d].rx_sge[0x%04X] has too many pages (%d)!\n", __FUNCTION__, fp->index, cqe_idx, pages); DBPRINT(sc, BXE_FATAL, "%s(): fp_cqe->pkt_len = %d fp_cqe->len_on_bd = %d\n", __FUNCTION__, le16toh(fp_cqe->pkt_len), len_on_bd); bxe_panic_dump(sc); rc = EINVAL; goto bxe_fill_frag_mbuf_exit; } /* * Scan through the scatter gather list, pulling individual * mbufs into a single mbuf for the host stack. */ for (i = 0, j = 0; i < pages; i += PAGES_PER_SGE, j++) { sge_idx = RX_SGE(le16toh(fp_cqe->sgl[j])); /* * Firmware gives the indices of the SGE as if the ring is an * array (meaning that the "next" element will consume 2 * indices). */ frag_len = min(frag_size, (uint32_t)(BCM_PAGE_SIZE * PAGES_PER_SGE)); DBPRINT(sc, BXE_VERBOSE_TPA, "%s(): i=%d, j=%d, frag_size=%d, frag_len=%d\n", __FUNCTION__, i, j, frag_size, frag_len); m_frag = fp->rx_sge_buf_ptr[sge_idx]; /* Allocate a new mbuf for the SGE. */ rc = bxe_alloc_rx_sge_mbuf(fp, sge_idx); if (rc) { /* * Leave all remaining SGEs in the ring. */ goto bxe_fill_frag_mbuf_exit; } /* Update the fragment its length. */ m_frag->m_len = frag_len; /* Concatenate the fragment to the head mbuf. */ m_cat(m, m_frag); DBRUN(fp->sge_mbuf_alloc--); /* Update TPA mbuf size and remaining fragment size. */ m->m_pkthdr.len += frag_len; frag_size -= frag_len; } bxe_fill_frag_mbuf_exit: DBPRINT(sc, BXE_VERBOSE_TPA, "%s(): frag_size=%d\n", __FUNCTION__, frag_size); DBEXIT(BXE_EXTREME_RECV | BXE_EXTREME_TPA); return (rc); } /* * The aggregation on the current TPA queue has completed. Pull the * individual mbuf fragments together into a single mbuf, perform all * necessary checksum calculations, and send the resuting mbuf to the stack. * * Returns: * None. */ static void bxe_tpa_stop(struct bxe_softc *sc, struct bxe_fastpath *fp, uint16_t queue, int pad, int len, union eth_rx_cqe *cqe, uint16_t cqe_idx) { struct mbuf *m; struct ifnet *ifp; int rc; DBENTER(BXE_INSANE_RECV | BXE_INSANE_TPA); DBPRINT(sc, (BXE_EXTREME_RECV | BXE_EXTREME_TPA), "%s(): fp[%02d].tpa[%02d], len=%d, pad=%d\n", __FUNCTION__, fp->index, queue, len, pad); rc = 0; ifp = sc->bxe_ifp; m = fp->tpa_mbuf_ptr[queue]; /* Allocate a replacement before modifying existing mbuf. */ rc = bxe_alloc_tpa_mbuf(fp, queue); if (rc) { /* Drop the frame and log a soft error. */ fp->rx_soft_errors++; goto bxe_tpa_stop_exit; } /* We have a replacement, fixup the current mbuf. */ m_adj(m, pad); m->m_pkthdr.len = m->m_len = len; /* Mark the checksums valid (taken care of by firmware). */ m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED | CSUM_IP_VALID | CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; /* Aggregate all of the SGEs into a single mbuf. */ rc = bxe_fill_frag_mbuf(sc, fp, m, &cqe->fast_path_cqe, cqe_idx); if (rc) { /* Drop the packet and log an error. */ fp->rx_soft_errors++; m_freem(m); } else { /* Find VLAN tag and send frame up to the stack. */ if ((le16toh(cqe->fast_path_cqe.pars_flags.flags) & PARSING_FLAGS_VLAN)) { m->m_pkthdr.ether_vtag = cqe->fast_path_cqe.vlan_tag; m->m_flags |= M_VLANTAG; } /* Assign packet to the appropriate interface. */ m->m_pkthdr.rcvif = ifp; /* Update packet statistics. */ fp->rx_tpa_pkts++; ifp->if_ipackets++; /* ToDo: Any potential locking issues here? */ /* Pass the frame to the stack. */ (*ifp->if_input)(ifp, m); } /* We passed mbuf up the stack or dropped the frame. */ DBRUN(fp->tpa_mbuf_alloc--); bxe_tpa_stop_exit: fp->tpa_state[queue] = BXE_TPA_STATE_STOP; DBRUN(fp->tpa_queue_used &= ~(1 << queue)); DBEXIT(BXE_INSANE_RECV | BXE_INSANE_TPA); } /* * Notify the controller that the RX producer indices have been updated for * a fastpath connection by writing them to the controller. * * Returns: * None */ static __inline void bxe_update_rx_prod(struct bxe_softc *sc, struct bxe_fastpath *fp, uint16_t bd_prod, uint16_t cqe_prod, uint16_t sge_prod) { volatile struct ustorm_eth_rx_producers rx_prods = {0}; int i; /* Update producers. */ rx_prods.bd_prod = bd_prod; rx_prods.cqe_prod = cqe_prod; rx_prods.sge_prod = sge_prod; wmb(); for (i = 0; i < sizeof(struct ustorm_eth_rx_producers) / 4; i++){ REG_WR(sc, BAR_USTORM_INTMEM + USTORM_RX_PRODS_OFFSET(BP_PORT(sc), fp->cl_id) + i * 4, ((volatile uint32_t *) &rx_prods)[i]); } DBPRINT(sc, BXE_EXTREME_RECV, "%s(%d): Wrote fp[%02d] bd_prod = 0x%04X, " "cqe_prod = 0x%04X, sge_prod = 0x%04X\n", __FUNCTION__, curcpu, fp->index, bd_prod, cqe_prod, sge_prod); } /* * Processes received frames. * * Returns: * Nothing. */ static void bxe_rxeof(struct bxe_fastpath *fp) { struct bxe_softc *sc; struct ifnet *ifp; uint16_t rx_bd_cons, rx_bd_cons_idx; uint16_t rx_bd_prod, rx_bd_prod_idx; uint16_t rx_cq_cons, rx_cq_cons_idx; uint16_t rx_cq_prod, rx_cq_cons_sb; unsigned long rx_pkts = 0; int rc; sc = fp->sc; ifp = sc->bxe_ifp; DBENTER(BXE_EXTREME_RECV); /* Get the status block's view of the RX completion consumer index. */ rx_cq_cons_sb = bxe_rx_cq_cons(fp); /* * Get working copies of the driver's view of the * RX indices. These are 16 bit values that are * expected to increment from 0 to 65535 and then * wrap-around to 0 again. */ rx_bd_cons = fp->rx_bd_cons; rx_bd_prod = fp->rx_bd_prod; rx_cq_cons = fp->rx_cq_cons; rx_cq_prod = fp->rx_cq_prod; DBPRINT(sc, (BXE_EXTREME_RECV), "%s(%d): BEFORE: fp[%02d], rx_bd_cons = 0x%04X, rx_bd_prod = 0x%04X, " "rx_cq_cons_sw = 0x%04X, rx_cq_prod_sw = 0x%04X\n", __FUNCTION__, curcpu, fp->index, rx_bd_cons, rx_bd_prod, rx_cq_cons, rx_cq_prod); /* * Memory barrier to prevent speculative reads of the RX buffer * from getting ahead of the index in the status block. */ rmb(); /* * Scan through the receive chain as long * as there is work to do. */ while (rx_cq_cons != rx_cq_cons_sb) { struct mbuf *m; union eth_rx_cqe *cqe; uint8_t cqe_fp_flags; uint16_t len, pad; /* * Convert the 16 bit indices used by hardware * into array indices used by the driver. */ rx_cq_cons_idx = RCQ_ENTRY(rx_cq_cons); rx_bd_prod_idx = RX_BD(rx_bd_prod); rx_bd_cons_idx = RX_BD(rx_bd_cons); wmb(); /* Fetch the completion queue entry (i.e. cookie). */ cqe = (union eth_rx_cqe *) &fp->rcq_chain[rx_cq_cons_idx]; cqe_fp_flags = cqe->fast_path_cqe.type_error_flags; /* Sanity check the cookie flags. */ if (__predict_false(cqe_fp_flags == 0)) { fp->rx_null_cqe_flags++; DBRUN(bxe_dump_cqe(fp, rx_cq_cons_idx, cqe)); /* ToDo: What error handling can be done here? */ } /* Check the CQE type for slowpath or fastpath completion. */ if (__predict_false(CQE_TYPE(cqe_fp_flags) == RX_ETH_CQE_TYPE_ETH_RAMROD)) { /* This is a slowpath completion. */ bxe_sp_event(fp, cqe); goto bxe_rxeof_next_cqe; } else { /* This is a fastpath completion. */ /* Get the length and pad information from the CQE. */ len = le16toh(cqe->fast_path_cqe.pkt_len); pad = cqe->fast_path_cqe.placement_offset; /* Check if the completion is for TPA. */ if ((fp->disable_tpa == FALSE) && (TPA_TYPE(cqe_fp_flags) != (TPA_TYPE_START | TPA_TYPE_END))) { uint16_t queue = cqe->fast_path_cqe.queue_index; /* * No need to worry about error flags in * the frame as the firmware has already * managed that for us when aggregating * the frames. */ /* Check if TPA aggregation has started. */ if (TPA_TYPE(cqe_fp_flags) == TPA_TYPE_START) { bxe_tpa_start(fp, queue, rx_bd_cons_idx, rx_bd_prod_idx); goto bxe_rxeof_next_rx; } /* Check if TPA aggregation has completed. */ if (TPA_TYPE(cqe_fp_flags) == TPA_TYPE_END) { DBRUNIF(!BXE_RX_SUM_FIX(cqe), DBPRINT(sc, BXE_FATAL, "%s(): STOP on non-TCP data.\n", __FUNCTION__)); /* * This is the size of the linear * data on this mbuf. */ len = le16toh(cqe->fast_path_cqe.len_on_bd); /* * Stop the aggregation and pass * the frame up. */ bxe_tpa_stop(sc, fp, queue, pad, len, cqe, rx_cq_cons_idx); bxe_update_sge_prod(fp, &cqe->fast_path_cqe); goto bxe_rxeof_next_cqe; } } m = fp->rx_mbuf_ptr[rx_bd_cons_idx]; /* Allocate a replacement before modifying existing mbuf. */ rc = bxe_alloc_rx_bd_mbuf(fp, rx_bd_prod_idx); if (rc) { /* Drop the frame and log a soft error. */ fp->rx_soft_errors++; goto bxe_rxeof_next_rx; } /* Check if the received frame has any errors. */ if (__predict_false(cqe_fp_flags & ETH_RX_ERROR_FLAGS)) { DBPRINT(sc, BXE_WARN , "%s(): fp[%02d].cqe[0x%04X] has errors " "(0x%08X)!\n", __FUNCTION__, fp->index, rx_cq_cons, cqe_fp_flags); fp->rx_soft_errors++; goto bxe_rxeof_next_rx; } /* We have a replacement, fixup the current mbuf. */ m_adj(m, pad); m->m_pkthdr.len = m->m_len = len; /* Assign packet to the appropriate interface. */ m->m_pkthdr.rcvif = ifp; /* Assume no hardware checksum complated. */ m->m_pkthdr.csum_flags = 0; /* Validate checksum if offload enabled. */ if (ifp->if_capenable & IFCAP_RXCSUM) { /* Check whether IP checksummed or not. */ if (sc->rx_csum && !(cqe->fast_path_cqe.status_flags & ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG)) { m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; if (__predict_false(cqe_fp_flags & ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG)) { DBPRINT(sc, BXE_WARN_SEND, "%s(): Invalid IP checksum!\n", __FUNCTION__); } else m->m_pkthdr.csum_flags |= CSUM_IP_VALID; } /* Check for a valid TCP/UDP frame. */ if (sc->rx_csum && !(cqe->fast_path_cqe.status_flags & ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG)) { /* Check for a good TCP/UDP checksum. */ if (__predict_false(cqe_fp_flags & ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG)) { DBPRINT(sc, BXE_VERBOSE_RECV, "%s(): Invalid TCP/UDP checksum!\n", __FUNCTION__); } else { m->m_pkthdr.csum_data = 0xFFFF; m->m_pkthdr.csum_flags |= (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); } } } /* * If we received a packet with a vlan tag, * attach that information to the packet. */ if (cqe->fast_path_cqe.pars_flags.flags & PARSING_FLAGS_VLAN) { m->m_pkthdr.ether_vtag = cqe->fast_path_cqe.vlan_tag; m->m_flags |= M_VLANTAG; } #if __FreeBSD_version >= 800000 /* Tell OS what RSS queue was used for this flow. */ m->m_pkthdr.flowid = fp->index; m->m_flags |= M_FLOWID; #endif /* Last chance to check for problems. */ DBRUN(bxe_validate_rx_packet(fp, rx_cq_cons, cqe, m)); /* Update packet statistics. */ ifp->if_ipackets++; rx_pkts++; /* ToDo: Any potential locking issues here? */ /* Pass the frame to the stack. */ (*ifp->if_input)(ifp, m); DBRUN(fp->rx_mbuf_alloc--); } bxe_rxeof_next_rx: rx_bd_prod = NEXT_RX_BD(rx_bd_prod); rx_bd_cons = NEXT_RX_BD(rx_bd_cons); bxe_rxeof_next_cqe: rx_cq_prod = NEXT_RCQ_IDX(rx_cq_prod); rx_cq_cons = NEXT_RCQ_IDX(rx_cq_cons); /* * Memory barrier to prevent speculative reads of the RX buffer * from getting ahead of the index in the status block. */ rmb(); } /* Update driver copy of the fastpath indices. */ fp->rx_bd_cons = rx_bd_cons; fp->rx_bd_prod = rx_bd_prod; fp->rx_cq_cons = rx_cq_cons; fp->rx_cq_prod = rx_cq_prod; DBPRINT(sc, (BXE_EXTREME_RECV), "%s(%d): AFTER: fp[%02d], rx_bd_cons = 0x%04X, rx_bd_prod = 0x%04X, " "rx_cq_cons_sw = 0x%04X, rx_cq_prod_sw = 0x%04X\n", __FUNCTION__, curcpu, fp->index, rx_bd_cons, rx_bd_prod, rx_cq_cons, rx_cq_prod); /* Update producers */ bxe_update_rx_prod(sc, fp, fp->rx_bd_prod, fp->rx_cq_prod, fp->rx_sge_prod); bus_space_barrier(sc->bxe_btag, sc->bxe_bhandle, 0, 0, BUS_SPACE_BARRIER_READ); fp->rx_pkts += rx_pkts; DBEXIT(BXE_EXTREME_RECV); } /* * Processes transmit completions. * * Returns: * Nothing. */ static void bxe_txeof(struct bxe_fastpath *fp) { struct bxe_softc *sc; struct ifnet *ifp; struct eth_tx_start_bd *txbd; uint16_t hw_pkt_cons, sw_pkt_cons, sw_tx_bd_cons; uint16_t bd_index, pkt_index, nbds; int i; sc = fp->sc; ifp = sc->bxe_ifp; DBENTER(BXE_EXTREME_SEND); /* Get the hardware's view of the TX packet consumer index. */ hw_pkt_cons = le16toh(*fp->tx_pkt_cons_sb); sw_pkt_cons = fp->tx_pkt_cons; sw_tx_bd_cons = fp->tx_bd_cons; /* Cycle through any completed TX chain page entries. */ while (sw_pkt_cons != hw_pkt_cons) { bd_index = TX_BD(sw_tx_bd_cons); pkt_index = TX_BD(sw_pkt_cons); txbd = &fp->tx_chain[bd_index].start_bd; nbds = txbd->nbd; /* Free the completed frame's mbuf. */ if (__predict_true(fp->tx_mbuf_ptr[pkt_index] != NULL)) { /* Unmap the mbuf from non-paged memory. */ bus_dmamap_unload(fp->tx_mbuf_tag, fp->tx_mbuf_map[pkt_index]); /* Return the mbuf to the system. */ m_freem(fp->tx_mbuf_ptr[pkt_index]); fp->tx_mbuf_alloc--; fp->tx_mbuf_ptr[pkt_index] = NULL; fp->opackets++; } else { fp->tx_chain_lost_mbuf++; } /* Updated packet consumer value. */ sw_pkt_cons++; /* Skip over the remaining used buffer descriptors. */ fp->tx_bd_used -= nbds; for (i = 0; i < nbds; i++) sw_tx_bd_cons = NEXT_TX_BD(sw_tx_bd_cons); /* Check for new work since we started. */ hw_pkt_cons = le16toh(*fp->tx_pkt_cons_sb); rmb(); } /* Enable new transmits if we've made enough room. */ if (fp->tx_bd_used < BXE_TX_CLEANUP_THRESHOLD) { ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; if (fp->tx_bd_used == 0) { /* * Clear the watchdog timer if we've emptied * the TX chain. */ fp->watchdog_timer = 0; } else { /* * Reset the watchdog timer if we still have * transmits pending. */ fp->watchdog_timer = BXE_TX_TIMEOUT; } } /* Save our indices. */ fp->tx_pkt_cons = sw_pkt_cons; fp->tx_bd_cons = sw_tx_bd_cons; DBEXIT(BXE_EXTREME_SEND); } /* * Transmit timeout handler. * * Returns: * 0 = No timeout, !0 = timeout occurred. */ static int bxe_watchdog(struct bxe_fastpath *fp) { struct bxe_softc *sc; int rc = 0; sc = fp->sc; DBENTER(BXE_INSANE_SEND); BXE_FP_LOCK(fp); if (fp->watchdog_timer == 0 || --fp->watchdog_timer) { rc = EINVAL; BXE_FP_UNLOCK(fp); goto bxe_watchdog_exit; } BXE_FP_UNLOCK(fp); BXE_PRINTF("TX watchdog timeout occurred on fp[%02d], " "resetting!\n", fp->index); /* DBRUNLV(BXE_FATAL, bxe_breakpoint(sc)); */ BXE_CORE_LOCK(sc); /* Mark the interface as down. */ sc->bxe_ifp->if_drv_flags &= ~IFF_DRV_RUNNING; bxe_stop_locked(sc, UNLOAD_NORMAL); DELAY(10000); bxe_init_locked(sc, LOAD_OPEN); BXE_CORE_UNLOCK(sc); bxe_watchdog_exit: DBEXIT(BXE_INSANE_SEND); return (rc); } /* * The periodic timer tick routine. * * This code only runs when the interface is up. * * Returns: * None */ static void bxe_tick(void *xsc) { struct bxe_softc *sc; struct bxe_fastpath *fp; #if 0 /* Re-enable at a later time. */ uint32_t drv_pulse, mcp_pulse; #endif int i, func; sc = xsc; DBENTER(BXE_INSANE_MISC); /* Check for TX timeouts on any fastpath. */ for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; if (bxe_watchdog(fp) != 0) break; } func = BP_FUNC(sc); /* Schedule the next tick. */ callout_reset(&sc->bxe_tick_callout, hz, bxe_tick, sc); #if 0 if (!NOMCP(sc)) { func = BP_FUNC(sc); ++sc->fw_drv_pulse_wr_seq; sc->fw_drv_pulse_wr_seq &= DRV_PULSE_SEQ_MASK; /* Let the MCP know we're alive. */ drv_pulse = sc->fw_drv_pulse_wr_seq; SHMEM_WR(sc, func_mb[func].drv_pulse_mb, drv_pulse); /* Check if the MCP is still alive. */ mcp_pulse = (SHMEM_RD(sc, func_mb[func].mcp_pulse_mb) & MCP_PULSE_SEQ_MASK); /* * The delta between driver pulse and MCP response should be 1 * (before MCP response) or 0 (after MCP response). */ if ((drv_pulse != mcp_pulse) && (drv_pulse != ((mcp_pulse + 1) & MCP_PULSE_SEQ_MASK))) { /* Someone's in cardiac arrest. */ DBPRINT(sc, BXE_WARN, "%s(): drv_pulse (0x%x) != mcp_pulse (0x%x)\n", __FUNCTION__, drv_pulse, mcp_pulse); } } #endif if ((sc->state == BXE_STATE_OPEN) || (sc->state == BXE_STATE_DISABLED)) bxe_stats_handle(sc, STATS_EVENT_UPDATE); } #ifdef BXE_DEBUG /* * Allows the driver state to be dumped through the sysctl interface. * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_driver_state(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; struct bxe_fastpath *fp; int error, i, result; sc = (struct bxe_softc *)arg1; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if (result == 1) { bxe_dump_driver_state(sc); for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; bxe_dump_fp_state(fp); } bxe_dump_status_block(sc); } return (error); } /* * Allows the hardware state to be dumped through the sysctl interface. * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_hw_state(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; int error, result; sc = (struct bxe_softc *)arg1; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if (result == 1) bxe_dump_hw_state(sc); return (error); } /* * Allows the MCP firmware to be dumped through the sysctl interface. * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_dump_fw(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; int error, result; sc = (struct bxe_softc *)arg1; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if (result == 1) bxe_dump_fw(sc); return (error); } /* * Provides a sysctl interface to allow dumping the RX completion chain. * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_dump_rx_cq_chain(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; struct bxe_fastpath *fp; int error, result; sc = (struct bxe_softc *)arg1; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if ((result >= 0) && (result < sc->num_queues)) { fp = &sc->fp[result]; bxe_dump_rx_cq_chain(fp, 0, TOTAL_RCQ_ENTRIES); } return (error); } /* * Provides a sysctl interface to allow dumping the RX chain. * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_dump_rx_bd_chain(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; struct bxe_fastpath *fp; int error, result; sc = (struct bxe_softc *)arg1; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if ((result >= 0) && (result < sc->num_queues)) { fp = &sc->fp[result]; bxe_dump_rx_bd_chain(fp, 0, TOTAL_RX_BD); } return (error); } /* * Provides a sysctl interface to allow dumping the TX chain. * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_dump_tx_chain(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; struct bxe_fastpath *fp; int error, result; sc = (struct bxe_softc *)arg1; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if ((result >= 0) && (result < sc->num_queues)) { fp = &sc->fp[result]; bxe_dump_tx_chain(fp, 0, TOTAL_TX_BD); } return (error); } /* * Provides a sysctl interface to allow reading arbitrary registers in the * device. DO NOT ENABLE ON PRODUCTION SYSTEMS! * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_reg_read(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; uint32_t result, val; int error; sc = (struct bxe_softc *)arg1; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || (req->newptr == NULL)) return (error); val = REG_RD(sc, result); BXE_PRINTF("reg 0x%08X = 0x%08X\n", result, val); return (error); } /* * Provides a sysctl interface to allow generating a grcdump. * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_grcdump(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; int error, result; sc = (struct bxe_softc *)arg1; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if (result == 1) { /* Generate a grcdump and log the contents.*/ bxe_grcdump(sc, 1); } else { /* Generate a grcdump and don't log the contents. */ bxe_grcdump(sc, 0); } return (error); } /* * Provides a sysctl interface to forcing the driver to dump state and * enter the debugger. DO NOT ENABLE ON PRODUCTION SYSTEMS! * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_breakpoint(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; int error, result; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if (result == 1) { sc = (struct bxe_softc *)arg1; bxe_breakpoint(sc); } return (error); } #endif /* * Adds any sysctl parameters for tuning or debugging purposes. * * Returns: * None. */ static void bxe_add_sysctls(struct bxe_softc *sc) { struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev); struct sysctl_oid_list *children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); struct bxe_port_stats *estats = &sc->eth_stats; SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_bytes_received_hi", CTLFLAG_RD, &estats->total_bytes_received_hi, 0, "Total bytes received (hi)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_bytes_received_lo", CTLFLAG_RD, &estats->total_bytes_received_lo, 0, "Total bytes received (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_valid_bytes_received_hi", CTLFLAG_RD, &estats->valid_bytes_received_hi, 0, "Valid bytes received (hi)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_valid_bytes_received_lo", CTLFLAG_RD, &estats->valid_bytes_received_lo, 0, "Valid bytes received (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_unicast_packets_received_hi", CTLFLAG_RD, &estats->total_unicast_packets_received_hi, 0, "Total unicast packets received (hi)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_unicast_packets_received_lo", CTLFLAG_RD, &estats->total_unicast_packets_received_lo, 0, "Total unicast packets received (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_bytes_transmitted_hi", CTLFLAG_RD, &estats->total_bytes_transmitted_hi, 0, "Total bytes transmitted (hi)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_bytes_transmitted_lo", CTLFLAG_RD, &estats->total_bytes_transmitted_lo, 0, "Total bytes transmitted (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_unicast_packets_transmitted_hi", CTLFLAG_RD, &estats->total_unicast_packets_transmitted_hi, 0, "Total unicast packets transmitted (hi)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_unicast_packets_transmitted_lo", CTLFLAG_RD, &estats->total_unicast_packets_transmitted_lo, 0, "Total unicast packets transmitted (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_broadcast_packets_received_lo", CTLFLAG_RD, &estats->total_broadcast_packets_received_lo, 0, "Total broadcast packets received (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_broadcast_packets_transmitted_lo", CTLFLAG_RD, &estats->total_broadcast_packets_transmitted_lo, 0, "Total broadcast packets transmitted (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_multicast_packets_received_lo", CTLFLAG_RD, &estats->total_multicast_packets_received_lo, 0, "Total multicast packets received (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_multicast_packets_transmitted_lo", CTLFLAG_RD, &estats->total_multicast_packets_transmitted_lo, 0, "Total multicast packets transmitted (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "tx_stat_etherstatspkts64octets_hi", CTLFLAG_RD, &estats->tx_stat_etherstatspkts64octets_hi, 0, "Total 64 byte packets transmitted (hi)"); /* ToDo: Fix for 64 bit access. */ SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "tx_stat_etherstatspkts64octets_lo", CTLFLAG_RD, &estats->tx_stat_etherstatspkts64octets_lo, 0, "Total 64 byte packets transmitted (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "driver_xoff", CTLFLAG_RD, &estats->driver_xoff, 0, "Driver transmit queue full count"); SYSCTL_ADD_ULONG(ctx, children, OID_AUTO, "tx_start_called_with_link_down", CTLFLAG_RD, &sc->tx_start_called_with_link_down, "TX start routine called while link down count"); SYSCTL_ADD_ULONG(ctx, children, OID_AUTO, "tx_start_called_with_queue_full", CTLFLAG_RD, &sc->tx_start_called_with_queue_full, "TX start routine called with queue full count"); /* ToDo: Add more statistics here. */ #ifdef BXE_DEBUG SYSCTL_ADD_INT(ctx, children, OID_AUTO, "bxe_debug", CTLFLAG_RW, &bxe_debug, 0, "Debug message level flag"); #endif do { #define QUEUE_NAME_LEN 32 char namebuf[QUEUE_NAME_LEN]; struct sysctl_oid *queue_node; struct sysctl_oid_list *queue_list; for (int i = 0; i < sc->num_queues; i++) { struct bxe_fastpath *fp = &sc->fp[i]; snprintf(namebuf, QUEUE_NAME_LEN, "fp[%02d]", i); queue_node = SYSCTL_ADD_NODE(ctx, children, OID_AUTO, namebuf, CTLFLAG_RD, NULL, "Queue Name"); queue_list = SYSCTL_CHILDREN(queue_node); /* * Receive related fastpath statistics.* */ SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "rx_pkts", CTLFLAG_RD, &fp->rx_pkts, "Received packets"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "rx_tpa_pkts", CTLFLAG_RD, &fp->rx_tpa_pkts, "Received TPA packets"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "rx_null_cqe_flags", CTLFLAG_RD, &fp->rx_null_cqe_flags, "CQEs with NULL flags count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "rx_soft_errors", CTLFLAG_RD, &fp->rx_soft_errors, "Received frames dropped by driver count"); /* * Transmit related fastpath statistics.* */ SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_pkts", CTLFLAG_RD, &fp->tx_pkts, "Transmitted packets"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_soft_errors", CTLFLAG_RD, &fp->tx_soft_errors, "Transmit frames dropped by driver count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_offload_frames_csum_ip", CTLFLAG_RD, &fp->tx_offload_frames_csum_ip, "IP checksum offload frame count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_offload_frames_csum_tcp", CTLFLAG_RD, &fp->tx_offload_frames_csum_tcp, "TCP checksum offload frame count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_offload_frames_csum_udp", CTLFLAG_RD, &fp->tx_offload_frames_csum_udp, "UDP checksum offload frame count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_offload_frames_tso", CTLFLAG_RD, &fp->tx_offload_frames_tso, "TSO offload frame count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_header_splits", CTLFLAG_RD, &fp->tx_header_splits, "TSO frame header/data split count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_encap_failures", CTLFLAG_RD, &fp->tx_encap_failures, "TX encapsulation failure count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_hw_queue_full", CTLFLAG_RD, &fp->tx_hw_queue_full, "TX H/W queue too full to add a frame count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_hw_max_queue_depth", CTLFLAG_RD, &fp->tx_hw_max_queue_depth, "TX H/W maximum queue depth count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_dma_mapping_failure", CTLFLAG_RD, &fp->tx_dma_mapping_failure, "TX DMA mapping failure"); SYSCTL_ADD_INT(ctx, queue_list, OID_AUTO, "tx_max_drbr_queue_depth", CTLFLAG_RD, &fp->tx_max_drbr_queue_depth, 0, "TX S/W queue maximum depth"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_window_violation_std", CTLFLAG_RD, &fp->tx_window_violation_std, "Standard frame TX BD window violation count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_window_violation_tso", CTLFLAG_RD, &fp->tx_window_violation_tso, "TSO frame TX BD window violation count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_unsupported_tso_request_ipv6", CTLFLAG_RD, &fp->tx_unsupported_tso_request_ipv6, "TSO frames with unsupported IPv6 protocol count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_unsupported_tso_request_not_tcp", CTLFLAG_RD, &fp->tx_unsupported_tso_request_not_tcp, "TSO frames with unsupported protocol count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_chain_lost_mbuf", CTLFLAG_RD, &fp->tx_chain_lost_mbuf, "Mbufs lost on TX chain count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_frame_deferred", CTLFLAG_RD, &fp->tx_frame_deferred, "TX frame deferred from H/W queue to S/W queue count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_queue_xoff", CTLFLAG_RD, &fp->tx_queue_xoff, "TX queue full count"); /* * Memory related fastpath statistics.* */ SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "mbuf_rx_bd_alloc_failed", CTLFLAG_RD, &fp->mbuf_rx_bd_alloc_failed, "RX BD mbuf allocation failure count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "mbuf_rx_bd_mapping_failed", CTLFLAG_RD, &fp->mbuf_rx_bd_mapping_failed, "RX BD mbuf mapping failure count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "mbuf_tpa_alloc_failed", CTLFLAG_RD, &fp->mbuf_tpa_alloc_failed, "TPA mbuf allocation failure count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "mbuf_tpa_mapping_failed", CTLFLAG_RD, &fp->mbuf_tpa_mapping_failed, "TPA mbuf mapping failure count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "mbuf_sge_alloc_failed", CTLFLAG_RD, &fp->mbuf_sge_alloc_failed, "SGE mbuf allocation failure count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "mbuf_sge_mapping_failed", CTLFLAG_RD, &fp->mbuf_sge_mapping_failed, "SGE mbuf mapping failure count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "mbuf_defrag_attempts", CTLFLAG_RD, &fp->mbuf_defrag_attempts, "Mbuf defrag attempt count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "mbuf_defrag_failures", CTLFLAG_RD, &fp->mbuf_defrag_failures, "Mbuf defrag failure count"); } } while (0); #ifdef BXE_DEBUG SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "driver_state", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_driver_state, "I", "Drive state information"); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "hw_state", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_hw_state, "I", "Hardware state information"); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dump_fw", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_dump_fw, "I", "Dump MCP firmware"); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dump_rx_bd_chain", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_dump_rx_bd_chain, "I", "Dump rx_bd chain"); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dump_rx_cq_chain", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_dump_rx_cq_chain, "I", "Dump cqe chain"); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dump_tx_chain", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_dump_tx_chain, "I", "Dump tx_bd chain"); /* * Generates a GRCdump (run sysctl dev.bxe.0.grcdump=0 * before accessing buffer below). */ SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "grcdump", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_grcdump, "I", "Initiate a grcdump operation"); /* * Hidden sysctl. * Use "sysctl -b dev.bxe.0.grcdump_buffer > buf.bin". */ SYSCTL_ADD_OPAQUE(ctx, children, OID_AUTO, "grcdump_buffer", CTLFLAG_RD | CTLFLAG_SKIP, sc->grcdump_buffer, BXE_GRCDUMP_BUF_SIZE, "IU", "Access grcdump buffer"); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "breakpoint", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_breakpoint, "I", "Driver breakpoint"); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "reg_read", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_reg_read, "I", "Register read"); #endif /* BXE_DEBUG */ } /* * BXE Debug Routines */ #ifdef BXE_DEBUG /* * Writes out the header for the debug dump buffer. * * Returns: * None. * * Modifies: * index */ static void bxe_dump_debug_header(struct bxe_softc *sc, uint32_t *index) { struct hd_param hd_param_cu = {0}; uint32_t *buf; buf = sc->grcdump_buffer; if (CHIP_IS_E1H(sc)) hd_param_cu = hd_param_e1h; else hd_param_cu = hd_param_e1; buf[(*index)++] = hd_param_cu.time_stamp; buf[(*index)++] = hd_param_cu.diag_ver; buf[(*index)++] = hd_param_cu.grc_dump_ver; buf[(*index)++] = REG_RD_IND(sc, XSTORM_WAITP_ADDRESS); buf[(*index)++] = REG_RD_IND(sc, TSTORM_WAITP_ADDRESS); buf[(*index)++] = REG_RD_IND(sc, USTORM_WAITP_ADDRESS); buf[(*index)++] = REG_RD_IND(sc, CSTORM_WAITP_ADDRESS); /* The size of the header is stored at the first DWORD. */ buf[0] = (*index) - 1; } /* * Writes to the controller to prepare it for a dump. * * Returns: * None. * * Modifies: * None. */ static void bxe_dump_debug_writes(struct bxe_softc *sc) { uint32_t write_val; write_val = 1; /* Halt the STORMs to get a consistent device state. */ REG_WR_IND(sc, XSTORM_WAITP_ADDRESS, write_val); REG_WR_IND(sc, TSTORM_WAITP_ADDRESS, write_val); REG_WR_IND(sc, USTORM_WAITP_ADDRESS, write_val); REG_WR_IND(sc, CSTORM_WAITP_ADDRESS, write_val); if (CHIP_IS_E1H(sc)) REG_WR_IND(sc, TSTORM_CAM_MODE, write_val); } /* * Cycles through the required register reads and dumps them * to the debug buffer. * * Returns: * None. * * Modifies: * index */ static void bxe_dump_debug_reg_read(struct bxe_softc *sc, uint32_t *index) { preg_addr preg_addrs; uint32_t regs_count, *buf; uint32_t i, reg_addrs_index; buf = sc->grcdump_buffer; preg_addrs = NULL; /* Read different registers for different controllers. */ if (CHIP_IS_E1H(sc)) { regs_count = regs_count_e1h; preg_addrs = ®_addrs_e1h[0]; } else { regs_count = regs_count_e1; preg_addrs = ®_addrs_e1[0]; } /* ToDo: Add a buffer size check. */ for (reg_addrs_index = 0; reg_addrs_index < regs_count; reg_addrs_index++) { for (i = 0; i < preg_addrs[reg_addrs_index].size; i++) { buf[(*index)++] = REG_RD_IND(sc, preg_addrs[reg_addrs_index].addr + (i * 4)); } } } /* * Cycles through the required wide register reads and dumps them * to the debug buffer. * * Returns: * None. */ static void bxe_dump_debug_reg_wread(struct bxe_softc *sc, uint32_t *index) { pwreg_addr pwreg_addrs; uint32_t reg_addrs_index, reg_add_read, reg_add_count; uint32_t *buf, cam_index, wregs_count; buf = sc->grcdump_buffer; pwreg_addrs = NULL; /* Read different registers for different controllers. */ if (CHIP_IS_E1H(sc)) { wregs_count = wregs_count_e1h; pwreg_addrs = &wreg_addrs_e1h[0]; } else { wregs_count = wregs_count_e1; pwreg_addrs = &wreg_addrs_e1[0]; } for (reg_addrs_index = 0; reg_addrs_index < wregs_count; reg_addrs_index++) { reg_add_read = pwreg_addrs[reg_addrs_index].addr; for (reg_add_count = 0; reg_add_count < pwreg_addrs[reg_addrs_index].size; reg_add_count++) { buf[(*index)++] = REG_RD_IND(sc, reg_add_read); reg_add_read += sizeof(uint32_t); for (cam_index = 0; cam_index < pwreg_addrs[reg_addrs_index].const_regs_count; cam_index++) buf[(*index)++] = REG_RD_IND(sc, pwreg_addrs[reg_addrs_index].const_regs[cam_index]); } } } /* * Performs a debug dump for offline diagnostics. * * Note that when this routine is called the STORM * processors will be stopped in order to create a * cohesive dump. The controller will need to be * reset before the device can begin passing traffic * again. * * Returns: * None. */ static void bxe_grcdump(struct bxe_softc *sc, int log) { uint32_t *buf, i, index; index = 1; buf = sc->grcdump_buffer; if (buf != NULL) { /* Write the header and regsiters contents to the dump buffer. */ bxe_dump_debug_header(sc, &index); bxe_dump_debug_writes(sc); bxe_dump_debug_reg_read(sc,&index); bxe_dump_debug_reg_wread(sc, &index); /* Print the results to the system log is necessary. */ if (log) { BXE_PRINTF( "-----------------------------" " grcdump " "-----------------------------\n"); BXE_PRINTF("Buffer length = 0x%08X bytes\n", index * 4); for (i = 0; i < index; i += 8) { BXE_PRINTF( "0x%08X - 0x%08X 0x%08X 0x%08X 0x%08X " "0x%08X 0x%08X 0x%08X 0x%08X\n", i * 4, buf[i + 0], buf[i + 1], buf[i + 2], buf[i + 3], buf[i + 4], buf[i + 5], buf[i + 6], buf[i + 7]); } BXE_PRINTF( "-----------------------------" "--------------" "-----------------------------\n"); } } else { BXE_PRINTF("No grcdump buffer allocated!\n"); } } /* * Check that an Etherent frame is valid and prints out debug info if it's * not. * * Returns: * Nothing. */ static __noinline void bxe_validate_rx_packet(struct bxe_fastpath *fp, uint16_t comp_cons, union eth_rx_cqe *cqe, struct mbuf *m) { struct bxe_softc *sc; int error; sc = fp->sc; /* Check that the mbuf is sane. */ error = m_sanity(m, FALSE); if (error != 1 || ((m->m_len < ETHER_HDR_LEN) | (m->m_len > ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD))) { m_print(m, 128); bxe_dump_enet(sc, m); bxe_dump_cqe(fp, comp_cons, cqe); /* Make sure the packet has a valid length. */ } } /* * Prints out Ethernet frame information from an mbuf. * * Partially decode an Ethernet frame to look at some important headers. * * Returns: * Nothing. */ static __noinline void bxe_dump_enet(struct bxe_softc *sc, struct mbuf *m) { struct ether_vlan_header *eh; uint16_t etype; int e_hlen; struct ip *ip; struct tcphdr *th; struct udphdr *uh; struct arphdr *ah; BXE_PRINTF( "-----------------------------" " Frame Decode " "-----------------------------\n"); eh = mtod(m, struct ether_vlan_header *); /* Handle VLAN encapsulation if present. */ if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { etype = ntohs(eh->evl_proto); e_hlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; } else { etype = ntohs(eh->evl_encap_proto); e_hlen = ETHER_HDR_LEN; } BXE_PRINTF("enet: dest = %6D, src = %6D, type = 0x%04X, e_hlen = %d\n", eh->evl_dhost, ":", eh->evl_shost, ":", etype, e_hlen); switch (etype) { case ETHERTYPE_IP: ip = (struct ip *)(m->m_data + e_hlen); BXE_PRINTF( "--ip: dest = 0x%08X , src = 0x%08X, " "ip_hlen = %d bytes, len = %d bytes, protocol = 0x%02X, " "ip_id = 0x%04X, csum = 0x%04X\n", ntohl(ip->ip_dst.s_addr), ntohl(ip->ip_src.s_addr), (ip->ip_hl << 2), ntohs(ip->ip_len), ip->ip_p, ntohs(ip->ip_id), ntohs(ip->ip_sum)); switch (ip->ip_p) { case IPPROTO_TCP: th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2)); BXE_PRINTF( "-tcp: dest = %d, src = %d, tcp_hlen = %d " "bytes, flags = 0x%b, csum = 0x%04X\n", ntohs(th->th_dport), ntohs(th->th_sport), (th->th_off << 2), th->th_flags, "\20\10CWR\07ECE\06URG\05ACK\04PSH\03RST\02SYN\01FIN", ntohs(th->th_sum)); break; case IPPROTO_UDP: uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2)); BXE_PRINTF( "-udp: dest = %d, src = %d, udp_hlen = %d " "bytes, len = %d bytes, csum = 0x%04X\n", ntohs(uh->uh_dport), ntohs(uh->uh_sport), (int)sizeof(struct udphdr), ntohs(uh->uh_ulen), ntohs(uh->uh_sum)); break; case IPPROTO_ICMP: BXE_PRINTF("icmp:\n"); break; default: BXE_PRINTF("----: Other IP protocol.\n"); } break; case ETHERTYPE_IPV6: /* ToDo: Add IPv6 support. */ BXE_PRINTF("IPv6 not supported!.\n"); break; case ETHERTYPE_ARP: BXE_PRINTF("-arp: "); ah = (struct arphdr *) (m->m_data + e_hlen); switch (ntohs(ah->ar_op)) { case ARPOP_REVREQUEST: printf("reverse ARP request\n"); break; case ARPOP_REVREPLY: printf("reverse ARP reply\n"); break; case ARPOP_REQUEST: printf("ARP request\n"); break; case ARPOP_REPLY: printf("ARP reply\n"); break; default: printf("other ARP operation\n"); } break; default: BXE_PRINTF("----: Other protocol.\n"); } BXE_PRINTF( "-----------------------------" "--------------" "-----------------------------\n"); } #if 0 static void bxe_dump_mbuf_data(struct mbuf *m, int len) { uint8_t *ptr; int i; ptr = mtod(m, uint8_t *); printf("\nmbuf->m_data:"); printf("\n0x"); for (i = 0; i < len; i++){ if (i != 0 && i % 40 == 0) printf("\n0x"); else if (i != 0 && i % 6 == 0) printf(" 0x"); printf("%02x", *ptr++); } printf("\n\n"); } #endif /* * Prints out information about an mbuf. * * Returns: * Nothing. */ static __noinline void bxe_dump_mbuf(struct bxe_softc *sc, struct mbuf *m) { if (m == NULL) { BXE_PRINTF("mbuf: null pointer\n"); return; } while (m) { BXE_PRINTF("mbuf: %p, m_len = %d, m_flags = 0x%b, " "m_data = %p\n", m, m->m_len, m->m_flags, "\20\1M_EXT\2M_PKTHDR\3M_EOR\4M_RDONLY", m->m_data); if (m->m_flags & M_PKTHDR) { BXE_PRINTF("- m_pkthdr: len = %d, flags = 0x%b, " "csum_flags = %b\n", m->m_pkthdr.len, m->m_flags, "\20\12M_BCAST\13M_MCAST\14M_FRAG" "\15M_FIRSTFRAG\16M_LASTFRAG\21M_VLANTAG" "\22M_PROMISC\23M_NOFREE", m->m_pkthdr.csum_flags, "\20\1CSUM_IP\2CSUM_TCP\3CSUM_UDP\4CSUM_IP_FRAGS" "\5CSUM_FRAGMENT\6CSUM_TSO\11CSUM_IP_CHECKED" "\12CSUM_IP_VALID\13CSUM_DATA_VALID" "\14CSUM_PSEUDO_HDR"); } if (m->m_flags & M_EXT) { BXE_PRINTF("- m_ext: %p, ext_size = %d, type = ", m->m_ext.ext_buf, m->m_ext.ext_size); switch (m->m_ext.ext_type) { case EXT_CLUSTER: printf("EXT_CLUSTER\n"); break; case EXT_SFBUF: printf("EXT_SFBUF\n"); break; case EXT_JUMBO9: printf("EXT_JUMBO9\n"); break; case EXT_JUMBO16: printf("EXT_JUMBO16\n"); break; case EXT_PACKET: printf("EXT_PACKET\n"); break; case EXT_MBUF: printf("EXT_MBUF\n"); break; case EXT_NET_DRV: printf("EXT_NET_DRV\n"); break; case EXT_MOD_TYPE: printf("EXT_MOD_TYPE\n"); break; case EXT_DISPOSABLE: printf("EXT_DISPOSABLE\n"); break; case EXT_EXTREF: printf("EXT_EXTREF\n"); break; default: printf("UNKNOWN\n"); } } m = m->m_next; } } /* * Prints out information about an rx_bd. * * Returns: * Nothing. */ static __noinline void bxe_dump_rxbd(struct bxe_fastpath *fp, int idx, struct eth_rx_bd *rx_bd) { struct bxe_softc *sc; sc = fp->sc; /* Check if index out of range. */ if (idx > MAX_RX_BD) { BXE_PRINTF("fp[%02d].rx_bd[0x%04X] XX: Invalid rx_bd index!\n", fp->index, idx); } else if ((idx & RX_BD_PER_PAGE_MASK) >= USABLE_RX_BD_PER_PAGE) { /* RX Chain page pointer. */ BXE_PRINTF("fp[%02d].rx_bd[0x%04X] NP: haddr=0x%08X:%08X\n", fp->index, idx, rx_bd->addr_hi, rx_bd->addr_lo); } else { BXE_PRINTF("fp[%02d].rx_bd[0x%04X] RX: haddr=0x%08X:%08X\n", fp->index, idx, rx_bd->addr_hi, rx_bd->addr_lo); } } /* * Prints out a completion queue entry. * * Returns: * Nothing. */ static __noinline void bxe_dump_cqe(struct bxe_fastpath *fp, int idx, union eth_rx_cqe *cqe) { struct bxe_softc *sc; sc = fp->sc; if (idx > MAX_RCQ_ENTRIES) { /* Index out of range. */ BXE_PRINTF("fp[%02d].rx_cqe[0x%04X]: Invalid rx_cqe index!\n", fp->index, idx); } else if ((idx & USABLE_RCQ_ENTRIES_PER_PAGE) == USABLE_RCQ_ENTRIES_PER_PAGE) { /* CQE next page pointer. */ BXE_PRINTF("fp[%02d].rx_cqe[0x%04X] NP: haddr=0x%08X:%08X\n", fp->index, idx, le32toh(cqe->next_page_cqe.addr_hi), le32toh(cqe->next_page_cqe.addr_lo)); } else { /* Normal CQE. */ BXE_PRINTF("fp[%02d].rx_cqe[0x%04X] CQ: error_flags=0x%b, " "pkt_len=0x%04X, status_flags=0x%02X, vlan=0x%04X " "rss_hash=0x%08X\n", fp->index, idx, cqe->fast_path_cqe.type_error_flags, BXE_ETH_FAST_PATH_RX_CQE_ERROR_FLAGS_PRINTFB, le16toh(cqe->fast_path_cqe.pkt_len), cqe->fast_path_cqe.status_flags, le16toh(cqe->fast_path_cqe.vlan_tag), le32toh(cqe->fast_path_cqe.rss_hash_result)); } } /* * Prints out information about a TX parsing BD. * * Returns: * Nothing. */ static __noinline void bxe_dump_tx_parsing_bd(struct bxe_fastpath *fp, int idx, struct eth_tx_parse_bd *p_bd) { struct bxe_softc *sc; sc = fp->sc; if (idx > MAX_TX_BD){ /* Index out of range. */ BXE_PRINTF("fp[%02d].tx_bd[0x%04X] XX: Invalid tx_bd index!\n", fp->index, idx); } else { BXE_PRINTF("fp[%02d]:tx_bd[0x%04X] PB: global_data=0x%b, " "tcp_flags=0x%b, ip_hlen=%04d, total_hlen=%04d, " "tcp_pseudo_csum=0x%04X, lso_mss=0x%04X, ip_id=0x%04X, " "tcp_send_seq=0x%08X\n", fp->index, idx, p_bd->global_data, BXE_ETH_TX_PARSE_BD_GLOBAL_DATA_PRINTFB, p_bd->tcp_flags, BXE_ETH_TX_PARSE_BD_TCP_FLAGS_PRINTFB, p_bd->ip_hlen, p_bd->total_hlen, p_bd->tcp_pseudo_csum, p_bd->lso_mss, p_bd->ip_id, p_bd->tcp_send_seq); } } /* * Prints out information about a tx_bd. * * Returns: * Nothing. */ static __noinline void bxe_dump_txbd(struct bxe_fastpath *fp, int idx, union eth_tx_bd_types *tx_bd) { struct bxe_softc *sc; sc = fp->sc; if (idx > MAX_TX_BD){ /* Index out of range. */ BXE_PRINTF("fp[%02d]:tx_bd[0x%04X] XX: Invalid tx_bd index!\n", fp->index, idx); } else if ((idx & USABLE_TX_BD_PER_PAGE) == USABLE_TX_BD_PER_PAGE) { /* TX next page BD. */ BXE_PRINTF("fp[%02d]:tx_bd[0x%04X] NP: haddr=0x%08X:%08X\n", fp->index, idx, tx_bd->next_bd.addr_hi, tx_bd->next_bd.addr_lo); } else if ((tx_bd->start_bd.bd_flags.as_bitfield & ETH_TX_BD_FLAGS_START_BD) != 0) { /* TX start BD. */ BXE_PRINTF("fp[%02d]:tx_bd[0x%04X] ST: haddr=0x%08X:%08X, " "nbd=%02d, nbytes=%05d, vlan/idx=0x%04X, flags=0x%b, " "gendata=0x%02X\n", fp->index, idx, tx_bd->start_bd.addr_hi, tx_bd->start_bd.addr_lo, tx_bd->start_bd.nbd, tx_bd->start_bd.nbytes, tx_bd->start_bd.vlan, tx_bd->start_bd.bd_flags.as_bitfield, BXE_ETH_TX_BD_FLAGS_PRINTFB, tx_bd->start_bd.general_data); } else { /* Regular TX BD. */ BXE_PRINTF("fp[%02d]:tx_bd[0x%04X] TX: haddr=0x%08X:%08X, " "total_pkt_bytes=%05d, nbytes=%05d\n", fp->index, idx, tx_bd->reg_bd.addr_hi, tx_bd->reg_bd.addr_lo, tx_bd->reg_bd.total_pkt_bytes, tx_bd->reg_bd.nbytes); } } /* * Prints out the transmit chain. * * Returns: * Nothing. */ static __noinline void bxe_dump_tx_chain(struct bxe_fastpath * fp, int tx_bd_prod, int count) { struct bxe_softc *sc; union eth_tx_bd_types *tx_bd; uint32_t val_hi, val_lo; int i, parsing_bd = 0; sc = fp->sc; /* First some info about the tx_bd chain structure. */ BXE_PRINTF( "----------------------------" " tx_bd chain " "----------------------------\n"); val_hi = U64_HI(fp->tx_dma.paddr); val_lo = U64_LO(fp->tx_dma.paddr); BXE_PRINTF( "0x%08X:%08X - (fp[%02d]->tx_dma.paddr) TX Chain physical address\n", val_hi, val_lo, fp->index); BXE_PRINTF( "page size = 0x%08X, tx chain pages = 0x%08X\n", (uint32_t)BCM_PAGE_SIZE, (uint32_t)NUM_TX_PAGES); BXE_PRINTF( "tx_bd per page = 0x%08X, usable tx_bd per page = 0x%08X\n", (uint32_t)TOTAL_TX_BD_PER_PAGE, (uint32_t)USABLE_TX_BD_PER_PAGE); BXE_PRINTF( "total tx_bd = 0x%08X\n", (uint32_t)TOTAL_TX_BD); BXE_PRINTF( "-----------------------------" " tx_bd data " "-----------------------------\n"); /* Now print out the tx_bd's themselves. */ for (i = 0; i < count; i++) { tx_bd = &fp->tx_chain[tx_bd_prod]; if (parsing_bd) { struct eth_tx_parse_bd *p_bd; p_bd = (struct eth_tx_parse_bd *) &fp->tx_chain[tx_bd_prod].parse_bd; bxe_dump_tx_parsing_bd(fp, tx_bd_prod, p_bd); parsing_bd = 0; } else { bxe_dump_txbd(fp, tx_bd_prod, tx_bd); if ((tx_bd->start_bd.bd_flags.as_bitfield & ETH_TX_BD_FLAGS_START_BD) != 0) /* * There is always a parsing BD following the * tx_bd with the start bit set. */ parsing_bd = 1; } /* Don't skip next page pointers. */ tx_bd_prod = ((tx_bd_prod + 1) & MAX_TX_BD); } BXE_PRINTF( "-----------------------------" "--------------" "-----------------------------\n"); } /* * Prints out the receive completion queue chain. * * Returns: * Nothing. */ static __noinline void bxe_dump_rx_cq_chain(struct bxe_fastpath *fp, int rx_cq_prod, int count) { struct bxe_softc *sc; union eth_rx_cqe *cqe; int i; sc = fp->sc; /* First some info about the tx_bd chain structure. */ BXE_PRINTF( "----------------------------" " CQE Chain " "----------------------------\n"); BXE_PRINTF("fp[%02d]->rcq_dma.paddr = 0x%jX\n", fp->index, (uintmax_t) fp->rcq_dma.paddr); BXE_PRINTF("page size = 0x%08X, cq chain pages " " = 0x%08X\n", (uint32_t)BCM_PAGE_SIZE, (uint32_t) NUM_RCQ_PAGES); BXE_PRINTF("cqe_bd per page = 0x%08X, usable cqe_bd per " "page = 0x%08X\n", (uint32_t) TOTAL_RCQ_ENTRIES_PER_PAGE, (uint32_t) USABLE_RCQ_ENTRIES_PER_PAGE); BXE_PRINTF("total cqe_bd = 0x%08X\n",(uint32_t) TOTAL_RCQ_ENTRIES); /* Now the CQE entries themselves. */ BXE_PRINTF( "----------------------------" " CQE Data " "----------------------------\n"); for (i = 0; i < count; i++) { cqe = (union eth_rx_cqe *)&fp->rcq_chain[rx_cq_prod]; bxe_dump_cqe(fp, rx_cq_prod, cqe); /* Don't skip next page pointers. */ rx_cq_prod = ((rx_cq_prod + 1) & MAX_RCQ_ENTRIES); } BXE_PRINTF( "----------------------------" "--------------" "----------------------------\n"); } /* * Prints out the receive chain. * * Returns: * Nothing. */ static __noinline void bxe_dump_rx_bd_chain(struct bxe_fastpath *fp, int prod, int count) { struct bxe_softc *sc; struct eth_rx_bd *rx_bd; struct mbuf *m; int i; sc = fp->sc; /* First some info about the tx_bd chain structure. */ BXE_PRINTF( "----------------------------" " rx_bd chain " "----------------------------\n"); BXE_PRINTF( "----- RX_BD Chain -----\n"); BXE_PRINTF("fp[%02d]->rx_dma.paddr = 0x%jX\n", fp->index, (uintmax_t) fp->rx_dma.paddr); BXE_PRINTF( "page size = 0x%08X, rx chain pages = 0x%08X\n", (uint32_t)BCM_PAGE_SIZE, (uint32_t)NUM_RX_PAGES); BXE_PRINTF( "rx_bd per page = 0x%08X, usable rx_bd per page = 0x%08X\n", (uint32_t)TOTAL_RX_BD_PER_PAGE, (uint32_t)USABLE_RX_BD_PER_PAGE); BXE_PRINTF( "total rx_bd = 0x%08X\n", (uint32_t)TOTAL_RX_BD); /* Now the rx_bd entries themselves. */ BXE_PRINTF( "----------------------------" " rx_bd data " "----------------------------\n"); /* Now print out the rx_bd's themselves. */ for (i = 0; i < count; i++) { rx_bd = (struct eth_rx_bd *) (&fp->rx_chain[prod]); m = sc->fp->rx_mbuf_ptr[prod]; bxe_dump_rxbd(fp, prod, rx_bd); bxe_dump_mbuf(sc, m); /* Don't skip next page pointers. */ prod = ((prod + 1) & MAX_RX_BD); } BXE_PRINTF( "----------------------------" "--------------" "----------------------------\n"); } /* * Prints out a register dump. * * Returns: * Nothing. */ static __noinline void bxe_dump_hw_state(struct bxe_softc *sc) { int i; BXE_PRINTF( "----------------------------" " Hardware State " "----------------------------\n"); for (i = 0x2000; i < 0x10000; i += 0x10) BXE_PRINTF("0x%04X: 0x%08X 0x%08X 0x%08X 0x%08X\n", i, REG_RD(sc, 0 + i), REG_RD(sc, 0 + i + 0x4), REG_RD(sc, 0 + i + 0x8), REG_RD(sc, 0 + i + 0xC)); BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * Prints out the RX mbuf chain. * * Returns: * Nothing. */ static __noinline void bxe_dump_rx_mbuf_chain(struct bxe_softc *sc, int chain_prod, int count) { struct mbuf *m; int i; BXE_PRINTF( "----------------------------" " rx mbuf data " "----------------------------\n"); for (i = 0; i < count; i++) { m = sc->fp->rx_mbuf_ptr[chain_prod]; BXE_PRINTF("rxmbuf[0x%04X]\n", chain_prod); bxe_dump_mbuf(sc, m); chain_prod = RX_BD(NEXT_RX_BD(chain_prod)); } BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * Prints out the mbufs in the TX mbuf chain. * * Returns: * Nothing. */ static __noinline void bxe_dump_tx_mbuf_chain(struct bxe_softc *sc, int chain_prod, int count) { struct mbuf *m; int i; BXE_PRINTF( "----------------------------" " tx mbuf data " "----------------------------\n"); for (i = 0; i < count; i++) { m = sc->fp->tx_mbuf_ptr[chain_prod]; BXE_PRINTF("txmbuf[%d]\n", chain_prod); bxe_dump_mbuf(sc, m); chain_prod = TX_BD(NEXT_TX_BD(chain_prod)); } BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * Prints out the status block from host memory. * * Returns: * Nothing. */ static __noinline void bxe_dump_status_block(struct bxe_softc *sc) { struct bxe_fastpath *fp; struct host_def_status_block *def_sb; struct host_status_block *fpsb; int i; def_sb = sc->def_sb; BXE_PRINTF( "----------------------------" " Status Block " "----------------------------\n"); for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; fpsb = fp->status_block; BXE_PRINTF( "----------------------------" " fp[%02d] " "----------------------------\n", fp->index); /* Print the USTORM fields (HC_USTORM_SB_NUM_INDICES). */ BXE_PRINTF( "0x%08X - USTORM Flags (F/W RESERVED)\n", fpsb->u_status_block.__flags); BXE_PRINTF( " 0x%02X - USTORM PCIe Function\n", fpsb->u_status_block.func); BXE_PRINTF( " 0x%02X - USTORM Status Block ID\n", fpsb->u_status_block.status_block_id); BXE_PRINTF( " 0x%04X - USTORM Status Block Index (Tag)\n", fpsb->u_status_block.status_block_index); BXE_PRINTF( " 0x%04X - USTORM [TOE_RX_CQ_CONS]\n", fpsb->u_status_block.index_values[HC_INDEX_U_TOE_RX_CQ_CONS]); BXE_PRINTF( " 0x%04X - USTORM [ETH_RX_CQ_CONS]\n", fpsb->u_status_block.index_values[HC_INDEX_U_ETH_RX_CQ_CONS]); BXE_PRINTF( " 0x%04X - USTORM [ETH_RX_BD_CONS]\n", fpsb->u_status_block.index_values[HC_INDEX_U_ETH_RX_BD_CONS]); BXE_PRINTF( " 0x%04X - USTORM [RESERVED]\n", fpsb->u_status_block.index_values[3]); /* Print the CSTORM fields (HC_CSTORM_SB_NUM_INDICES). */ BXE_PRINTF( "0x%08X - CSTORM Flags (F/W RESERVED)\n", fpsb->c_status_block.__flags); BXE_PRINTF( " 0x%02X - CSTORM PCIe Function\n", fpsb->c_status_block.func); BXE_PRINTF( " 0x%02X - CSTORM Status Block ID\n", fpsb->c_status_block.status_block_id); BXE_PRINTF( " 0x%04X - CSTORM Status Block Index (Tag)\n", fpsb->c_status_block.status_block_index); BXE_PRINTF( " 0x%04X - CSTORM [TOE_TX_CQ_CONS]\n", fpsb->c_status_block.index_values[HC_INDEX_C_TOE_TX_CQ_CONS]); BXE_PRINTF( " 0x%04X - CSTORM [ETH_TX_CQ_CONS]\n", fpsb->c_status_block.index_values[HC_INDEX_C_ETH_TX_CQ_CONS]); BXE_PRINTF( " 0x%04X - CSTORM [ISCSI_EQ_CONS]\n", fpsb->c_status_block.index_values[HC_INDEX_C_ISCSI_EQ_CONS]); BXE_PRINTF( " 0x%04X - CSTORM [RESERVED]\n", fpsb->c_status_block.index_values[3]); } BXE_PRINTF( "--------------------------" " Def Status Block " "--------------------------\n"); /* Print attention information. */ BXE_PRINTF( " 0x%02X - Status Block ID\n", def_sb->atten_status_block.status_block_id); BXE_PRINTF( "0x%08X - Attn Bits\n", def_sb->atten_status_block.attn_bits); BXE_PRINTF( "0x%08X - Attn Bits Ack\n", def_sb->atten_status_block.attn_bits_ack); BXE_PRINTF( " 0x%04X - Attn Block Index\n", le16toh(def_sb->atten_status_block.attn_bits_index)); /* Print the USTORM fields (HC_USTORM_DEF_SB_NUM_INDICES). */ BXE_PRINTF( " 0x%02X - USTORM Status Block ID\n", def_sb->u_def_status_block.status_block_id); BXE_PRINTF( " 0x%04X - USTORM Status Block Index\n", le16toh(def_sb->u_def_status_block.status_block_index)); BXE_PRINTF( " 0x%04X - USTORM [ETH_RDMA_RX_CQ_CONS]\n", le16toh(def_sb->u_def_status_block.index_values[HC_INDEX_DEF_U_ETH_RDMA_RX_CQ_CONS])); BXE_PRINTF( " 0x%04X - USTORM [ETH_ISCSI_RX_CQ_CONS]\n", le16toh(def_sb->u_def_status_block.index_values[HC_INDEX_DEF_U_ETH_ISCSI_RX_CQ_CONS])); BXE_PRINTF( " 0x%04X - USTORM [ETH_RDMA_RX_BD_CONS]\n", le16toh(def_sb->u_def_status_block.index_values[HC_INDEX_DEF_U_ETH_RDMA_RX_BD_CONS])); BXE_PRINTF( " 0x%04X - USTORM [ETH_ISCSI_RX_BD_CONS]\n", le16toh(def_sb->u_def_status_block.index_values[HC_INDEX_DEF_U_ETH_ISCSI_RX_BD_CONS])); /* Print the CSTORM fields (HC_CSTORM_DEF_SB_NUM_INDICES). */ BXE_PRINTF( " 0x%02X - CSTORM Status Block ID\n", def_sb->c_def_status_block.status_block_id); BXE_PRINTF( " 0x%04X - CSTORM Status Block Index\n", le16toh(def_sb->c_def_status_block.status_block_index)); BXE_PRINTF( " 0x%04X - CSTORM [RDMA_EQ_CONS]\n", le16toh(def_sb->c_def_status_block.index_values[HC_INDEX_DEF_C_RDMA_EQ_CONS])); BXE_PRINTF( " 0x%04X - CSTORM [RDMA_NAL_PROD]\n", le16toh(def_sb->c_def_status_block.index_values[HC_INDEX_DEF_C_RDMA_NAL_PROD])); BXE_PRINTF( " 0x%04X - CSTORM [ETH_FW_TX_CQ_CONS]\n", le16toh(def_sb->c_def_status_block.index_values[HC_INDEX_DEF_C_ETH_FW_TX_CQ_CONS])); BXE_PRINTF( " 0x%04X - CSTORM [ETH_SLOW_PATH]\n", le16toh(def_sb->c_def_status_block.index_values[HC_INDEX_DEF_C_ETH_SLOW_PATH])); BXE_PRINTF( " 0x%04X - CSTORM [ETH_RDMA_CQ_CONS]\n", le16toh(def_sb->c_def_status_block.index_values[HC_INDEX_DEF_C_ETH_RDMA_CQ_CONS])); BXE_PRINTF( " 0x%04X - CSTORM [ETH_ISCSI_CQ_CONS]\n", le16toh(def_sb->c_def_status_block.index_values[HC_INDEX_DEF_C_ETH_ISCSI_CQ_CONS])); BXE_PRINTF( " 0x%04X - CSTORM [UNUSED]\n", le16toh(def_sb->c_def_status_block.index_values[6])); BXE_PRINTF( " 0x%04X - CSTORM [UNUSED]\n", le16toh(def_sb->c_def_status_block.index_values[7])); /* Print the TSTORM fields (HC_TSTORM_DEF_SB_NUM_INDICES). */ BXE_PRINTF( " 0x%02X - TSTORM Status Block ID\n", def_sb->t_def_status_block.status_block_id); BXE_PRINTF( " 0x%04X - TSTORM Status Block Index\n", le16toh(def_sb->t_def_status_block.status_block_index)); for (i = 0; i < HC_TSTORM_DEF_SB_NUM_INDICES; i++) BXE_PRINTF( " 0x%04X - TSTORM [UNUSED]\n", le16toh(def_sb->t_def_status_block.index_values[i])); /* Print the XSTORM fields (HC_XSTORM_DEF_SB_NUM_INDICES). */ BXE_PRINTF( " 0x%02X - XSTORM Status Block ID\n", def_sb->x_def_status_block.status_block_id); BXE_PRINTF( " 0x%04X - XSTORM Status Block Index\n", le16toh(def_sb->x_def_status_block.status_block_index)); for (i = 0; i < HC_XSTORM_DEF_SB_NUM_INDICES; i++) BXE_PRINTF( " 0x%04X - XSTORM [UNUSED]\n", le16toh(def_sb->x_def_status_block.index_values[i])); BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * Prints out the statistics block from host memory. * * Returns: * Nothing. */ static __noinline void bxe_dump_stats_block(struct bxe_softc *sc) { } /* * Prints out a summary of the fastpath state. * * Returns: * Nothing. */ static __noinline void bxe_dump_fp_state(struct bxe_fastpath *fp) { struct bxe_softc *sc; uint32_t val_hi, val_lo; int i; sc = fp->sc; BXE_PRINTF( "----------------------------" " Fastpath State " "----------------------------\n"); val_hi = U64_HI(fp); val_lo = U64_LO(fp); BXE_PRINTF( "0x%08X:%08X - (fp[%02d]) fastpath virtual address\n", val_hi, val_lo, fp->index); BXE_PRINTF( " %3d - (fp[%02d]->sb_id)\n", fp->sb_id, fp->index); BXE_PRINTF( " %3d - (fp[%02d]->cl_id)\n", fp->cl_id, fp->index); BXE_PRINTF( " 0x%08X - (fp[%02d]->state)\n", (uint32_t)fp->state, fp->index); /* Receive state. */ BXE_PRINTF( " 0x%04X - (fp[%02d]->rx_bd_prod)\n", fp->rx_bd_prod, fp->index); BXE_PRINTF( " 0x%04X - (fp[%02d]->rx_bd_cons)\n", fp->rx_bd_cons, fp->index); BXE_PRINTF( " 0x%04X - (fp[%02d]->rx_cq_prod)\n", fp->rx_cq_prod, fp->index); BXE_PRINTF( " 0x%04X - (fp[%02d]->rx_cq_cons)\n", fp->rx_cq_cons, fp->index); BXE_PRINTF( " %16lu - (fp[%02d]->rx_pkts)\n", fp->rx_pkts, fp->index); BXE_PRINTF( " 0x%08X - (fp[%02d]->rx_mbuf_alloc)\n", fp->rx_mbuf_alloc, fp->index); BXE_PRINTF( " %16lu - (fp[%02d]->ipackets)\n", fp->ipackets, fp->index); BXE_PRINTF( " %16lu - (fp[%02d]->rx_soft_errors)\n", fp->rx_soft_errors, fp->index); /* Transmit state. */ BXE_PRINTF( " 0x%04X - (fp[%02d]->tx_bd_used)\n", fp->tx_bd_used, fp->index); BXE_PRINTF( " 0x%04X - (fp[%02d]->tx_bd_prod)\n", fp->tx_bd_prod, fp->index); BXE_PRINTF( " 0x%04X - (fp[%02d]->tx_bd_cons)\n", fp->tx_bd_cons, fp->index); BXE_PRINTF( " 0x%04X - (fp[%02d]->tx_pkt_prod)\n", fp->tx_pkt_prod, fp->index); BXE_PRINTF( " 0x%04X - (fp[%02d]->tx_pkt_cons)\n", fp->tx_pkt_cons, fp->index); BXE_PRINTF( " %16lu - (fp[%02d]->tx_pkts)\n", fp->tx_pkts, fp->index); BXE_PRINTF( " 0x%08X - (fp[%02d]->tx_mbuf_alloc)\n", fp->tx_mbuf_alloc, fp->index); BXE_PRINTF( " %16lu - (fp[%02d]->opackets)\n", fp->opackets, fp->index); BXE_PRINTF( " %16lu - (fp[%02d]->tx_soft_errors)\n", fp->tx_soft_errors, fp->index); /* TPA state. */ if (TPA_ENABLED(sc)) { BXE_PRINTF( " %16lu - (fp[%02d]->rx_tpa_pkts)\n", fp->rx_tpa_pkts, fp->index); BXE_PRINTF( " 0x%08X - (fp[%02d]->tpa_mbuf_alloc)\n", fp->tpa_mbuf_alloc, fp->index); BXE_PRINTF( " 0x%08X - (fp[%02d]->sge_mbuf_alloc)\n", fp->sge_mbuf_alloc, fp->index); if (CHIP_IS_E1(sc)) { for (i = 0; i < ETH_MAX_AGGREGATION_QUEUES_E1; i++) BXE_PRINTF( " 0x%08X - (fp[%02d]->tpa_state[%02d])\n", (uint32_t)fp->tpa_state[i], fp->index, i); } else { for (i = 0; i < ETH_MAX_AGGREGATION_QUEUES_E1; i++) BXE_PRINTF( " 0x%08X - (fp[%02d]->tpa_state[%02d])\n", (uint32_t)fp->tpa_state[i], fp->index, i); } } BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * Returns: * Nothing. */ static __noinline void bxe_dump_port_state_locked(struct bxe_softc *sc) { BXE_PRINTF( "------------------------------" " Port State " "------------------------------\n"); BXE_PRINTF( " %2d - (port) pmf\n", sc->port.pmf); BXE_PRINTF( "0x%08X - (port) link_config\n", sc->port.link_config); BXE_PRINTF( "0x%08X - (port) supported\n", sc->port.supported); BXE_PRINTF( "0x%08X - (port) advertising\n", sc->port.advertising); BXE_PRINTF( "0x%08X - (port) port_stx\n", sc->port.port_stx); BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * Returns: * Nothing. */ static __noinline void bxe_dump_link_vars_state_locked(struct bxe_softc *sc) { BXE_PRINTF( "---------------------------" " Link Vars State " "----------------------------\n"); switch (sc->link_vars.mac_type) { case MAC_TYPE_NONE: BXE_PRINTF(" NONE"); break; case MAC_TYPE_EMAC: BXE_PRINTF(" EMAC"); break; case MAC_TYPE_BMAC: BXE_PRINTF(" BMAC"); break; default: BXE_PRINTF(" UNKN"); } printf(" - (link_vars->mac_type)\n"); BXE_PRINTF( " %2d - (link_vars->phy_link_up)\n", sc->link_vars.phy_link_up); BXE_PRINTF( " %2d - (link_vars->link_up)\n", sc->link_vars.link_up); BXE_PRINTF( " %2d - (link_vars->duplex)\n", sc->link_vars.duplex); BXE_PRINTF( " 0x%04X - (link_vars->flow_ctrl)\n", sc->link_vars.flow_ctrl); BXE_PRINTF( " 0x%04X - (link_vars->line_speed)\n", sc->link_vars.line_speed); BXE_PRINTF( "0x%08X - (link_vars->ieee_fc)\n", sc->link_vars.ieee_fc); BXE_PRINTF( "0x%08X - (link_vars->autoneg)\n", sc->link_vars.autoneg); BXE_PRINTF( "0x%08X - (link_vars->phy_flags)\n", sc->link_vars.phy_flags); BXE_PRINTF( "0x%08X - (link_vars->link_status)\n", sc->link_vars.link_status); BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * * Returns: * Nothing. */ static __noinline void bxe_dump_link_params_state_locked(struct bxe_softc *sc) { BXE_PRINTF( "--------------------------" " Link Params State " "---------------------------\n"); BXE_PRINTF( " %2d - (link_params->port)\n", sc->link_params.port); BXE_PRINTF( " %2d - (link_params->loopback_mode)\n", sc->link_params.loopback_mode); BXE_PRINTF( " %3d - (link_params->phy_addr)\n", sc->link_params.phy_addr); BXE_PRINTF( " 0x%04X - (link_params->req_duplex)\n", sc->link_params.req_duplex); BXE_PRINTF( " 0x%04X - (link_params->req_flow_ctrl)\n", sc->link_params.req_flow_ctrl); BXE_PRINTF( " 0x%04X - (link_params->req_line_speed)\n", sc->link_params.req_line_speed); BXE_PRINTF( " %5d - (link_params->ether_mtu)\n", sc->port.ether_mtu); BXE_PRINTF( "0x%08X - (link_params->shmem_base) shared memory base address\n", sc->link_params.shmem_base); BXE_PRINTF( "0x%08X - (link_params->speed_cap_mask)\n", sc->link_params.speed_cap_mask); BXE_PRINTF( "0x%08X - (link_params->ext_phy_config)\n", sc->link_params.ext_phy_config); BXE_PRINTF( "0x%08X - (link_params->switch_cfg)\n", sc->link_params.switch_cfg); BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * Prints out a summary of the driver state. * * Returns: * Nothing. */ static __noinline void bxe_dump_driver_state(struct bxe_softc *sc) { uint32_t val_hi, val_lo; BXE_PRINTF( "-----------------------------" " Driver State " "-----------------------------\n"); val_hi = U64_HI(sc); val_lo = U64_LO(sc); BXE_PRINTF( "0x%08X:%08X - (sc) driver softc structure virtual address\n", val_hi, val_lo); val_hi = U64_HI(sc->bxe_vhandle); val_lo = U64_LO(sc->bxe_vhandle); BXE_PRINTF( "0x%08X:%08X - (sc->bxe_vhandle) PCI BAR0 virtual address\n", val_hi, val_lo); val_hi = U64_HI(sc->bxe_db_vhandle); val_lo = U64_LO(sc->bxe_db_vhandle); BXE_PRINTF( "0x%08X:%08X - (sc->bxe_db_vhandle) PCI BAR2 virtual address\n", val_hi, val_lo); BXE_PRINTF(" 0x%08X - (sc->num_queues) Fastpath queues\n", sc->num_queues); BXE_PRINTF(" 0x%08X - (sc->rx_lane_swap) RX XAUI lane swap\n", sc->rx_lane_swap); BXE_PRINTF(" 0x%08X - (sc->tx_lane_swap) TX XAUI lane swap\n", sc->tx_lane_swap); BXE_PRINTF(" %16lu - (sc->debug_sim_mbuf_alloc_failed)\n", sc->debug_sim_mbuf_alloc_failed); BXE_PRINTF(" %16lu - (sc->debug_sim_mbuf_map_failed)\n", sc->debug_sim_mbuf_map_failed); BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); bxe_dump_port_state_locked(sc); bxe_dump_link_params_state_locked(sc); bxe_dump_link_vars_state_locked(sc); } /* * Dump bootcode (MCP) debug buffer to the console. * * Returns: * None */ static __noinline void bxe_dump_fw(struct bxe_softc *sc) { uint32_t addr, mark, data[9], offset; int word; addr = sc->common.shmem_base - 0x0800 + 4; mark = REG_RD(sc, addr); mark = MCP_REG_MCPR_SCRATCH + ((mark + 0x3) & ~0x3) - 0x08000000; BXE_PRINTF( "---------------------------" " MCP Debug Buffer " "---------------------------\n"); /* Read from "mark" to the end of the buffer. */ for (offset = mark; offset <= sc->common.shmem_base; offset += (0x8 * 4)) { for (word = 0; word < 8; word++) data[word] = htonl(REG_RD(sc, offset + 4 * word)); data[8] = 0x0; printf("%s", (char *) data); } /* Read from the start of the buffer to "mark". */ for (offset = addr + 4; offset <= mark; offset += (0x8 * 4)) { for (word = 0; word < 8; word++) data[word] = htonl(REG_RD(sc, offset + 4 * word)); data[8] = 0x0; printf("%s", (char *) data); } BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * Decode firmware messages. * * Returns: * None */ static void bxe_decode_mb_msgs(struct bxe_softc *sc, uint32_t drv_mb_header, uint32_t fw_mb_header) { if (drv_mb_header) { BXE_PRINTF("Driver message is "); switch (drv_mb_header & DRV_MSG_CODE_MASK) { case DRV_MSG_CODE_LOAD_REQ: printf( "LOAD_REQ (0x%08X)", (uint32_t)DRV_MSG_CODE_LOAD_REQ); break; case DRV_MSG_CODE_LOAD_DONE: printf( "LOAD_DONE (0x%08X)", (uint32_t)DRV_MSG_CODE_LOAD_DONE); break; case DRV_MSG_CODE_UNLOAD_REQ_WOL_EN: printf( "UNLOAD_REQ_WOL_EN (0x%08X)", (uint32_t)DRV_MSG_CODE_UNLOAD_REQ_WOL_EN); break; case DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS: printf( "UNLOAD_REQ_WOL_DIS (0x%08X)", (uint32_t)DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS); break; case DRV_MSG_CODE_UNLOAD_REQ_WOL_MCP: printf( "UNLOADREQ_WOL_MCP (0x%08X)", (uint32_t)DRV_MSG_CODE_UNLOAD_REQ_WOL_MCP); break; case DRV_MSG_CODE_UNLOAD_DONE: printf( "UNLOAD_DONE (0x%08X)", (uint32_t)DRV_MSG_CODE_UNLOAD_DONE); break; case DRV_MSG_CODE_DIAG_ENTER_REQ: printf( "DIAG_ENTER_REQ (0x%08X)", (uint32_t)DRV_MSG_CODE_DIAG_ENTER_REQ); break; case DRV_MSG_CODE_DIAG_EXIT_REQ: printf( "DIAG_EXIT_REQ (0x%08X)", (uint32_t)DRV_MSG_CODE_DIAG_EXIT_REQ); break; case DRV_MSG_CODE_VALIDATE_KEY: printf( "CODE_VALIDITY_KEY (0x%08X)", (uint32_t)DRV_MSG_CODE_VALIDATE_KEY); break; case DRV_MSG_CODE_GET_CURR_KEY: printf( "GET_CURR_KEY (0x%08X)", (uint32_t) DRV_MSG_CODE_GET_CURR_KEY); break; case DRV_MSG_CODE_GET_UPGRADE_KEY: printf( "GET_UPGRADE_KEY (0x%08X)", (uint32_t)DRV_MSG_CODE_GET_UPGRADE_KEY); break; case DRV_MSG_CODE_GET_MANUF_KEY: printf( "GET_MANUF_KEY (0x%08X)", (uint32_t)DRV_MSG_CODE_GET_MANUF_KEY); break; case DRV_MSG_CODE_LOAD_L2B_PRAM: printf( "LOAD_L2B_PRAM (0x%08X)", (uint32_t)DRV_MSG_CODE_LOAD_L2B_PRAM); break; case BIOS_MSG_CODE_LIC_CHALLENGE: printf( "LIC_CHALLENGE (0x%08X)", (uint32_t)BIOS_MSG_CODE_LIC_CHALLENGE); break; case BIOS_MSG_CODE_LIC_RESPONSE: printf( "LIC_RESPONSE (0x%08X)", (uint32_t)BIOS_MSG_CODE_LIC_RESPONSE); break; case BIOS_MSG_CODE_VIRT_MAC_PRIM: printf( "VIRT_MAC_PRIM (0x%08X)", (uint32_t)BIOS_MSG_CODE_VIRT_MAC_PRIM); break; case BIOS_MSG_CODE_VIRT_MAC_ISCSI: printf( "VIRT_MAC_ISCSI (0x%08X)", (uint32_t)BIOS_MSG_CODE_VIRT_MAC_ISCSI); break; default: printf( "Unknown command (0x%08X)!", (drv_mb_header & DRV_MSG_CODE_MASK)); } printf(" (seq = 0x%04X)\n", (drv_mb_header & DRV_MSG_SEQ_NUMBER_MASK)); } if (fw_mb_header) { BXE_PRINTF("Firmware response is "); switch (fw_mb_header & FW_MSG_CODE_MASK) { case FW_MSG_CODE_DRV_LOAD_COMMON: printf( "DRV_LOAD_COMMON (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_LOAD_COMMON); break; case FW_MSG_CODE_DRV_LOAD_PORT: printf( "DRV_LOAD_PORT (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_LOAD_PORT); break; case FW_MSG_CODE_DRV_LOAD_FUNCTION: printf( "DRV_LOAD_FUNCTION (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_LOAD_FUNCTION); break; case FW_MSG_CODE_DRV_LOAD_REFUSED: printf( "DRV_LOAD_REFUSED (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_LOAD_REFUSED); break; case FW_MSG_CODE_DRV_LOAD_DONE: printf( "DRV_LOAD_DONE (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_LOAD_DONE); break; case FW_MSG_CODE_DRV_UNLOAD_COMMON: printf( "DRV_UNLOAD_COMMON (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_UNLOAD_COMMON); break; case FW_MSG_CODE_DRV_UNLOAD_PORT: printf( "DRV_UNLOAD_PORT (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_UNLOAD_PORT); break; case FW_MSG_CODE_DRV_UNLOAD_FUNCTION: printf( "DRV_UNLOAD_FUNCTION (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_UNLOAD_FUNCTION); break; case FW_MSG_CODE_DRV_UNLOAD_DONE: printf( "DRV_UNLOAD_DONE (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_UNLOAD_DONE); break; case FW_MSG_CODE_DIAG_ENTER_DONE: printf( "DIAG_ENTER_DONE (0x%08X)", (uint32_t)FW_MSG_CODE_DIAG_ENTER_DONE); break; case FW_MSG_CODE_DIAG_REFUSE: printf( "DIAG_REFUSE (0x%08X)", (uint32_t)FW_MSG_CODE_DIAG_REFUSE); break; case FW_MSG_CODE_DIAG_EXIT_DONE: printf( "DIAG_EXIT_DONE (0x%08X)", (uint32_t)FW_MSG_CODE_DIAG_EXIT_DONE); break; case FW_MSG_CODE_VALIDATE_KEY_SUCCESS: printf( "VALIDATE_KEY_SUCCESS (0x%08X)", (uint32_t)FW_MSG_CODE_VALIDATE_KEY_SUCCESS); break; case FW_MSG_CODE_VALIDATE_KEY_FAILURE: printf( "VALIDATE_KEY_FAILURE (0x%08X)", (uint32_t)FW_MSG_CODE_VALIDATE_KEY_FAILURE); break; case FW_MSG_CODE_GET_KEY_DONE: printf( "GET_KEY_DONE (0x%08X)", (uint32_t)FW_MSG_CODE_GET_KEY_DONE); break; case FW_MSG_CODE_NO_KEY: printf( "NO_KEY (0x%08X)", (uint32_t)FW_MSG_CODE_NO_KEY); break; default: printf( "unknown value (0x%08X)!", (fw_mb_header & FW_MSG_CODE_MASK)); } printf(" (seq = 0x%04X)\n", (fw_mb_header & FW_MSG_SEQ_NUMBER_MASK)); } } /* * Prints a text string for the ramrod command. * * Returns: * None */ static void bxe_decode_ramrod_cmd(struct bxe_softc *sc, int command) { BXE_PRINTF("Ramrod command = "); switch (command) { case RAMROD_CMD_ID_ETH_PORT_SETUP: printf("ETH_PORT_SETUP\n"); break; case RAMROD_CMD_ID_ETH_CLIENT_SETUP: printf("ETH_CLIENT_SETUP\n"); break; case RAMROD_CMD_ID_ETH_STAT_QUERY: printf("ETH_STAT_QUERY\n"); break; case RAMROD_CMD_ID_ETH_UPDATE: printf("ETH_UPDATE\n"); break; case RAMROD_CMD_ID_ETH_HALT: printf("ETH_HALT\n"); break; case RAMROD_CMD_ID_ETH_SET_MAC: printf("ETH_SET_MAC\n"); break; case RAMROD_CMD_ID_ETH_CFC_DEL: printf("ETH_CFC_DEL\n"); break; case RAMROD_CMD_ID_ETH_PORT_DEL: printf("ETH_PORT_DEL\n"); break; case RAMROD_CMD_ID_ETH_FORWARD_SETUP: printf("ETH_FORWARD_SETUP\n"); break; default: printf("Unknown ramrod command!\n"); } } /* * Prints out driver information and forces a kernel breakpoint. * * Returns: * Nothing. */ static void bxe_breakpoint(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i; fp = &sc->fp[0]; /* Unreachable code to silence the compiler about unused functions. */ if (0) { bxe_reg_read16(sc, PCICFG_OFFSET); bxe_dump_tx_mbuf_chain(sc, 0, USABLE_TX_BD); bxe_dump_rx_mbuf_chain(sc, 0, USABLE_RX_BD); bxe_dump_tx_chain(fp, 0, USABLE_TX_BD); bxe_dump_rx_cq_chain(fp, 0, USABLE_RCQ_ENTRIES); bxe_dump_rx_bd_chain(fp, 0, USABLE_RX_BD); bxe_dump_status_block(sc); bxe_dump_stats_block(sc); bxe_dump_fp_state(fp); bxe_dump_driver_state(sc); bxe_dump_hw_state(sc); bxe_dump_fw(sc); } /* * Do some device sanity checking. Run it twice in case * the hardware is still running so we can identify any * transient conditions. */ bxe_idle_chk(sc); bxe_idle_chk(sc); bxe_dump_driver_state(sc); for (i = 0; i < sc->num_queues; i++) bxe_dump_fp_state(&sc->fp[i]); bxe_dump_status_block(sc); bxe_dump_fw(sc); /* Call the OS debugger. */ breakpoint(); } #endif