diff options
Diffstat (limited to 'drivers/net/ethernet/broadcom')
18 files changed, 1310 insertions, 344 deletions
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c index 2c6ba046d2a8..17ae6df90723 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c @@ -1145,7 +1145,7 @@ static void bnx2x_dcbx_get_num_pg_traf_type(struct bnx2x *bp, break; } } - if (false == pg_found) { + if (!pg_found) { data[help_data->num_of_pg].pg = add_pg; data[help_data->num_of_pg].pg_priority = (1 << ttp[add_traf_type]); @@ -1155,7 +1155,7 @@ static void bnx2x_dcbx_get_num_pg_traf_type(struct bnx2x *bp, } DP(BNX2X_MSG_DCB, "add_traf_type %d pg_found %s num_of_pg %d\n", - add_traf_type, (false == pg_found) ? "NO" : "YES", + add_traf_type, !pg_found ? "NO" : "YES", help_data->num_of_pg); } } @@ -1544,8 +1544,7 @@ static void bnx2x_dcbx_2cos_limit_cee_three_pg_to_cos_params( if (pg_entry < DCBX_MAX_NUM_PG_BW_ENTRIES) { entry = 0; - if (i == (num_of_pri-1) && - false == b_found_strict) + if (i == (num_of_pri-1) && !b_found_strict) /* last entry will be handled separately * If no priority is strict than last * entry goes to last queue. diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index 517caedc0a87..1426c691c7c4 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -3085,6 +3085,7 @@ static int bnx2x_bsc_read(struct link_params *params, u8 xfer_cnt, u32 *data_array) { + u64 t0, delta; u32 val, i; int rc = 0; @@ -3114,17 +3115,18 @@ static int bnx2x_bsc_read(struct link_params *params, REG_WR(bp, MCP_REG_MCPR_IMC_COMMAND, val); /* Poll for completion */ - i = 0; + t0 = ktime_get_ns(); val = REG_RD(bp, MCP_REG_MCPR_IMC_COMMAND); while (((val >> MCPR_IMC_COMMAND_IMC_STATUS_BITSHIFT) & 0x3) != 1) { - udelay(10); - val = REG_RD(bp, MCP_REG_MCPR_IMC_COMMAND); - if (i++ > 1000) { - DP(NETIF_MSG_LINK, "wr 0 byte timed out after %d try\n", - i); + delta = ktime_get_ns() - t0; + if (delta > 10 * NSEC_PER_MSEC) { + DP(NETIF_MSG_LINK, "wr 0 byte timed out after %Lu ns\n", + delta); rc = -EFAULT; break; } + usleep_range(10, 20); + val = REG_RD(bp, MCP_REG_MCPR_IMC_COMMAND); } if (rc == -EFAULT) return rc; @@ -3138,16 +3140,18 @@ static int bnx2x_bsc_read(struct link_params *params, REG_WR(bp, MCP_REG_MCPR_IMC_COMMAND, val); /* Poll for completion */ - i = 0; + t0 = ktime_get_ns(); val = REG_RD(bp, MCP_REG_MCPR_IMC_COMMAND); while (((val >> MCPR_IMC_COMMAND_IMC_STATUS_BITSHIFT) & 0x3) != 1) { - udelay(10); - val = REG_RD(bp, MCP_REG_MCPR_IMC_COMMAND); - if (i++ > 1000) { - DP(NETIF_MSG_LINK, "rd op timed out after %d try\n", i); + delta = ktime_get_ns() - t0; + if (delta > 10 * NSEC_PER_MSEC) { + DP(NETIF_MSG_LINK, "rd op timed out after %Lu ns\n", + delta); rc = -EFAULT; break; } + usleep_range(10, 20); + val = REG_RD(bp, MCP_REG_MCPR_IMC_COMMAND); } if (rc == -EFAULT) return rc; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c index 5097a44686b3..b4476f44e386 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c @@ -331,27 +331,6 @@ bnx2x_vf_set_igu_info(struct bnx2x *bp, u8 igu_sb_id, u8 abs_vfid) BP_VFDB(bp)->vf_sbs_pool++; } -static inline void bnx2x_vf_vlan_credit(struct bnx2x *bp, - struct bnx2x_vlan_mac_obj *obj, - atomic_t *counter) -{ - struct list_head *pos; - int read_lock; - int cnt = 0; - - read_lock = bnx2x_vlan_mac_h_read_lock(bp, obj); - if (read_lock) - DP(BNX2X_MSG_SP, "Failed to take vlan mac read head; continuing anyway\n"); - - list_for_each(pos, &obj->head) - cnt++; - - if (!read_lock) - bnx2x_vlan_mac_h_read_unlock(bp, obj); - - atomic_set(counter, cnt); -} - static int bnx2x_vf_vlan_mac_clear(struct bnx2x *bp, struct bnx2x_virtf *vf, int qid, bool drv_only, int type) { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 58e0d9a781e9..c62589c266b2 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1766,7 +1766,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_cp_ring_info *cpr, rc = -EIO; if (rx_err & RX_CMPL_ERRORS_BUFFER_ERROR_MASK) { - bnapi->cp_ring.rx_buf_errors++; + bnapi->cp_ring.sw_stats.rx.rx_buf_errors++; if (!(bp->flags & BNXT_FLAG_CHIP_P5)) { netdev_warn(bp->dev, "RX buffer error %x\n", rx_err); @@ -1849,7 +1849,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_cp_ring_info *cpr, } else { if (rxcmp1->rx_cmp_cfa_code_errors_v2 & RX_CMP_L4_CS_ERR_BITS) { if (dev->features & NETIF_F_RXCSUM) - bnapi->cp_ring.rx_l4_csum_errors++; + bnapi->cp_ring.sw_stats.rx.rx_l4_csum_errors++; } } @@ -5039,8 +5039,7 @@ int bnxt_hwrm_vnic_cfg(struct bnxt *bp, u16 vnic_id) req.dflt_ring_grp = cpu_to_le16(bp->grp_info[grp_idx].fw_grp_id); req.lb_rule = cpu_to_le16(0xffff); vnic_mru: - req.mru = cpu_to_le16(bp->dev->mtu + ETH_HLEN + ETH_FCS_LEN + - VLAN_HLEN); + req.mru = cpu_to_le16(bp->dev->mtu + ETH_HLEN + VLAN_HLEN); req.vnic_id = cpu_to_le16(vnic->fw_vnic_id); #ifdef CONFIG_BNXT_SRIOV @@ -5350,9 +5349,9 @@ static void bnxt_set_db(struct bnxt *bp, struct bnxt_db_info *db, u32 ring_type, { if (bp->flags & BNXT_FLAG_CHIP_P5) { if (BNXT_PF(bp)) - db->doorbell = bp->bar1 + 0x10000; + db->doorbell = bp->bar1 + DB_PF_OFFSET_P5; else - db->doorbell = bp->bar1 + 0x4000; + db->doorbell = bp->bar1 + DB_VF_OFFSET_P5; switch (ring_type) { case HWRM_RING_ALLOC_TX: db->db_key64 = DBR_PATH_L2 | DBR_TYPE_SQ; @@ -6359,6 +6358,7 @@ static int bnxt_hwrm_func_qcfg(struct bnxt *bp) { struct hwrm_func_qcfg_input req = {0}; struct hwrm_func_qcfg_output *resp = bp->hwrm_cmd_resp_addr; + u32 min_db_offset = 0; u16 flags; int rc; @@ -6407,6 +6407,21 @@ static int bnxt_hwrm_func_qcfg(struct bnxt *bp) if (!bp->max_mtu) bp->max_mtu = BNXT_MAX_MTU; + if (bp->db_size) + goto func_qcfg_exit; + + if (bp->flags & BNXT_FLAG_CHIP_P5) { + if (BNXT_PF(bp)) + min_db_offset = DB_PF_OFFSET_P5; + else + min_db_offset = DB_VF_OFFSET_P5; + } + bp->db_size = PAGE_ALIGN(le16_to_cpu(resp->l2_doorbell_bar_size_kb) * + 1024); + if (!bp->db_size || bp->db_size > pci_resource_len(bp->pdev, 2) || + bp->db_size <= min_db_offset) + bp->db_size = pci_resource_len(bp->pdev, 2); + func_qcfg_exit: mutex_unlock(&bp->hwrm_cmd_lock); return rc; @@ -6428,23 +6443,13 @@ static int bnxt_hwrm_func_backing_store_qcaps(struct bnxt *bp) if (!rc) { struct bnxt_ctx_pg_info *ctx_pg; struct bnxt_ctx_mem_info *ctx; - int i; + int i, tqm_rings; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) { rc = -ENOMEM; goto ctx_err; } - ctx_pg = kzalloc(sizeof(*ctx_pg) * (bp->max_q + 1), GFP_KERNEL); - if (!ctx_pg) { - kfree(ctx); - rc = -ENOMEM; - goto ctx_err; - } - for (i = 0; i < bp->max_q + 1; i++, ctx_pg++) - ctx->tqm_mem[i] = ctx_pg; - - bp->ctx = ctx; ctx->qp_max_entries = le32_to_cpu(resp->qp_max_entries); ctx->qp_min_qp1_entries = le16_to_cpu(resp->qp_min_qp1_entries); ctx->qp_max_l2_entries = le16_to_cpu(resp->qp_max_l2_entries); @@ -6477,6 +6482,20 @@ static int bnxt_hwrm_func_backing_store_qcaps(struct bnxt *bp) ctx->tim_entry_size = le16_to_cpu(resp->tim_entry_size); ctx->tim_max_entries = le32_to_cpu(resp->tim_max_entries); ctx->ctx_kind_initializer = resp->ctx_kind_initializer; + ctx->tqm_fp_rings_count = resp->tqm_fp_rings_count; + if (!ctx->tqm_fp_rings_count) + ctx->tqm_fp_rings_count = bp->max_q; + + tqm_rings = ctx->tqm_fp_rings_count + 1; + ctx_pg = kcalloc(tqm_rings, sizeof(*ctx_pg), GFP_KERNEL); + if (!ctx_pg) { + kfree(ctx); + rc = -ENOMEM; + goto ctx_err; + } + for (i = 0; i < tqm_rings; i++, ctx_pg++) + ctx->tqm_mem[i] = ctx_pg; + bp->ctx = ctx; } else { rc = 0; } @@ -6729,7 +6748,7 @@ static void bnxt_free_ctx_mem(struct bnxt *bp) return; if (ctx->tqm_mem[0]) { - for (i = 0; i < bp->max_q + 1; i++) + for (i = 0; i < ctx->tqm_fp_rings_count + 1; i++) bnxt_free_ctx_pg_tbls(bp, ctx->tqm_mem[i]); kfree(ctx->tqm_mem[0]); ctx->tqm_mem[0] = NULL; @@ -6750,6 +6769,7 @@ static int bnxt_alloc_ctx_mem(struct bnxt *bp) struct bnxt_ctx_pg_info *ctx_pg; struct bnxt_ctx_mem_info *ctx; u32 mem_size, ena, entries; + u32 entries_sp, min; u32 num_mr, num_ah; u32 extra_srqs = 0; u32 extra_qps = 0; @@ -6839,14 +6859,17 @@ static int bnxt_alloc_ctx_mem(struct bnxt *bp) ena |= FUNC_BACKING_STORE_CFG_REQ_ENABLES_TIM; skip_rdma: - entries = ctx->qp_max_l2_entries + extra_qps; + min = ctx->tqm_min_entries_per_ring; + entries_sp = ctx->vnic_max_vnic_entries + ctx->qp_max_l2_entries + + 2 * (extra_qps + ctx->qp_min_qp1_entries) + min; + entries_sp = roundup(entries_sp, ctx->tqm_entries_multiple); + entries = ctx->qp_max_l2_entries + extra_qps + ctx->qp_min_qp1_entries; entries = roundup(entries, ctx->tqm_entries_multiple); - entries = clamp_t(u32, entries, ctx->tqm_min_entries_per_ring, - ctx->tqm_max_entries_per_ring); - for (i = 0; i < bp->max_q + 1; i++) { + entries = clamp_t(u32, entries, min, ctx->tqm_max_entries_per_ring); + for (i = 0; i < ctx->tqm_fp_rings_count + 1; i++) { ctx_pg = ctx->tqm_mem[i]; - ctx_pg->entries = entries; - mem_size = ctx->tqm_entry_size * entries; + ctx_pg->entries = i ? entries : entries_sp; + mem_size = ctx->tqm_entry_size * ctx_pg->entries; rc = bnxt_alloc_ctx_pg_tbls(bp, ctx_pg, mem_size, 1, false); if (rc) return rc; @@ -10259,7 +10282,7 @@ static void bnxt_chk_missed_irq(struct bnxt *bp) bnxt_dbg_hwrm_ring_info_get(bp, DBG_RING_INFO_GET_REQ_RING_TYPE_L2_CMPL, fw_ring_id, &val[0], &val[1]); - cpr->missed_irqs++; + cpr->sw_stats.cmn.missed_irqs++; } } } @@ -10888,6 +10911,9 @@ static int bnxt_init_board(struct pci_dev *pdev, struct net_device *dev) bp->dev = dev; bp->pdev = pdev; + /* Doorbell BAR bp->bar1 is mapped after bnxt_fw_init_one_p2() + * determines the BAR size. + */ bp->bar0 = pci_ioremap_bar(pdev, 0); if (!bp->bar0) { dev_err(&pdev->dev, "Cannot map device registers, aborting\n"); @@ -10895,13 +10921,6 @@ static int bnxt_init_board(struct pci_dev *pdev, struct net_device *dev) goto init_err_release; } - bp->bar1 = pci_ioremap_bar(pdev, 2); - if (!bp->bar1) { - dev_err(&pdev->dev, "Cannot map doorbell registers, aborting\n"); - rc = -ENOMEM; - goto init_err_release; - } - bp->bar2 = pci_ioremap_bar(pdev, 4); if (!bp->bar2) { dev_err(&pdev->dev, "Cannot map bar4 registers, aborting\n"); @@ -11823,6 +11842,16 @@ static int bnxt_pcie_dsn_get(struct bnxt *bp, u8 dsn[]) return 0; } +static int bnxt_map_db_bar(struct bnxt *bp) +{ + if (!bp->db_size) + return -ENODEV; + bp->bar1 = pci_iomap(bp->pdev, 2, bp->db_size); + if (!bp->bar1) + return -ENOMEM; + return 0; +} + static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { struct net_device *dev; @@ -11883,6 +11912,13 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) goto init_err_pci_clean; + rc = bnxt_map_db_bar(bp); + if (rc) { + dev_err(&pdev->dev, "Cannot map doorbell BAR rc = %d, aborting\n", + rc); + goto init_err_pci_clean; + } + dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_GRE | diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 3d39638521d6..9e173d74b72a 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -537,6 +537,9 @@ struct nqe_cn { #define DBR_TYPE_NQ_ARM (0xbULL << 60) #define DBR_TYPE_NULL (0xfULL << 60) +#define DB_PF_OFFSET_P5 0x10000 +#define DB_VF_OFFSET_P5 0x4000 + #define INVALID_HW_RING_ID ((u16)-1) /* The hardware supports certain page sizes. Use the supported page sizes @@ -902,6 +905,20 @@ struct bnxt_rx_ring_info { struct page_pool *page_pool; }; +struct bnxt_rx_sw_stats { + u64 rx_l4_csum_errors; + u64 rx_buf_errors; +}; + +struct bnxt_cmn_sw_stats { + u64 missed_irqs; +}; + +struct bnxt_sw_stats { + struct bnxt_rx_sw_stats rx; + struct bnxt_cmn_sw_stats cmn; +}; + struct bnxt_cp_ring_info { struct bnxt_napi *bnapi; u32 cp_raw_cons; @@ -929,9 +946,8 @@ struct bnxt_cp_ring_info { struct ctx_hw_stats *hw_stats; dma_addr_t hw_stats_map; u32 hw_stats_ctx_id; - u64 rx_l4_csum_errors; - u64 rx_buf_errors; - u64 missed_irqs; + + struct bnxt_sw_stats sw_stats; struct bnxt_ring_struct cp_ring_struct; @@ -1351,6 +1367,7 @@ struct bnxt_ctx_mem_info { u16 mrav_num_entries_units; u8 tqm_entries_multiple; u8 ctx_kind_initializer; + u8 tqm_fp_rings_count; u32 flags; #define BNXT_CTX_FLAG_INITED 0x01 @@ -1810,6 +1827,7 @@ struct bnxt { /* ensure atomic 64-bit doorbell writes on 32-bit systems. */ spinlock_t db_lock; #endif + int db_size; #define BNXT_NTP_FLTR_MAX_FLTR 4096 #define BNXT_NTP_FLTR_HASH_SIZE 512 @@ -1852,7 +1870,6 @@ struct bnxt { u8 dsn[8]; struct bnxt_tc_info *tc_info; struct list_head tc_indr_block_list; - struct notifier_block tc_netdev_nb; struct dentry *debugfs_pdev; struct device *hwmon_dev; }; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 360f9a95c1d5..6b88143af5ea 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -137,7 +137,7 @@ reset_coalesce: return rc; } -static const char * const bnxt_ring_stats_str[] = { +static const char * const bnxt_ring_rx_stats_str[] = { "rx_ucast_packets", "rx_mcast_packets", "rx_bcast_packets", @@ -146,6 +146,9 @@ static const char * const bnxt_ring_stats_str[] = { "rx_ucast_bytes", "rx_mcast_bytes", "rx_bcast_bytes", +}; + +static const char * const bnxt_ring_tx_stats_str[] = { "tx_ucast_packets", "tx_mcast_packets", "tx_bcast_packets", @@ -171,9 +174,12 @@ static const char * const bnxt_ring_tpa2_stats_str[] = { "rx_tpa_errors", }; -static const char * const bnxt_ring_sw_stats_str[] = { +static const char * const bnxt_rx_sw_stats_str[] = { "rx_l4_csum_errors", "rx_buf_errors", +}; + +static const char * const bnxt_cmn_sw_stats_str[] = { "missed_irqs", }; @@ -303,6 +309,11 @@ static struct { {0, "tx_total_discard_pkts"}, }; +#define NUM_RING_RX_SW_STATS ARRAY_SIZE(bnxt_rx_sw_stats_str) +#define NUM_RING_CMN_SW_STATS ARRAY_SIZE(bnxt_cmn_sw_stats_str) +#define NUM_RING_RX_HW_STATS ARRAY_SIZE(bnxt_ring_rx_stats_str) +#define NUM_RING_TX_HW_STATS ARRAY_SIZE(bnxt_ring_tx_stats_str) + static const struct { long offset; char string[ETH_GSTRING_LEN]; @@ -482,12 +493,21 @@ static int bnxt_get_num_tpa_ring_stats(struct bnxt *bp) static int bnxt_get_num_ring_stats(struct bnxt *bp) { - int num_stats; + int rx, tx, cmn; + bool sh = false; + + if (bp->flags & BNXT_FLAG_SHARED_RINGS) + sh = true; - num_stats = ARRAY_SIZE(bnxt_ring_stats_str) + - ARRAY_SIZE(bnxt_ring_sw_stats_str) + - bnxt_get_num_tpa_ring_stats(bp); - return num_stats * bp->cp_nr_rings; + rx = NUM_RING_RX_HW_STATS + NUM_RING_RX_SW_STATS + + bnxt_get_num_tpa_ring_stats(bp); + tx = NUM_RING_TX_HW_STATS; + cmn = NUM_RING_CMN_SW_STATS; + if (sh) + return (rx + tx + cmn) * bp->cp_nr_rings; + else + return rx * bp->rx_nr_rings + tx * bp->tx_nr_rings + + cmn * bp->cp_nr_rings; } static int bnxt_get_num_stats(struct bnxt *bp) @@ -528,13 +548,29 @@ static int bnxt_get_sset_count(struct net_device *dev, int sset) } } +static bool is_rx_ring(struct bnxt *bp, int ring_num) +{ + return ring_num < bp->rx_nr_rings; +} + +static bool is_tx_ring(struct bnxt *bp, int ring_num) +{ + int tx_base = 0; + + if (!(bp->flags & BNXT_FLAG_SHARED_RINGS)) + tx_base = bp->rx_nr_rings; + + if (ring_num >= tx_base && ring_num < (tx_base + bp->tx_nr_rings)) + return true; + return false; +} + static void bnxt_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *buf) { u32 i, j = 0; struct bnxt *bp = netdev_priv(dev); - u32 stat_fields = ARRAY_SIZE(bnxt_ring_stats_str) + - bnxt_get_num_tpa_ring_stats(bp); + u32 tpa_stats; if (!bp->bnapi) { j += bnxt_get_num_ring_stats(bp) + BNXT_NUM_SW_FUNC_STATS; @@ -544,17 +580,42 @@ static void bnxt_get_ethtool_stats(struct net_device *dev, for (i = 0; i < BNXT_NUM_SW_FUNC_STATS; i++) bnxt_sw_func_stats[i].counter = 0; + tpa_stats = bnxt_get_num_tpa_ring_stats(bp); for (i = 0; i < bp->cp_nr_rings; i++) { struct bnxt_napi *bnapi = bp->bnapi[i]; struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring; __le64 *hw_stats = (__le64 *)cpr->hw_stats; + u64 *sw; int k; - for (k = 0; k < stat_fields; j++, k++) + if (is_rx_ring(bp, i)) { + for (k = 0; k < NUM_RING_RX_HW_STATS; j++, k++) + buf[j] = le64_to_cpu(hw_stats[k]); + } + if (is_tx_ring(bp, i)) { + k = NUM_RING_RX_HW_STATS; + for (; k < NUM_RING_RX_HW_STATS + NUM_RING_TX_HW_STATS; + j++, k++) + buf[j] = le64_to_cpu(hw_stats[k]); + } + if (!tpa_stats || !is_rx_ring(bp, i)) + goto skip_tpa_ring_stats; + + k = NUM_RING_RX_HW_STATS + NUM_RING_TX_HW_STATS; + for (; k < NUM_RING_RX_HW_STATS + NUM_RING_TX_HW_STATS + + tpa_stats; j++, k++) buf[j] = le64_to_cpu(hw_stats[k]); - buf[j++] = cpr->rx_l4_csum_errors; - buf[j++] = cpr->rx_buf_errors; - buf[j++] = cpr->missed_irqs; + +skip_tpa_ring_stats: + sw = (u64 *)&cpr->sw_stats.rx; + if (is_rx_ring(bp, i)) { + for (k = 0; k < NUM_RING_RX_SW_STATS; j++, k++) + buf[j] = sw[k]; + } + + sw = (u64 *)&cpr->sw_stats.cmn; + for (k = 0; k < NUM_RING_CMN_SW_STATS; j++, k++) + buf[j] = sw[k]; bnxt_sw_func_stats[RX_TOTAL_DISCARDS].counter += le64_to_cpu(cpr->hw_stats->rx_discard_pkts); @@ -632,31 +693,48 @@ static void bnxt_get_strings(struct net_device *dev, u32 stringset, u8 *buf) switch (stringset) { case ETH_SS_STATS: for (i = 0; i < bp->cp_nr_rings; i++) { - num_str = ARRAY_SIZE(bnxt_ring_stats_str); - for (j = 0; j < num_str; j++) { - sprintf(buf, "[%d]: %s", i, - bnxt_ring_stats_str[j]); - buf += ETH_GSTRING_LEN; + if (is_rx_ring(bp, i)) { + num_str = NUM_RING_RX_HW_STATS; + for (j = 0; j < num_str; j++) { + sprintf(buf, "[%d]: %s", i, + bnxt_ring_rx_stats_str[j]); + buf += ETH_GSTRING_LEN; + } } - if (!BNXT_SUPPORTS_TPA(bp)) + if (is_tx_ring(bp, i)) { + num_str = NUM_RING_TX_HW_STATS; + for (j = 0; j < num_str; j++) { + sprintf(buf, "[%d]: %s", i, + bnxt_ring_tx_stats_str[j]); + buf += ETH_GSTRING_LEN; + } + } + num_str = bnxt_get_num_tpa_ring_stats(bp); + if (!num_str || !is_rx_ring(bp, i)) goto skip_tpa_stats; - if (bp->max_tpa_v2) { - num_str = ARRAY_SIZE(bnxt_ring_tpa2_stats_str); + if (bp->max_tpa_v2) str = bnxt_ring_tpa2_stats_str; - } else { - num_str = ARRAY_SIZE(bnxt_ring_tpa_stats_str); + else str = bnxt_ring_tpa_stats_str; - } + for (j = 0; j < num_str; j++) { sprintf(buf, "[%d]: %s", i, str[j]); buf += ETH_GSTRING_LEN; } skip_tpa_stats: - num_str = ARRAY_SIZE(bnxt_ring_sw_stats_str); + if (is_rx_ring(bp, i)) { + num_str = NUM_RING_RX_SW_STATS; + for (j = 0; j < num_str; j++) { + sprintf(buf, "[%d]: %s", i, + bnxt_rx_sw_stats_str[j]); + buf += ETH_GSTRING_LEN; + } + } + num_str = NUM_RING_CMN_SW_STATS; for (j = 0; j < num_str; j++) { sprintf(buf, "[%d]: %s", i, - bnxt_ring_sw_stats_str[j]); + bnxt_cmn_sw_stats_str[j]); buf += ETH_GSTRING_LEN; } } @@ -1749,8 +1827,8 @@ static int bnxt_flash_nvram(struct net_device *dev, return rc; } -static int bnxt_firmware_reset(struct net_device *dev, - u16 dir_type) +static int bnxt_hwrm_firmware_reset(struct net_device *dev, u8 proc_type, + u8 self_reset, u8 flags) { struct hwrm_fw_reset_input req = {0}; struct bnxt *bp = netdev_priv(dev); @@ -1758,48 +1836,77 @@ static int bnxt_firmware_reset(struct net_device *dev, bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FW_RESET, -1, -1); + req.embedded_proc_type = proc_type; + req.selfrst_status = self_reset; + req.flags = flags; + + if (proc_type == FW_RESET_REQ_EMBEDDED_PROC_TYPE_AP) { + rc = hwrm_send_message_silent(bp, &req, sizeof(req), + HWRM_CMD_TIMEOUT); + } else { + rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (rc == -EACCES) + bnxt_print_admin_err(bp); + } + return rc; +} + +static int bnxt_firmware_reset(struct net_device *dev, + enum bnxt_nvm_directory_type dir_type) +{ + u8 self_reset = FW_RESET_REQ_SELFRST_STATUS_SELFRSTNONE; + u8 proc_type, flags = 0; + /* TODO: Address self-reset of APE/KONG/BONO/TANG or ungraceful reset */ /* (e.g. when firmware isn't already running) */ switch (dir_type) { case BNX_DIR_TYPE_CHIMP_PATCH: case BNX_DIR_TYPE_BOOTCODE: case BNX_DIR_TYPE_BOOTCODE_2: - req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_BOOT; + proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_BOOT; /* Self-reset ChiMP upon next PCIe reset: */ - req.selfrst_status = FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST; + self_reset = FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST; break; case BNX_DIR_TYPE_APE_FW: case BNX_DIR_TYPE_APE_PATCH: - req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_MGMT; + proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_MGMT; /* Self-reset APE upon next PCIe reset: */ - req.selfrst_status = FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST; + self_reset = FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST; break; case BNX_DIR_TYPE_KONG_FW: case BNX_DIR_TYPE_KONG_PATCH: - req.embedded_proc_type = - FW_RESET_REQ_EMBEDDED_PROC_TYPE_NETCTRL; + proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_NETCTRL; break; case BNX_DIR_TYPE_BONO_FW: case BNX_DIR_TYPE_BONO_PATCH: - req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_ROCE; - break; - case BNXT_FW_RESET_CHIP: - req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_CHIP; - req.selfrst_status = FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP; - if (bp->fw_cap & BNXT_FW_CAP_HOT_RESET) - req.flags = FW_RESET_REQ_FLAGS_RESET_GRACEFUL; - break; - case BNXT_FW_RESET_AP: - req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_AP; + proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_ROCE; break; default: return -EINVAL; } - rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); - if (rc == -EACCES) - bnxt_print_admin_err(bp); - return rc; + return bnxt_hwrm_firmware_reset(dev, proc_type, self_reset, flags); +} + +static int bnxt_firmware_reset_chip(struct net_device *dev) +{ + struct bnxt *bp = netdev_priv(dev); + u8 flags = 0; + + if (bp->fw_cap & BNXT_FW_CAP_HOT_RESET) + flags = FW_RESET_REQ_FLAGS_RESET_GRACEFUL; + + return bnxt_hwrm_firmware_reset(dev, + FW_RESET_REQ_EMBEDDED_PROC_TYPE_CHIP, + FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP, + flags); +} + +static int bnxt_firmware_reset_ap(struct net_device *dev) +{ + return bnxt_hwrm_firmware_reset(dev, FW_RESET_REQ_EMBEDDED_PROC_TYPE_AP, + FW_RESET_REQ_SELFRST_STATUS_SELFRSTNONE, + 0); } static int bnxt_flash_firmware(struct net_device *dev, @@ -1988,9 +2095,9 @@ static int bnxt_flash_firmware_from_file(struct net_device *dev, rc, filename); return rc; } - if (bnxt_dir_type_is_ape_bin_format(dir_type) == true) + if (bnxt_dir_type_is_ape_bin_format(dir_type)) rc = bnxt_flash_firmware(dev, dir_type, fw->data, fw->size); - else if (bnxt_dir_type_is_other_exec_format(dir_type) == true) + else if (bnxt_dir_type_is_other_exec_format(dir_type)) rc = bnxt_flash_microcode(dev, dir_type, fw->data, fw->size); else rc = bnxt_flash_nvram(dev, dir_type, BNX_DIR_ORDINAL_FIRST, @@ -2378,7 +2485,7 @@ static int bnxt_set_eeprom(struct net_device *dev, } /* Create or re-write an NVM item: */ - if (bnxt_dir_type_is_executable(type) == true) + if (bnxt_dir_type_is_executable(type)) return -EOPNOTSUPP; ext = eeprom->magic & 0xffff; ordinal = eeprom->offset >> 16; @@ -2976,7 +3083,11 @@ static void bnxt_self_test(struct net_device *dev, struct ethtool_test *etest, static int bnxt_reset(struct net_device *dev, u32 *flags) { struct bnxt *bp = netdev_priv(dev); - int rc = 0; + bool reload = false; + u32 req = *flags; + + if (!req) + return -EINVAL; if (!BNXT_PF(bp)) { netdev_err(dev, "Reset is not supported from a VF\n"); @@ -2990,33 +3101,37 @@ static int bnxt_reset(struct net_device *dev, u32 *flags) return -EBUSY; } - if (*flags == ETH_RESET_ALL) { + if ((req & BNXT_FW_RESET_CHIP) == BNXT_FW_RESET_CHIP) { /* This feature is not supported in older firmware versions */ - if (bp->hwrm_spec_code < 0x10803) - return -EOPNOTSUPP; - - rc = bnxt_firmware_reset(dev, BNXT_FW_RESET_CHIP); - if (!rc) { - netdev_info(dev, "Reset request successful.\n"); - if (!(bp->fw_cap & BNXT_FW_CAP_HOT_RESET)) - netdev_info(dev, "Reload driver to complete reset\n"); - *flags = 0; + if (bp->hwrm_spec_code >= 0x10803) { + if (!bnxt_firmware_reset_chip(dev)) { + netdev_info(dev, "Firmware reset request successful.\n"); + if (!(bp->fw_cap & BNXT_FW_CAP_HOT_RESET)) + reload = true; + *flags &= ~BNXT_FW_RESET_CHIP; + } + } else if (req == BNXT_FW_RESET_CHIP) { + return -EOPNOTSUPP; /* only request, fail hard */ } - } else if (*flags == ETH_RESET_AP) { - /* This feature is not supported in older firmware versions */ - if (bp->hwrm_spec_code < 0x10803) - return -EOPNOTSUPP; + } - rc = bnxt_firmware_reset(dev, BNXT_FW_RESET_AP); - if (!rc) { - netdev_info(dev, "Reset Application Processor request successful.\n"); - *flags = 0; + if (req & BNXT_FW_RESET_AP) { + /* This feature is not supported in older firmware versions */ + if (bp->hwrm_spec_code >= 0x10803) { + if (!bnxt_firmware_reset_ap(dev)) { + netdev_info(dev, "Reset application processor successful.\n"); + reload = true; + *flags &= ~BNXT_FW_RESET_AP; + } + } else if (req == BNXT_FW_RESET_AP) { + return -EOPNOTSUPP; /* only request, fail hard */ } - } else { - rc = -EINVAL; } - return rc; + if (reload) + netdev_info(dev, "Reload driver to complete reset\n"); + + return 0; } static int bnxt_hwrm_dbg_dma_data(struct bnxt *bp, void *msg, int msg_len, diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h index 3576d951727b..ce7585ff9e4d 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h @@ -77,8 +77,12 @@ struct hwrm_dbg_cmn_output { #define BNXT_LED_DFLT_ENABLES(x) \ cpu_to_le32(BNXT_LED_DFLT_ENA << (BNXT_LED_DFLT_ENA_SHIFT * (x))) -#define BNXT_FW_RESET_AP 0xfffe -#define BNXT_FW_RESET_CHIP 0xffff +#define BNXT_FW_RESET_AP (ETH_RESET_AP << ETH_RESET_SHARED_SHIFT) +#define BNXT_FW_RESET_CHIP ((ETH_RESET_MGMT | ETH_RESET_IRQ | \ + ETH_RESET_DMA | ETH_RESET_FILTER | \ + ETH_RESET_OFFLOAD | ETH_RESET_MAC | \ + ETH_RESET_PHY | ETH_RESET_RAM) \ + << ETH_RESET_SHARED_SHIFT) extern const struct ethtool_ops bnxt_ethtool_ops; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h index 7cf27dffadb5..7e9235c8d21e 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h @@ -2,7 +2,7 @@ * * Copyright (c) 2014-2016 Broadcom Corporation * Copyright (c) 2014-2018 Broadcom Limited - * Copyright (c) 2018-2019 Broadcom Inc. + * Copyright (c) 2018-2020 Broadcom Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -207,6 +207,8 @@ struct cmd_nums { #define HWRM_PORT_PHY_MDIO_READ 0xb6UL #define HWRM_PORT_PHY_MDIO_BUS_ACQUIRE 0xb7UL #define HWRM_PORT_PHY_MDIO_BUS_RELEASE 0xb8UL + #define HWRM_PORT_QSTATS_EXT_PFC_WD 0xb9UL + #define HWRM_PORT_ECN_QSTATS 0xbaUL #define HWRM_FW_RESET 0xc0UL #define HWRM_FW_QSTATUS 0xc1UL #define HWRM_FW_HEALTH_CHECK 0xc2UL @@ -220,6 +222,8 @@ struct cmd_nums { #define HWRM_FW_SET_STRUCTURED_DATA 0xcaUL #define HWRM_FW_GET_STRUCTURED_DATA 0xcbUL #define HWRM_FW_IPC_MAILBOX 0xccUL + #define HWRM_FW_ECN_CFG 0xcdUL + #define HWRM_FW_ECN_QCFG 0xceUL #define HWRM_EXEC_FWD_RESP 0xd0UL #define HWRM_REJECT_FWD_RESP 0xd1UL #define HWRM_FWD_RESP 0xd2UL @@ -233,6 +237,7 @@ struct cmd_nums { #define HWRM_TEMP_MONITOR_QUERY 0xe0UL #define HWRM_REG_POWER_QUERY 0xe1UL #define HWRM_CORE_FREQUENCY_QUERY 0xe2UL + #define HWRM_REG_POWER_HISTOGRAM 0xe3UL #define HWRM_WOL_FILTER_ALLOC 0xf0UL #define HWRM_WOL_FILTER_FREE 0xf1UL #define HWRM_WOL_FILTER_QCFG 0xf2UL @@ -331,6 +336,7 @@ struct cmd_nums { #define HWRM_FUNC_VF_BW_CFG 0x195UL #define HWRM_FUNC_VF_BW_QCFG 0x196UL #define HWRM_FUNC_HOST_PF_IDS_QUERY 0x197UL + #define HWRM_FUNC_QSTATS_EXT 0x198UL #define HWRM_SELFTEST_QLIST 0x200UL #define HWRM_SELFTEST_EXEC 0x201UL #define HWRM_SELFTEST_IRQ 0x202UL @@ -341,6 +347,31 @@ struct cmd_nums { #define HWRM_MFG_OTP_CFG 0x207UL #define HWRM_MFG_OTP_QCFG 0x208UL #define HWRM_MFG_HDMA_TEST 0x209UL + #define HWRM_MFG_FRU_EEPROM_WRITE 0x20aUL + #define HWRM_MFG_FRU_EEPROM_READ 0x20bUL + #define HWRM_TF 0x2bcUL + #define HWRM_TF_VERSION_GET 0x2bdUL + #define HWRM_TF_SESSION_OPEN 0x2c6UL + #define HWRM_TF_SESSION_ATTACH 0x2c7UL + #define HWRM_TF_SESSION_CLOSE 0x2c8UL + #define HWRM_TF_SESSION_QCFG 0x2c9UL + #define HWRM_TF_SESSION_RESC_QCAPS 0x2caUL + #define HWRM_TF_SESSION_RESC_ALLOC 0x2cbUL + #define HWRM_TF_SESSION_RESC_FREE 0x2ccUL + #define HWRM_TF_SESSION_RESC_FLUSH 0x2cdUL + #define HWRM_TF_TBL_TYPE_GET 0x2d0UL + #define HWRM_TF_TBL_TYPE_SET 0x2d1UL + #define HWRM_TF_CTXT_MEM_RGTR 0x2daUL + #define HWRM_TF_CTXT_MEM_UNRGTR 0x2dbUL + #define HWRM_TF_EXT_EM_QCAPS 0x2dcUL + #define HWRM_TF_EXT_EM_OP 0x2ddUL + #define HWRM_TF_EXT_EM_CFG 0x2deUL + #define HWRM_TF_EXT_EM_QCFG 0x2dfUL + #define HWRM_TF_TCAM_SET 0x2eeUL + #define HWRM_TF_TCAM_GET 0x2efUL + #define HWRM_TF_TCAM_MOVE 0x2f0UL + #define HWRM_TF_TCAM_FREE 0x2f1UL + #define HWRM_SV 0x400UL #define HWRM_DBG_READ_DIRECT 0xff10UL #define HWRM_DBG_READ_INDIRECT 0xff11UL #define HWRM_DBG_WRITE_DIRECT 0xff12UL @@ -356,6 +387,10 @@ struct cmd_nums { #define HWRM_DBG_RING_INFO_GET 0xff1cUL #define HWRM_DBG_CRASHDUMP_HEADER 0xff1dUL #define HWRM_DBG_CRASHDUMP_ERASE 0xff1eUL + #define HWRM_DBG_DRV_TRACE 0xff1fUL + #define HWRM_DBG_QCAPS 0xff20UL + #define HWRM_DBG_QCFG 0xff21UL + #define HWRM_DBG_CRASHDUMP_MEDIUM_CFG 0xff22UL #define HWRM_NVM_FACTORY_DEFAULTS 0xffeeUL #define HWRM_NVM_VALIDATE_OPTION 0xffefUL #define HWRM_NVM_FLUSH 0xfff0UL @@ -429,8 +464,8 @@ struct hwrm_err_output { #define HWRM_VERSION_MAJOR 1 #define HWRM_VERSION_MINOR 10 #define HWRM_VERSION_UPDATE 1 -#define HWRM_VERSION_RSVD 12 -#define HWRM_VERSION_STR "1.10.1.12" +#define HWRM_VERSION_RSVD 33 +#define HWRM_VERSION_STR "1.10.1.33" /* hwrm_ver_get_input (size:192b/24B) */ struct hwrm_ver_get_input { @@ -482,6 +517,7 @@ struct hwrm_ver_get_output { #define VER_GET_RESP_DEV_CAPS_CFG_CFA_EEM_SUPPORTED 0x800UL #define VER_GET_RESP_DEV_CAPS_CFG_CFA_ADV_FLOW_MGNT_SUPPORTED 0x1000UL #define VER_GET_RESP_DEV_CAPS_CFG_CFA_TFLIB_SUPPORTED 0x2000UL + #define VER_GET_RESP_DEV_CAPS_CFG_CFA_TRUFLOW_SUPPORTED 0x4000UL u8 roce_fw_maj_8b; u8 roce_fw_min_8b; u8 roce_fw_bld_8b; @@ -647,6 +683,7 @@ struct hwrm_async_event_cmpl { #define ASYNC_EVENT_CMPL_EVENT_ID_TFLIB_LINK_STATUS_CHANGE 0x3eUL #define ASYNC_EVENT_CMPL_EVENT_ID_QUIESCE_DONE 0x3fUL #define ASYNC_EVENT_CMPL_EVENT_ID_DEFERRED_RESPONSE 0x40UL + #define ASYNC_EVENT_CMPL_EVENT_ID_PFC_WATCHDOG_CFG_CHANGE 0x41UL #define ASYNC_EVENT_CMPL_EVENT_ID_FW_TRACE_MSG 0xfeUL #define ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR 0xffUL #define ASYNC_EVENT_CMPL_EVENT_ID_LAST ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR @@ -1089,7 +1126,7 @@ struct hwrm_func_qcaps_input { u8 unused_0[6]; }; -/* hwrm_func_qcaps_output (size:640b/80B) */ +/* hwrm_func_qcaps_output (size:704b/88B) */ struct hwrm_func_qcaps_output { __le16 error_code; __le16 req_type; @@ -1126,6 +1163,10 @@ struct hwrm_func_qcaps_output { #define FUNC_QCAPS_RESP_FLAGS_ERR_RECOVER_RELOAD 0x2000000UL #define FUNC_QCAPS_RESP_FLAGS_NOTIFY_VF_DEF_VNIC_CHNG_SUPPORTED 0x4000000UL #define FUNC_QCAPS_RESP_FLAGS_VLAN_ACCELERATION_TX_DISABLED 0x8000000UL + #define FUNC_QCAPS_RESP_FLAGS_COREDUMP_CMD_SUPPORTED 0x10000000UL + #define FUNC_QCAPS_RESP_FLAGS_CRASHDUMP_CMD_SUPPORTED 0x20000000UL + #define FUNC_QCAPS_RESP_FLAGS_PFC_WD_STATS_SUPPORTED 0x40000000UL + #define FUNC_QCAPS_RESP_FLAGS_DBG_QCAPS_CMD_SUPPORTED 0x80000000UL u8 mac_address[6]; __le16 max_rsscos_ctx; __le16 max_cmpl_rings; @@ -1146,7 +1187,12 @@ struct hwrm_func_qcaps_output { __le32 max_flow_id; __le32 max_hw_ring_grps; __le16 max_sp_tx_rings; - u8 unused_0; + u8 unused_0[2]; + __le32 flags_ext; + #define FUNC_QCAPS_RESP_FLAGS_EXT_ECN_MARK_SUPPORTED 0x1UL + #define FUNC_QCAPS_RESP_FLAGS_EXT_ECN_STATS_SUPPORTED 0x2UL + #define FUNC_QCAPS_RESP_FLAGS_EXT_EXT_HW_STATS_SUPPORTED 0x4UL + u8 unused_1[3]; u8 valid; }; @@ -1161,7 +1207,7 @@ struct hwrm_func_qcfg_input { u8 unused_0[6]; }; -/* hwrm_func_qcfg_output (size:704b/88B) */ +/* hwrm_func_qcfg_output (size:768b/96B) */ struct hwrm_func_qcfg_output { __le16 error_code; __le16 req_type; @@ -1267,7 +1313,11 @@ struct hwrm_func_qcfg_output { u8 always_1; __le32 reset_addr_poll; __le16 legacy_l2_db_size_kb; - u8 unused_2[1]; + __le16 svif_info; + #define FUNC_QCFG_RESP_SVIF_INFO_SVIF_MASK 0x7fffUL + #define FUNC_QCFG_RESP_SVIF_INFO_SVIF_SFT 0 + #define FUNC_QCFG_RESP_SVIF_INFO_SVIF_VALID 0x8000UL + u8 unused_2[7]; u8 valid; }; @@ -1420,9 +1470,10 @@ struct hwrm_func_qstats_input { __le64 resp_addr; __le16 fid; u8 flags; - #define FUNC_QSTATS_REQ_FLAGS_UNUSED 0x0UL - #define FUNC_QSTATS_REQ_FLAGS_ROCE_ONLY 0x1UL - #define FUNC_QSTATS_REQ_FLAGS_LAST FUNC_QSTATS_REQ_FLAGS_ROCE_ONLY + #define FUNC_QSTATS_REQ_FLAGS_UNUSED 0x0UL + #define FUNC_QSTATS_REQ_FLAGS_ROCE_ONLY 0x1UL + #define FUNC_QSTATS_REQ_FLAGS_COUNTER_MASK 0x2UL + #define FUNC_QSTATS_REQ_FLAGS_LAST FUNC_QSTATS_REQ_FLAGS_COUNTER_MASK u8 unused_0[5]; }; @@ -1456,6 +1507,53 @@ struct hwrm_func_qstats_output { u8 valid; }; +/* hwrm_func_qstats_ext_input (size:192b/24B) */ +struct hwrm_func_qstats_ext_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 fid; + u8 flags; + #define FUNC_QSTATS_EXT_REQ_FLAGS_UNUSED 0x0UL + #define FUNC_QSTATS_EXT_REQ_FLAGS_ROCE_ONLY 0x1UL + #define FUNC_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK 0x2UL + #define FUNC_QSTATS_EXT_REQ_FLAGS_LAST FUNC_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK + u8 unused_0[5]; +}; + +/* hwrm_func_qstats_ext_output (size:1472b/184B) */ +struct hwrm_func_qstats_ext_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le64 rx_ucast_pkts; + __le64 rx_mcast_pkts; + __le64 rx_bcast_pkts; + __le64 rx_discard_pkts; + __le64 rx_drop_pkts; + __le64 rx_ucast_bytes; + __le64 rx_mcast_bytes; + __le64 rx_bcast_bytes; + __le64 tx_ucast_pkts; + __le64 tx_mcast_pkts; + __le64 tx_bcast_pkts; + __le64 tx_discard_pkts; + __le64 tx_drop_pkts; + __le64 tx_ucast_bytes; + __le64 tx_mcast_bytes; + __le64 tx_bcast_bytes; + __le64 rx_tpa_eligible_pkt; + __le64 rx_tpa_eligible_bytes; + __le64 rx_tpa_pkt; + __le64 rx_tpa_bytes; + __le64 rx_tpa_errors; + u8 unused_0[7]; + u8 valid; +}; + /* hwrm_func_clr_stats_input (size:192b/24B) */ struct hwrm_func_clr_stats_input { __le16 req_type; @@ -1808,7 +1906,7 @@ struct hwrm_func_backing_store_qcaps_output { u8 ctx_kind_initializer; __le32 rsvd; __le16 rsvd1; - u8 rsvd2; + u8 tqm_fp_rings_count; u8 valid; }; @@ -2231,7 +2329,17 @@ struct hwrm_error_recovery_qcfg_output { #define ERROR_RECOVERY_QCFG_RESP_RESET_REG_ADDR_SFT 2 __le32 reset_reg_val[16]; u8 delay_after_reset[16]; - u8 unused_1[7]; + __le32 err_recovery_cnt_reg; + #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SPACE_MASK 0x3UL + #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SPACE_SFT 0 + #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SPACE_PCIE_CFG 0x0UL + #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SPACE_GRC 0x1UL + #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SPACE_BAR0 0x2UL + #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SPACE_BAR1 0x3UL + #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SPACE_LAST ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SPACE_BAR1 + #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_MASK 0xfffffffcUL + #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SFT 2 + u8 unused_1[3]; u8 valid; }; @@ -2934,7 +3042,11 @@ struct hwrm_port_qstats_input { __le16 target_id; __le64 resp_addr; __le16 port_id; - u8 unused_0[6]; + u8 flags; + #define PORT_QSTATS_REQ_FLAGS_UNUSED 0x0UL + #define PORT_QSTATS_REQ_FLAGS_COUNTER_MASK 0x1UL + #define PORT_QSTATS_REQ_FLAGS_LAST PORT_QSTATS_REQ_FLAGS_COUNTER_MASK + u8 unused_0[5]; __le64 tx_stat_host_addr; __le64 rx_stat_host_addr; }; @@ -3058,7 +3170,11 @@ struct hwrm_port_qstats_ext_input { __le16 port_id; __le16 tx_stat_size; __le16 rx_stat_size; - u8 unused_0[2]; + u8 flags; + #define PORT_QSTATS_EXT_REQ_FLAGS_UNUSED 0x0UL + #define PORT_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK 0x1UL + #define PORT_QSTATS_EXT_REQ_FLAGS_LAST PORT_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK + u8 unused_0; __le64 tx_stat_host_addr; __le64 rx_stat_host_addr; }; @@ -3840,14 +3956,22 @@ struct hwrm_queue_pfcenable_qcfg_output { __le16 seq_id; __le16 resp_len; __le32 flags; - #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI0_PFC_ENABLED 0x1UL - #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI1_PFC_ENABLED 0x2UL - #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI2_PFC_ENABLED 0x4UL - #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI3_PFC_ENABLED 0x8UL - #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI4_PFC_ENABLED 0x10UL - #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI5_PFC_ENABLED 0x20UL - #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI6_PFC_ENABLED 0x40UL - #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI7_PFC_ENABLED 0x80UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI0_PFC_ENABLED 0x1UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI1_PFC_ENABLED 0x2UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI2_PFC_ENABLED 0x4UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI3_PFC_ENABLED 0x8UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI4_PFC_ENABLED 0x10UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI5_PFC_ENABLED 0x20UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI6_PFC_ENABLED 0x40UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI7_PFC_ENABLED 0x80UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI0_PFC_WATCHDOG_ENABLED 0x100UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI1_PFC_WATCHDOG_ENABLED 0x200UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI2_PFC_WATCHDOG_ENABLED 0x400UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI3_PFC_WATCHDOG_ENABLED 0x800UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI4_PFC_WATCHDOG_ENABLED 0x1000UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI5_PFC_WATCHDOG_ENABLED 0x2000UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI6_PFC_WATCHDOG_ENABLED 0x4000UL + #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI7_PFC_WATCHDOG_ENABLED 0x8000UL u8 unused_0[3]; u8 valid; }; @@ -3860,14 +3984,22 @@ struct hwrm_queue_pfcenable_cfg_input { __le16 target_id; __le64 resp_addr; __le32 flags; - #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI0_PFC_ENABLED 0x1UL - #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI1_PFC_ENABLED 0x2UL - #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI2_PFC_ENABLED 0x4UL - #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI3_PFC_ENABLED 0x8UL - #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI4_PFC_ENABLED 0x10UL - #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI5_PFC_ENABLED 0x20UL - #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI6_PFC_ENABLED 0x40UL - #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI7_PFC_ENABLED 0x80UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI0_PFC_ENABLED 0x1UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI1_PFC_ENABLED 0x2UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI2_PFC_ENABLED 0x4UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI3_PFC_ENABLED 0x8UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI4_PFC_ENABLED 0x10UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI5_PFC_ENABLED 0x20UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI6_PFC_ENABLED 0x40UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI7_PFC_ENABLED 0x80UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI0_PFC_WATCHDOG_ENABLED 0x100UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI1_PFC_WATCHDOG_ENABLED 0x200UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI2_PFC_WATCHDOG_ENABLED 0x400UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI3_PFC_WATCHDOG_ENABLED 0x800UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI4_PFC_WATCHDOG_ENABLED 0x1000UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI5_PFC_WATCHDOG_ENABLED 0x2000UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI6_PFC_WATCHDOG_ENABLED 0x4000UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI7_PFC_WATCHDOG_ENABLED 0x8000UL __le16 port_id; u8 unused_0[2]; }; @@ -5287,7 +5419,11 @@ struct hwrm_ring_cmpl_ring_qaggint_params_input { __le16 target_id; __le64 resp_addr; __le16 ring_id; - u8 unused_0[6]; + __le16 flags; + #define RING_CMPL_RING_QAGGINT_PARAMS_REQ_FLAGS_UNUSED_0_MASK 0x3UL + #define RING_CMPL_RING_QAGGINT_PARAMS_REQ_FLAGS_UNUSED_0_SFT 0 + #define RING_CMPL_RING_QAGGINT_PARAMS_REQ_FLAGS_IS_NQ 0x4UL + u8 unused_0[4]; }; /* hwrm_ring_cmpl_ring_qaggint_params_output (size:256b/32B) */ @@ -7618,7 +7754,9 @@ struct hwrm_nvm_modify_input { __le64 resp_addr; __le64 host_src_addr; __le16 dir_idx; - u8 unused_0[2]; + __le16 flags; + #define NVM_MODIFY_REQ_FLAGS_BATCH_MODE 0x1UL + #define NVM_MODIFY_REQ_FLAGS_BATCH_LAST 0x2UL __le32 offset; __le32 len; u8 unused_1[4]; @@ -8027,4 +8165,18 @@ struct hwrm_selftest_irq_output { u8 valid; }; +/* fw_status_reg (size:32b/4B) */ +struct fw_status_reg { + u32 fw_status; + #define FW_STATUS_REG_CODE_MASK 0xffffUL + #define FW_STATUS_REG_CODE_SFT 0 + #define FW_STATUS_REG_CODE_READY 0x8000UL + #define FW_STATUS_REG_CODE_LAST FW_STATUS_REG_CODE_READY + #define FW_STATUS_REG_IMAGE_DEGRADED 0x10000UL + #define FW_STATUS_REG_RECOVERABLE 0x20000UL + #define FW_STATUS_REG_CRASHDUMP_ONGOING 0x40000UL + #define FW_STATUS_REG_CRASHDUMP_COMPLETE 0x80000UL + #define FW_STATUS_REG_SHUTDOWN 0x100000UL +}; + #endif /* _BNXT_HSI_H_ */ diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c index cea2f9958a1d..3a9a51f7063a 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c @@ -645,7 +645,7 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int num_vfs) FUNC_CFG_REQ_ENABLES_NUM_VNICS | FUNC_CFG_REQ_ENABLES_NUM_HW_RING_GRPS); - mtu = bp->dev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + mtu = bp->dev->mtu + ETH_HLEN + VLAN_HLEN; req.mru = cpu_to_le16(mtu); req.mtu = cpu_to_le16(mtu); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c index 782ea0771221..0eef4f5e4a46 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c @@ -1939,53 +1939,25 @@ static int bnxt_tc_setup_indr_block(struct net_device *netdev, struct bnxt *bp, return 0; } -static int bnxt_tc_setup_indr_cb(struct net_device *netdev, void *cb_priv, - enum tc_setup_type type, void *type_data) -{ - switch (type) { - case TC_SETUP_BLOCK: - return bnxt_tc_setup_indr_block(netdev, cb_priv, type_data); - default: - return -EOPNOTSUPP; - } -} - static bool bnxt_is_netdev_indr_offload(struct net_device *netdev) { return netif_is_vxlan(netdev); } -static int bnxt_tc_indr_block_event(struct notifier_block *nb, - unsigned long event, void *ptr) +static int bnxt_tc_setup_indr_cb(struct net_device *netdev, void *cb_priv, + enum tc_setup_type type, void *type_data) { - struct net_device *netdev; - struct bnxt *bp; - int rc; - - netdev = netdev_notifier_info_to_dev(ptr); if (!bnxt_is_netdev_indr_offload(netdev)) - return NOTIFY_OK; - - bp = container_of(nb, struct bnxt, tc_netdev_nb); + return -EOPNOTSUPP; - switch (event) { - case NETDEV_REGISTER: - rc = __flow_indr_block_cb_register(netdev, bp, - bnxt_tc_setup_indr_cb, - bp); - if (rc) - netdev_info(bp->dev, - "Failed to register indirect blk: dev: %s\n", - netdev->name); - break; - case NETDEV_UNREGISTER: - __flow_indr_block_cb_unregister(netdev, - bnxt_tc_setup_indr_cb, - bp); + switch (type) { + case TC_SETUP_BLOCK: + return bnxt_tc_setup_indr_block(netdev, cb_priv, type_data); + default: break; } - return NOTIFY_DONE; + return -EOPNOTSUPP; } static const struct rhashtable_params bnxt_tc_flow_ht_params = { @@ -2074,8 +2046,8 @@ int bnxt_init_tc(struct bnxt *bp) /* init indirect block notifications */ INIT_LIST_HEAD(&bp->tc_indr_block_list); - bp->tc_netdev_nb.notifier_call = bnxt_tc_indr_block_event; - rc = register_netdevice_notifier(&bp->tc_netdev_nb); + + rc = flow_indr_dev_register(bnxt_tc_setup_indr_cb, bp); if (!rc) return 0; @@ -2101,7 +2073,8 @@ void bnxt_shutdown_tc(struct bnxt *bp) if (!bnxt_tc_flower_enabled(bp)) return; - unregister_netdevice_notifier(&bp->tc_netdev_nb); + flow_indr_dev_unregister(bnxt_tc_setup_indr_cb, bp, + bnxt_tc_setup_indr_block_cb); rhashtable_destroy(&tc_info->flow_table); rhashtable_destroy(&tc_info->l2_table); rhashtable_destroy(&tc_info->decap_l2_table); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c index 4a316c4b3fa8..8c8368c2f335 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c @@ -104,7 +104,13 @@ static void bnxt_fill_msix_vecs(struct bnxt *bp, struct bnxt_msix_entry *ent) for (i = 0; i < num_msix; i++) { ent[i].vector = bp->irq_tbl[idx + i].vector; ent[i].ring_idx = idx + i; - ent[i].db_offset = (idx + i) * 0x80; + if (bp->flags & BNXT_FLAG_CHIP_P5) { + ent[i].db_offset = DB_PF_OFFSET_P5; + if (BNXT_VF(bp)) + ent[i].db_offset = DB_VF_OFFSET_P5; + } else { + ent[i].db_offset = (idx + i) * 0x80; + } } } @@ -475,6 +481,8 @@ struct bnxt_en_dev *bnxt_ulp_probe(struct net_device *dev) edev->flags |= BNXT_EN_FLAG_ROCEV2_CAP; edev->net = dev; edev->pdev = bp->pdev; + edev->l2_db_size = bp->db_size; + edev->l2_db_size_nc = bp->db_size; bp->edev = edev; } return bp->edev; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h index 9895406b9830..6b4d2556a6df 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h @@ -67,6 +67,14 @@ struct bnxt_en_dev { #define BNXT_EN_FLAG_ULP_STOPPED 0x8 const struct bnxt_en_ops *en_ops; struct bnxt_ulp ulp_tbl[BNXT_MAX_ULP]; + int l2_db_size; /* Doorbell BAR size in + * bytes mapped by L2 + * driver. + */ + int l2_db_size_nc; /* Doorbell BAR size in + * bytes mapped as non- + * cacheable. + */ }; struct bnxt_en_ops { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c index c6f6f2033880..5e3b4a3b69ea 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c @@ -138,6 +138,7 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons, xdp_set_data_meta_invalid(&xdp); xdp.data_end = *data_ptr + *len; xdp.rxq = &rxr->xdp_rxq; + xdp.frame_sz = PAGE_SIZE; /* BNXT_RX_PAGE_MODE(bp) when XDP enabled */ orig_data = xdp.data; rcu_read_lock(); diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index 61ab7d21f6bd..c5cca63b8571 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -1918,7 +1918,6 @@ static int cnic_bnx2x_iscsi_ofld1(struct cnic_dev *dev, struct kwqe *wqes[], ret = cnic_alloc_bnx2x_conn_resc(dev, l5_cid); if (ret) { atomic_dec(&cp->iscsi_conn); - ret = 0; goto done; } ret = cnic_setup_bnx2x_ctx(dev, wqes, num); diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 79636c78127c..ff31da0ed846 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -2,7 +2,7 @@ /* * Broadcom GENET (Gigabit Ethernet) controller driver * - * Copyright (c) 2014-2019 Broadcom + * Copyright (c) 2014-2020 Broadcom */ #define pr_fmt(fmt) "bcmgenet: " fmt @@ -23,11 +23,6 @@ #include <linux/dma-mapping.h> #include <linux/pm.h> #include <linux/clk.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> -#include <linux/of_net.h> -#include <linux/of_platform.h> #include <net/arp.h> #include <linux/mii.h> @@ -70,6 +65,9 @@ #define GENET_RDMA_REG_OFF (priv->hw_params->rdma_offset + \ TOTAL_DESC * DMA_DESC_SIZE) +/* Forward declarations */ +static void bcmgenet_set_rx_mode(struct net_device *dev); + static inline void bcmgenet_writel(u32 value, void __iomem *offset) { /* MIPS chips strapped for BE will automagically configure the @@ -461,6 +459,384 @@ static inline void bcmgenet_rdma_ring_writel(struct bcmgenet_priv *priv, genet_dma_ring_regs[r]); } +static bool bcmgenet_hfb_is_filter_enabled(struct bcmgenet_priv *priv, + u32 f_index) +{ + u32 offset; + u32 reg; + + offset = HFB_FLT_ENABLE_V3PLUS + (f_index < 32) * sizeof(u32); + reg = bcmgenet_hfb_reg_readl(priv, offset); + return !!(reg & (1 << (f_index % 32))); +} + +static void bcmgenet_hfb_enable_filter(struct bcmgenet_priv *priv, u32 f_index) +{ + u32 offset; + u32 reg; + + offset = HFB_FLT_ENABLE_V3PLUS + (f_index < 32) * sizeof(u32); + reg = bcmgenet_hfb_reg_readl(priv, offset); + reg |= (1 << (f_index % 32)); + bcmgenet_hfb_reg_writel(priv, reg, offset); + reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL); + reg |= RBUF_HFB_EN; + bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL); +} + +static void bcmgenet_hfb_disable_filter(struct bcmgenet_priv *priv, u32 f_index) +{ + u32 offset, reg, reg1; + + offset = HFB_FLT_ENABLE_V3PLUS; + reg = bcmgenet_hfb_reg_readl(priv, offset); + reg1 = bcmgenet_hfb_reg_readl(priv, offset + sizeof(u32)); + if (f_index < 32) { + reg1 &= ~(1 << (f_index % 32)); + bcmgenet_hfb_reg_writel(priv, reg1, offset + sizeof(u32)); + } else { + reg &= ~(1 << (f_index % 32)); + bcmgenet_hfb_reg_writel(priv, reg, offset); + } + if (!reg && !reg1) { + reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL); + reg &= ~RBUF_HFB_EN; + bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL); + } +} + +static void bcmgenet_hfb_set_filter_rx_queue_mapping(struct bcmgenet_priv *priv, + u32 f_index, u32 rx_queue) +{ + u32 offset; + u32 reg; + + offset = f_index / 8; + reg = bcmgenet_rdma_readl(priv, DMA_INDEX2RING_0 + offset); + reg &= ~(0xF << (4 * (f_index % 8))); + reg |= ((rx_queue & 0xF) << (4 * (f_index % 8))); + bcmgenet_rdma_writel(priv, reg, DMA_INDEX2RING_0 + offset); +} + +static void bcmgenet_hfb_set_filter_length(struct bcmgenet_priv *priv, + u32 f_index, u32 f_length) +{ + u32 offset; + u32 reg; + + offset = HFB_FLT_LEN_V3PLUS + + ((priv->hw_params->hfb_filter_cnt - 1 - f_index) / 4) * + sizeof(u32); + reg = bcmgenet_hfb_reg_readl(priv, offset); + reg &= ~(0xFF << (8 * (f_index % 4))); + reg |= ((f_length & 0xFF) << (8 * (f_index % 4))); + bcmgenet_hfb_reg_writel(priv, reg, offset); +} + +static int bcmgenet_hfb_find_unused_filter(struct bcmgenet_priv *priv) +{ + u32 f_index; + + /* First MAX_NUM_OF_FS_RULES are reserved for Rx NFC filters */ + for (f_index = MAX_NUM_OF_FS_RULES; + f_index < priv->hw_params->hfb_filter_cnt; f_index++) + if (!bcmgenet_hfb_is_filter_enabled(priv, f_index)) + return f_index; + + return -ENOMEM; +} + +static int bcmgenet_hfb_validate_mask(void *mask, size_t size) +{ + while (size) { + switch (*(unsigned char *)mask++) { + case 0x00: + case 0x0f: + case 0xf0: + case 0xff: + size--; + continue; + default: + return -EINVAL; + } + } + + return 0; +} + +#define VALIDATE_MASK(x) \ + bcmgenet_hfb_validate_mask(&(x), sizeof(x)) + +static int bcmgenet_hfb_insert_data(u32 *f, int offset, + void *val, void *mask, size_t size) +{ + int index; + u32 tmp; + + index = offset / 2; + tmp = f[index]; + + while (size--) { + if (offset++ & 1) { + tmp &= ~0x300FF; + tmp |= (*(unsigned char *)val++); + switch ((*(unsigned char *)mask++)) { + case 0xFF: + tmp |= 0x30000; + break; + case 0xF0: + tmp |= 0x20000; + break; + case 0x0F: + tmp |= 0x10000; + break; + } + f[index++] = tmp; + if (size) + tmp = f[index]; + } else { + tmp &= ~0xCFF00; + tmp |= (*(unsigned char *)val++) << 8; + switch ((*(unsigned char *)mask++)) { + case 0xFF: + tmp |= 0xC0000; + break; + case 0xF0: + tmp |= 0x80000; + break; + case 0x0F: + tmp |= 0x40000; + break; + } + if (!size) + f[index] = tmp; + } + } + + return 0; +} + +static void bcmgenet_hfb_set_filter(struct bcmgenet_priv *priv, u32 *f_data, + u32 f_length, u32 rx_queue, int f_index) +{ + u32 base = f_index * priv->hw_params->hfb_filter_size; + int i; + + for (i = 0; i < f_length; i++) + bcmgenet_hfb_writel(priv, f_data[i], (base + i) * sizeof(u32)); + + bcmgenet_hfb_set_filter_length(priv, f_index, 2 * f_length); + bcmgenet_hfb_set_filter_rx_queue_mapping(priv, f_index, rx_queue); +} + +static int bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv, + struct bcmgenet_rxnfc_rule *rule) +{ + struct ethtool_rx_flow_spec *fs = &rule->fs; + int err = 0, offset = 0, f_length = 0; + u16 val_16, mask_16; + u8 val_8, mask_8; + size_t size; + u32 *f_data; + + f_data = kcalloc(priv->hw_params->hfb_filter_size, sizeof(u32), + GFP_KERNEL); + if (!f_data) + return -ENOMEM; + + if (fs->flow_type & FLOW_MAC_EXT) { + bcmgenet_hfb_insert_data(f_data, 0, + &fs->h_ext.h_dest, &fs->m_ext.h_dest, + sizeof(fs->h_ext.h_dest)); + } + + if (fs->flow_type & FLOW_EXT) { + if (fs->m_ext.vlan_etype || + fs->m_ext.vlan_tci) { + bcmgenet_hfb_insert_data(f_data, 12, + &fs->h_ext.vlan_etype, + &fs->m_ext.vlan_etype, + sizeof(fs->h_ext.vlan_etype)); + bcmgenet_hfb_insert_data(f_data, 14, + &fs->h_ext.vlan_tci, + &fs->m_ext.vlan_tci, + sizeof(fs->h_ext.vlan_tci)); + offset += VLAN_HLEN; + f_length += DIV_ROUND_UP(VLAN_HLEN, 2); + } + } + + switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) { + case ETHER_FLOW: + f_length += DIV_ROUND_UP(ETH_HLEN, 2); + bcmgenet_hfb_insert_data(f_data, 0, + &fs->h_u.ether_spec.h_dest, + &fs->m_u.ether_spec.h_dest, + sizeof(fs->h_u.ether_spec.h_dest)); + bcmgenet_hfb_insert_data(f_data, ETH_ALEN, + &fs->h_u.ether_spec.h_source, + &fs->m_u.ether_spec.h_source, + sizeof(fs->h_u.ether_spec.h_source)); + bcmgenet_hfb_insert_data(f_data, (2 * ETH_ALEN) + offset, + &fs->h_u.ether_spec.h_proto, + &fs->m_u.ether_spec.h_proto, + sizeof(fs->h_u.ether_spec.h_proto)); + break; + case IP_USER_FLOW: + f_length += DIV_ROUND_UP(ETH_HLEN + 20, 2); + /* Specify IP Ether Type */ + val_16 = htons(ETH_P_IP); + mask_16 = 0xFFFF; + bcmgenet_hfb_insert_data(f_data, (2 * ETH_ALEN) + offset, + &val_16, &mask_16, sizeof(val_16)); + bcmgenet_hfb_insert_data(f_data, 15 + offset, + &fs->h_u.usr_ip4_spec.tos, + &fs->m_u.usr_ip4_spec.tos, + sizeof(fs->h_u.usr_ip4_spec.tos)); + bcmgenet_hfb_insert_data(f_data, 23 + offset, + &fs->h_u.usr_ip4_spec.proto, + &fs->m_u.usr_ip4_spec.proto, + sizeof(fs->h_u.usr_ip4_spec.proto)); + bcmgenet_hfb_insert_data(f_data, 26 + offset, + &fs->h_u.usr_ip4_spec.ip4src, + &fs->m_u.usr_ip4_spec.ip4src, + sizeof(fs->h_u.usr_ip4_spec.ip4src)); + bcmgenet_hfb_insert_data(f_data, 30 + offset, + &fs->h_u.usr_ip4_spec.ip4dst, + &fs->m_u.usr_ip4_spec.ip4dst, + sizeof(fs->h_u.usr_ip4_spec.ip4dst)); + if (!fs->m_u.usr_ip4_spec.l4_4_bytes) + break; + + /* Only supports 20 byte IPv4 header */ + val_8 = 0x45; + mask_8 = 0xFF; + bcmgenet_hfb_insert_data(f_data, ETH_HLEN + offset, + &val_8, &mask_8, + sizeof(val_8)); + size = sizeof(fs->h_u.usr_ip4_spec.l4_4_bytes); + bcmgenet_hfb_insert_data(f_data, + ETH_HLEN + 20 + offset, + &fs->h_u.usr_ip4_spec.l4_4_bytes, + &fs->m_u.usr_ip4_spec.l4_4_bytes, + size); + f_length += DIV_ROUND_UP(size, 2); + break; + } + + if (!fs->ring_cookie || fs->ring_cookie == RX_CLS_FLOW_WAKE) { + /* Ring 0 flows can be handled by the default Descriptor Ring + * We'll map them to ring 0, but don't enable the filter + */ + bcmgenet_hfb_set_filter(priv, f_data, f_length, 0, + fs->location); + rule->state = BCMGENET_RXNFC_STATE_DISABLED; + } else { + /* Other Rx rings are direct mapped here */ + bcmgenet_hfb_set_filter(priv, f_data, f_length, + fs->ring_cookie, fs->location); + bcmgenet_hfb_enable_filter(priv, fs->location); + rule->state = BCMGENET_RXNFC_STATE_ENABLED; + } + + kfree(f_data); + + return err; +} + +/* bcmgenet_hfb_add_filter + * + * Add new filter to Hardware Filter Block to match and direct Rx traffic to + * desired Rx queue. + * + * f_data is an array of unsigned 32-bit integers where each 32-bit integer + * provides filter data for 2 bytes (4 nibbles) of Rx frame: + * + * bits 31:20 - unused + * bit 19 - nibble 0 match enable + * bit 18 - nibble 1 match enable + * bit 17 - nibble 2 match enable + * bit 16 - nibble 3 match enable + * bits 15:12 - nibble 0 data + * bits 11:8 - nibble 1 data + * bits 7:4 - nibble 2 data + * bits 3:0 - nibble 3 data + * + * Example: + * In order to match: + * - Ethernet frame type = 0x0800 (IP) + * - IP version field = 4 + * - IP protocol field = 0x11 (UDP) + * + * The following filter is needed: + * u32 hfb_filter_ipv4_udp[] = { + * Rx frame offset 0x00: 0x00000000, 0x00000000, 0x00000000, 0x00000000, + * Rx frame offset 0x08: 0x00000000, 0x00000000, 0x000F0800, 0x00084000, + * Rx frame offset 0x10: 0x00000000, 0x00000000, 0x00000000, 0x00030011, + * }; + * + * To add the filter to HFB and direct the traffic to Rx queue 0, call: + * bcmgenet_hfb_add_filter(priv, hfb_filter_ipv4_udp, + * ARRAY_SIZE(hfb_filter_ipv4_udp), 0); + */ +int bcmgenet_hfb_add_filter(struct bcmgenet_priv *priv, u32 *f_data, + u32 f_length, u32 rx_queue) +{ + int f_index; + + f_index = bcmgenet_hfb_find_unused_filter(priv); + if (f_index < 0) + return -ENOMEM; + + if (f_length > priv->hw_params->hfb_filter_size) + return -EINVAL; + + bcmgenet_hfb_set_filter(priv, f_data, f_length, rx_queue, f_index); + bcmgenet_hfb_enable_filter(priv, f_index); + + return 0; +} + +/* bcmgenet_hfb_clear + * + * Clear Hardware Filter Block and disable all filtering. + */ +static void bcmgenet_hfb_clear(struct bcmgenet_priv *priv) +{ + u32 i; + + bcmgenet_hfb_reg_writel(priv, 0x0, HFB_CTRL); + bcmgenet_hfb_reg_writel(priv, 0x0, HFB_FLT_ENABLE_V3PLUS); + bcmgenet_hfb_reg_writel(priv, 0x0, HFB_FLT_ENABLE_V3PLUS + 4); + + for (i = DMA_INDEX2RING_0; i <= DMA_INDEX2RING_7; i++) + bcmgenet_rdma_writel(priv, 0x0, i); + + for (i = 0; i < (priv->hw_params->hfb_filter_cnt / 4); i++) + bcmgenet_hfb_reg_writel(priv, 0x0, + HFB_FLT_LEN_V3PLUS + i * sizeof(u32)); + + for (i = 0; i < priv->hw_params->hfb_filter_cnt * + priv->hw_params->hfb_filter_size; i++) + bcmgenet_hfb_writel(priv, 0x0, i * sizeof(u32)); +} + +static void bcmgenet_hfb_init(struct bcmgenet_priv *priv) +{ + int i; + + if (GENET_IS_V1(priv) || GENET_IS_V2(priv)) + return; + + INIT_LIST_HEAD(&priv->rxnfc_list); + for (i = 0; i < MAX_NUM_OF_FS_RULES; i++) { + INIT_LIST_HEAD(&priv->rxnfc_rules[i].list); + priv->rxnfc_rules[i].state = BCMGENET_RXNFC_STATE_UNUSED; + } + + bcmgenet_hfb_clear(priv); +} + static int bcmgenet_begin(struct net_device *dev) { struct bcmgenet_priv *priv = netdev_priv(dev); @@ -1045,6 +1421,229 @@ static int bcmgenet_set_eee(struct net_device *dev, struct ethtool_eee *e) return phy_ethtool_set_eee(dev->phydev, e); } +static int bcmgenet_validate_flow(struct net_device *dev, + struct ethtool_rxnfc *cmd) +{ + struct ethtool_usrip4_spec *l4_mask; + struct ethhdr *eth_mask; + + if (cmd->fs.location >= MAX_NUM_OF_FS_RULES) { + netdev_err(dev, "rxnfc: Invalid location (%d)\n", + cmd->fs.location); + return -EINVAL; + } + + switch (cmd->fs.flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) { + case IP_USER_FLOW: + l4_mask = &cmd->fs.m_u.usr_ip4_spec; + /* don't allow mask which isn't valid */ + if (VALIDATE_MASK(l4_mask->ip4src) || + VALIDATE_MASK(l4_mask->ip4dst) || + VALIDATE_MASK(l4_mask->l4_4_bytes) || + VALIDATE_MASK(l4_mask->proto) || + VALIDATE_MASK(l4_mask->ip_ver) || + VALIDATE_MASK(l4_mask->tos)) { + netdev_err(dev, "rxnfc: Unsupported mask\n"); + return -EINVAL; + } + break; + case ETHER_FLOW: + eth_mask = &cmd->fs.m_u.ether_spec; + /* don't allow mask which isn't valid */ + if (VALIDATE_MASK(eth_mask->h_source) || + VALIDATE_MASK(eth_mask->h_source) || + VALIDATE_MASK(eth_mask->h_proto)) { + netdev_err(dev, "rxnfc: Unsupported mask\n"); + return -EINVAL; + } + break; + default: + netdev_err(dev, "rxnfc: Unsupported flow type (0x%x)\n", + cmd->fs.flow_type); + return -EINVAL; + } + + if ((cmd->fs.flow_type & FLOW_EXT)) { + /* don't allow mask which isn't valid */ + if (VALIDATE_MASK(cmd->fs.m_ext.vlan_etype) || + VALIDATE_MASK(cmd->fs.m_ext.vlan_tci)) { + netdev_err(dev, "rxnfc: Unsupported mask\n"); + return -EINVAL; + } + if (cmd->fs.m_ext.data[0] || cmd->fs.m_ext.data[1]) { + netdev_err(dev, "rxnfc: user-def not supported\n"); + return -EINVAL; + } + } + + if ((cmd->fs.flow_type & FLOW_MAC_EXT)) { + /* don't allow mask which isn't valid */ + if (VALIDATE_MASK(cmd->fs.m_ext.h_dest)) { + netdev_err(dev, "rxnfc: Unsupported mask\n"); + return -EINVAL; + } + } + + return 0; +} + +static int bcmgenet_insert_flow(struct net_device *dev, + struct ethtool_rxnfc *cmd) +{ + struct bcmgenet_priv *priv = netdev_priv(dev); + struct bcmgenet_rxnfc_rule *loc_rule; + int err; + + if (priv->hw_params->hfb_filter_size < 128) { + netdev_err(dev, "rxnfc: Not supported by this device\n"); + return -EINVAL; + } + + if (cmd->fs.ring_cookie > priv->hw_params->rx_queues && + cmd->fs.ring_cookie != RX_CLS_FLOW_WAKE) { + netdev_err(dev, "rxnfc: Unsupported action (%llu)\n", + cmd->fs.ring_cookie); + return -EINVAL; + } + + err = bcmgenet_validate_flow(dev, cmd); + if (err) + return err; + + loc_rule = &priv->rxnfc_rules[cmd->fs.location]; + if (loc_rule->state == BCMGENET_RXNFC_STATE_ENABLED) + bcmgenet_hfb_disable_filter(priv, cmd->fs.location); + if (loc_rule->state != BCMGENET_RXNFC_STATE_UNUSED) + list_del(&loc_rule->list); + loc_rule->state = BCMGENET_RXNFC_STATE_UNUSED; + memcpy(&loc_rule->fs, &cmd->fs, + sizeof(struct ethtool_rx_flow_spec)); + + err = bcmgenet_hfb_create_rxnfc_filter(priv, loc_rule); + if (err) { + netdev_err(dev, "rxnfc: Could not install rule (%d)\n", + err); + return err; + } + + list_add_tail(&loc_rule->list, &priv->rxnfc_list); + + return 0; +} + +static int bcmgenet_delete_flow(struct net_device *dev, + struct ethtool_rxnfc *cmd) +{ + struct bcmgenet_priv *priv = netdev_priv(dev); + struct bcmgenet_rxnfc_rule *rule; + int err = 0; + + if (cmd->fs.location >= MAX_NUM_OF_FS_RULES) + return -EINVAL; + + rule = &priv->rxnfc_rules[cmd->fs.location]; + if (rule->state == BCMGENET_RXNFC_STATE_UNUSED) { + err = -ENOENT; + goto out; + } + + if (rule->state == BCMGENET_RXNFC_STATE_ENABLED) + bcmgenet_hfb_disable_filter(priv, cmd->fs.location); + if (rule->state != BCMGENET_RXNFC_STATE_UNUSED) + list_del(&rule->list); + rule->state = BCMGENET_RXNFC_STATE_UNUSED; + memset(&rule->fs, 0, sizeof(struct ethtool_rx_flow_spec)); + +out: + return err; +} + +static int bcmgenet_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) +{ + struct bcmgenet_priv *priv = netdev_priv(dev); + int err = 0; + + switch (cmd->cmd) { + case ETHTOOL_SRXCLSRLINS: + err = bcmgenet_insert_flow(dev, cmd); + break; + case ETHTOOL_SRXCLSRLDEL: + err = bcmgenet_delete_flow(dev, cmd); + break; + default: + netdev_warn(priv->dev, "Unsupported ethtool command. (%d)\n", + cmd->cmd); + return -EINVAL; + } + + return err; +} + +static int bcmgenet_get_flow(struct net_device *dev, struct ethtool_rxnfc *cmd, + int loc) +{ + struct bcmgenet_priv *priv = netdev_priv(dev); + struct bcmgenet_rxnfc_rule *rule; + int err = 0; + + if (loc < 0 || loc >= MAX_NUM_OF_FS_RULES) + return -EINVAL; + + rule = &priv->rxnfc_rules[loc]; + if (rule->state == BCMGENET_RXNFC_STATE_UNUSED) + err = -ENOENT; + else + memcpy(&cmd->fs, &rule->fs, + sizeof(struct ethtool_rx_flow_spec)); + + return err; +} + +static int bcmgenet_get_num_flows(struct bcmgenet_priv *priv) +{ + struct list_head *pos; + int res = 0; + + list_for_each(pos, &priv->rxnfc_list) + res++; + + return res; +} + +static int bcmgenet_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + struct bcmgenet_priv *priv = netdev_priv(dev); + struct bcmgenet_rxnfc_rule *rule; + int err = 0; + int i = 0; + + switch (cmd->cmd) { + case ETHTOOL_GRXRINGS: + cmd->data = priv->hw_params->rx_queues ?: 1; + break; + case ETHTOOL_GRXCLSRLCNT: + cmd->rule_cnt = bcmgenet_get_num_flows(priv); + cmd->data = MAX_NUM_OF_FS_RULES; + break; + case ETHTOOL_GRXCLSRULE: + err = bcmgenet_get_flow(dev, cmd, cmd->fs.location); + break; + case ETHTOOL_GRXCLSRLALL: + list_for_each_entry(rule, &priv->rxnfc_list, list) + if (i < cmd->rule_cnt) + rule_locs[i++] = rule->fs.location; + cmd->rule_cnt = i; + cmd->data = MAX_NUM_OF_FS_RULES; + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + /* standard ethtool support functions. */ static const struct ethtool_ops bcmgenet_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS | @@ -1069,6 +1668,8 @@ static const struct ethtool_ops bcmgenet_ethtool_ops = { .get_link_ksettings = bcmgenet_get_link_ksettings, .set_link_ksettings = bcmgenet_set_link_ksettings, .get_ts_info = ethtool_op_get_ts_info, + .get_rxnfc = bcmgenet_get_rxnfc, + .set_rxnfc = bcmgenet_set_rxnfc, }; /* Power down the unimac, based on mode. */ @@ -2669,10 +3270,7 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id) static irqreturn_t bcmgenet_wol_isr(int irq, void *dev_id) { - struct bcmgenet_priv *priv = dev_id; - - pm_wakeup_event(&priv->pdev->dev, 0); - + /* Acknowledge the interrupt */ return IRQ_HANDLED; } @@ -2710,9 +3308,8 @@ static void bcmgenet_umac_reset(struct bcmgenet_priv *priv) static void bcmgenet_set_hw_addr(struct bcmgenet_priv *priv, unsigned char *addr) { - bcmgenet_umac_writel(priv, (addr[0] << 24) | (addr[1] << 16) | - (addr[2] << 8) | addr[3], UMAC_MAC0); - bcmgenet_umac_writel(priv, (addr[4] << 8) | addr[5], UMAC_MAC1); + bcmgenet_umac_writel(priv, get_unaligned_be32(&addr[0]), UMAC_MAC0); + bcmgenet_umac_writel(priv, get_unaligned_be16(&addr[4]), UMAC_MAC1); } static void bcmgenet_get_hw_addr(struct bcmgenet_priv *priv, @@ -2721,13 +3318,9 @@ static void bcmgenet_get_hw_addr(struct bcmgenet_priv *priv, u32 addr_tmp; addr_tmp = bcmgenet_umac_readl(priv, UMAC_MAC0); - addr[0] = addr_tmp >> 24; - addr[1] = (addr_tmp >> 16) & 0xff; - addr[2] = (addr_tmp >> 8) & 0xff; - addr[3] = addr_tmp & 0xff; + put_unaligned_be32(addr_tmp, &addr[0]); addr_tmp = bcmgenet_umac_readl(priv, UMAC_MAC1); - addr[4] = (addr_tmp >> 8) & 0xff; - addr[5] = addr_tmp & 0xff; + put_unaligned_be16(addr_tmp, &addr[4]); } /* Returns a reusable dma control register value */ @@ -2766,43 +3359,12 @@ static void bcmgenet_enable_dma(struct bcmgenet_priv *priv, u32 dma_ctrl) bcmgenet_tdma_writel(priv, reg, DMA_CTRL); } -/* bcmgenet_hfb_clear - * - * Clear Hardware Filter Block and disable all filtering. - */ -static void bcmgenet_hfb_clear(struct bcmgenet_priv *priv) -{ - u32 i; - - bcmgenet_hfb_reg_writel(priv, 0x0, HFB_CTRL); - bcmgenet_hfb_reg_writel(priv, 0x0, HFB_FLT_ENABLE_V3PLUS); - bcmgenet_hfb_reg_writel(priv, 0x0, HFB_FLT_ENABLE_V3PLUS + 4); - - for (i = DMA_INDEX2RING_0; i <= DMA_INDEX2RING_7; i++) - bcmgenet_rdma_writel(priv, 0x0, i); - - for (i = 0; i < (priv->hw_params->hfb_filter_cnt / 4); i++) - bcmgenet_hfb_reg_writel(priv, 0x0, - HFB_FLT_LEN_V3PLUS + i * sizeof(u32)); - - for (i = 0; i < priv->hw_params->hfb_filter_cnt * - priv->hw_params->hfb_filter_size; i++) - bcmgenet_hfb_writel(priv, 0x0, i * sizeof(u32)); -} - -static void bcmgenet_hfb_init(struct bcmgenet_priv *priv) -{ - if (GENET_IS_V1(priv) || GENET_IS_V2(priv)) - return; - - bcmgenet_hfb_clear(priv); -} - static void bcmgenet_netif_start(struct net_device *dev) { struct bcmgenet_priv *priv = netdev_priv(dev); /* Start the network engine */ + bcmgenet_set_rx_mode(dev); bcmgenet_enable_rx_napi(priv); umac_enable_set(priv, CMD_TX_EN | CMD_RX_EN, true); @@ -3421,8 +3983,6 @@ MODULE_DEVICE_TABLE(of, bcmgenet_match); static int bcmgenet_probe(struct platform_device *pdev) { struct bcmgenet_platform_data *pd = pdev->dev.platform_data; - struct device_node *dn = pdev->dev.of_node; - const struct of_device_id *of_id = NULL; const struct bcmgenet_plat_data *pdata; struct bcmgenet_priv *priv; struct net_device *dev; @@ -3437,12 +3997,6 @@ static int bcmgenet_probe(struct platform_device *pdev) return -ENOMEM; } - if (dn) { - of_id = of_match_node(bcmgenet_match, dn); - if (!of_id) - return -EINVAL; - } - priv = netdev_priv(dev); priv->irq0 = platform_get_irq(pdev, 0); if (priv->irq0 < 0) { @@ -3504,13 +4058,16 @@ static int bcmgenet_probe(struct platform_device *pdev) priv->dma_max_burst_length = DMA_MAX_BURST_LENGTH; } - priv->clk = devm_clk_get(&priv->pdev->dev, "enet"); + priv->clk = devm_clk_get_optional(&priv->pdev->dev, "enet"); if (IS_ERR(priv->clk)) { dev_dbg(&priv->pdev->dev, "failed to get enet clock\n"); - priv->clk = NULL; + err = PTR_ERR(priv->clk); + goto err; } - clk_prepare_enable(priv->clk); + err = clk_prepare_enable(priv->clk); + if (err) + goto err; bcmgenet_set_hw_params(priv); @@ -3528,16 +4085,18 @@ static int bcmgenet_probe(struct platform_device *pdev) priv->rx_buf_len = RX_BUF_LENGTH; INIT_WORK(&priv->bcmgenet_irq_work, bcmgenet_irq_task); - priv->clk_wol = devm_clk_get(&priv->pdev->dev, "enet-wol"); + priv->clk_wol = devm_clk_get_optional(&priv->pdev->dev, "enet-wol"); if (IS_ERR(priv->clk_wol)) { dev_dbg(&priv->pdev->dev, "failed to get enet-wol clock\n"); - priv->clk_wol = NULL; + err = PTR_ERR(priv->clk_wol); + goto err; } - priv->clk_eee = devm_clk_get(&priv->pdev->dev, "enet-eee"); + priv->clk_eee = devm_clk_get_optional(&priv->pdev->dev, "enet-eee"); if (IS_ERR(priv->clk_eee)) { dev_dbg(&priv->pdev->dev, "failed to get enet-eee clock\n"); - priv->clk_eee = NULL; + err = PTR_ERR(priv->clk_eee); + goto err; } /* If this is an internal GPHY, power it on now, before UniMAC is @@ -3546,7 +4105,7 @@ static int bcmgenet_probe(struct platform_device *pdev) if (device_get_phy_mode(&pdev->dev) == PHY_INTERFACE_MODE_INTERNAL) bcmgenet_power_up(priv, GENET_POWER_PASSIVE); - if ((pd) && (!IS_ERR_OR_NULL(pd->mac_address))) + if (pd && !IS_ERR_OR_NULL(pd->mac_address)) ether_addr_copy(dev->dev_addr, pd->mac_address); else if (!device_get_mac_address(&pdev->dev, dev->dev_addr, ETH_ALEN)) @@ -3612,11 +4171,10 @@ static void bcmgenet_shutdown(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP -static int bcmgenet_resume(struct device *d) +static int bcmgenet_resume_noirq(struct device *d) { struct net_device *dev = dev_get_drvdata(d); struct bcmgenet_priv *priv = netdev_priv(dev); - unsigned long dma_ctrl; int ret; u32 reg; @@ -3628,6 +4186,38 @@ static int bcmgenet_resume(struct device *d) if (ret) return ret; + if (device_may_wakeup(d) && priv->wolopts) { + /* Account for Wake-on-LAN events and clear those events + * (Some devices need more time between enabling the clocks + * and the interrupt register reflecting the wake event so + * read the register twice) + */ + reg = bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_STAT); + reg = bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_STAT); + if (reg & UMAC_IRQ_WAKE_EVENT) + pm_wakeup_event(&priv->pdev->dev, 0); + } + + bcmgenet_intrl2_0_writel(priv, UMAC_IRQ_WAKE_EVENT, INTRL2_CPU_CLEAR); + + return 0; +} + +static int bcmgenet_resume(struct device *d) +{ + struct net_device *dev = dev_get_drvdata(d); + struct bcmgenet_priv *priv = netdev_priv(dev); + unsigned long dma_ctrl; + u32 offset, reg; + int ret; + + if (!netif_running(dev)) + return 0; + + /* From WOL-enabled suspend, switch to regular clock */ + if (device_may_wakeup(d) && priv->wolopts) + bcmgenet_power_up(priv, GENET_POWER_WOL_MAGIC); + /* If this is an internal GPHY, power it back on now, before UniMAC is * brought out of reset as absolutely no UniMAC activity is allowed */ @@ -3638,10 +4228,6 @@ static int bcmgenet_resume(struct device *d) init_umac(priv); - /* From WOL-enabled suspend, switch to regular clock */ - if (priv->wolopts) - clk_disable_unprepare(priv->clk_wol); - phy_init_hw(dev->phydev); /* Speed settings must be restored */ @@ -3653,15 +4239,17 @@ static int bcmgenet_resume(struct device *d) bcmgenet_set_hw_addr(priv, dev->dev_addr); + offset = HFB_FLT_ENABLE_V3PLUS; + bcmgenet_hfb_reg_writel(priv, priv->hfb_en[1], offset); + bcmgenet_hfb_reg_writel(priv, priv->hfb_en[2], offset + sizeof(u32)); + bcmgenet_hfb_reg_writel(priv, priv->hfb_en[0], HFB_CTRL); + if (priv->internal_phy) { reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT); reg |= EXT_ENERGY_DET_MASK; bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT); } - if (priv->wolopts) - bcmgenet_power_up(priv, GENET_POWER_WOL_MAGIC); - /* Disable RX/TX DMA and flush TX queues */ dma_ctrl = bcmgenet_dma_disable(priv); @@ -3698,7 +4286,7 @@ static int bcmgenet_suspend(struct device *d) { struct net_device *dev = dev_get_drvdata(d); struct bcmgenet_priv *priv = netdev_priv(dev); - int ret = 0; + u32 offset; if (!netif_running(dev)) return 0; @@ -3710,25 +4298,53 @@ static int bcmgenet_suspend(struct device *d) if (!device_may_wakeup(d)) phy_suspend(dev->phydev); + /* Preserve filter state and disable filtering */ + priv->hfb_en[0] = bcmgenet_hfb_reg_readl(priv, HFB_CTRL); + offset = HFB_FLT_ENABLE_V3PLUS; + priv->hfb_en[1] = bcmgenet_hfb_reg_readl(priv, offset); + priv->hfb_en[2] = bcmgenet_hfb_reg_readl(priv, offset + sizeof(u32)); + bcmgenet_hfb_reg_writel(priv, 0, HFB_CTRL); + + return 0; +} + +static int bcmgenet_suspend_noirq(struct device *d) +{ + struct net_device *dev = dev_get_drvdata(d); + struct bcmgenet_priv *priv = netdev_priv(dev); + int ret = 0; + + if (!netif_running(dev)) + return 0; + /* Prepare the device for Wake-on-LAN and switch to the slow clock */ - if (device_may_wakeup(d) && priv->wolopts) { + if (device_may_wakeup(d) && priv->wolopts) ret = bcmgenet_power_down(priv, GENET_POWER_WOL_MAGIC); - clk_prepare_enable(priv->clk_wol); - } else if (priv->internal_phy) { + else if (priv->internal_phy) ret = bcmgenet_power_down(priv, GENET_POWER_PASSIVE); - } + + /* Let the framework handle resumption and leave the clocks on */ + if (ret) + return ret; /* Turn off the clocks */ clk_disable_unprepare(priv->clk); - if (ret) - bcmgenet_resume(d); - - return ret; + return 0; } +#else +#define bcmgenet_suspend NULL +#define bcmgenet_suspend_noirq NULL +#define bcmgenet_resume NULL +#define bcmgenet_resume_noirq NULL #endif /* CONFIG_PM_SLEEP */ -static SIMPLE_DEV_PM_OPS(bcmgenet_pm_ops, bcmgenet_suspend, bcmgenet_resume); +static const struct dev_pm_ops bcmgenet_pm_ops = { + .suspend = bcmgenet_suspend, + .suspend_noirq = bcmgenet_suspend_noirq, + .resume = bcmgenet_resume, + .resume_noirq = bcmgenet_resume_noirq, +}; static const struct acpi_device_id genet_acpi_match[] = { { "BCM6E4E", (kernel_ulong_t)&bcm2711_plat_data }, @@ -3744,7 +4360,7 @@ static struct platform_driver bcmgenet_driver = { .name = "bcmgenet", .of_match_table = bcmgenet_match, .pm = &bcmgenet_pm_ops, - .acpi_match_table = ACPI_PTR(genet_acpi_match), + .acpi_match_table = genet_acpi_match, }, }; module_platform_driver(bcmgenet_driver); diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index daf8fb2c39b6..a12cb59298f4 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2014-2017 Broadcom + * Copyright (c) 2014-2020 Broadcom */ #ifndef __BCMGENET_H__ @@ -14,6 +14,7 @@ #include <linux/if_vlan.h> #include <linux/phy.h> #include <linux/dim.h> +#include <linux/ethtool.h> /* total number of Buffer Descriptors, same for Rx/Tx */ #define TOTAL_DESC 256 @@ -31,6 +32,7 @@ #define DMA_MAX_BURST_LENGTH 0x10 /* misc. configuration */ +#define MAX_NUM_OF_FS_RULES 16 #define CLEAR_ALL_HFB 0xFF #define DMA_FC_THRESH_HI (TOTAL_DESC >> 4) #define DMA_FC_THRESH_LO 5 @@ -310,6 +312,8 @@ struct bcmgenet_mib_counters { #define UMAC_IRQ_HFB_SM (1 << 10) #define UMAC_IRQ_HFB_MM (1 << 11) #define UMAC_IRQ_MPD_R (1 << 12) +#define UMAC_IRQ_WAKE_EVENT (UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM | \ + UMAC_IRQ_MPD_R) #define UMAC_IRQ_RXDMA_MBDONE (1 << 13) #define UMAC_IRQ_RXDMA_PDONE (1 << 14) #define UMAC_IRQ_RXDMA_BDONE (1 << 15) @@ -608,6 +612,18 @@ struct bcmgenet_rx_ring { struct bcmgenet_priv *priv; }; +enum bcmgenet_rxnfc_state { + BCMGENET_RXNFC_STATE_UNUSED = 0, + BCMGENET_RXNFC_STATE_DISABLED, + BCMGENET_RXNFC_STATE_ENABLED +}; + +struct bcmgenet_rxnfc_rule { + struct list_head list; + struct ethtool_rx_flow_spec fs; + enum bcmgenet_rxnfc_state state; +}; + /* device context */ struct bcmgenet_priv { void __iomem *base; @@ -626,6 +642,8 @@ struct bcmgenet_priv { struct enet_cb *rx_cbs; unsigned int num_rx_bds; unsigned int rx_buf_len; + struct bcmgenet_rxnfc_rule rxnfc_rules[MAX_NUM_OF_FS_RULES]; + struct list_head rxnfc_list; struct bcmgenet_rx_ring rx_rings[DESC_INDEX + 1]; @@ -676,6 +694,9 @@ struct bcmgenet_priv { /* WOL */ struct clk *clk_wol; u32 wolopts; + u8 sopass[SOPASS_MAX]; + bool wol_active; + u32 hfb_en[3]; struct bcmgenet_mib_counters mib; diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c b/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c index c9a43695b182..4ea6a26b04f7 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c @@ -2,7 +2,7 @@ /* * Broadcom GENET (Gigabit Ethernet) Wake-on-LAN support * - * Copyright (c) 2014-2017 Broadcom + * Copyright (c) 2014-2020 Broadcom */ #define pr_fmt(fmt) "bcmgenet_wol: " fmt @@ -41,18 +41,13 @@ void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct bcmgenet_priv *priv = netdev_priv(dev); - u32 reg; - wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE; + wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER; wol->wolopts = priv->wolopts; memset(wol->sopass, 0, sizeof(wol->sopass)); - if (wol->wolopts & WAKE_MAGICSECURE) { - reg = bcmgenet_umac_readl(priv, UMAC_MPD_PW_MS); - put_unaligned_be16(reg, &wol->sopass[0]); - reg = bcmgenet_umac_readl(priv, UMAC_MPD_PW_LS); - put_unaligned_be32(reg, &wol->sopass[2]); - } + if (wol->wolopts & WAKE_MAGICSECURE) + memcpy(wol->sopass, priv->sopass, sizeof(priv->sopass)); } /* ethtool function - set WOL (Wake on LAN) settings. @@ -62,25 +57,15 @@ int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct bcmgenet_priv *priv = netdev_priv(dev); struct device *kdev = &priv->pdev->dev; - u32 reg; if (!device_can_wakeup(kdev)) return -ENOTSUPP; - if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE)) + if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER)) return -EINVAL; - reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); - if (wol->wolopts & WAKE_MAGICSECURE) { - bcmgenet_umac_writel(priv, get_unaligned_be16(&wol->sopass[0]), - UMAC_MPD_PW_MS); - bcmgenet_umac_writel(priv, get_unaligned_be32(&wol->sopass[2]), - UMAC_MPD_PW_LS); - reg |= MPD_PW_EN; - } else { - reg &= ~MPD_PW_EN; - } - bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); + if (wol->wolopts & WAKE_MAGICSECURE) + memcpy(priv->sopass, wol->sopass, sizeof(priv->sopass)); /* Flag the device and relevant IRQ as wakeup capable */ if (wol->wolopts) { @@ -120,12 +105,21 @@ static int bcmgenet_poll_wol_status(struct bcmgenet_priv *priv) return retries; } +static void bcmgenet_set_mpd_password(struct bcmgenet_priv *priv) +{ + bcmgenet_umac_writel(priv, get_unaligned_be16(&priv->sopass[0]), + UMAC_MPD_PW_MS); + bcmgenet_umac_writel(priv, get_unaligned_be32(&priv->sopass[2]), + UMAC_MPD_PW_LS); +} + int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv, enum bcmgenet_power_mode mode) { struct net_device *dev = priv->dev; + struct bcmgenet_rxnfc_rule *rule; + u32 reg, hfb_ctrl_reg, hfb_enable = 0; int retries = 0; - u32 reg; if (mode != GENET_POWER_WOL_MAGIC) { netif_err(priv, wol, dev, "unsupported mode: %d\n", mode); @@ -142,22 +136,48 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv, bcmgenet_umac_writel(priv, reg, UMAC_CMD); mdelay(10); - reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); - reg |= MPD_EN; - bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); + if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) { + reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); + reg |= MPD_EN; + if (priv->wolopts & WAKE_MAGICSECURE) { + bcmgenet_set_mpd_password(priv); + reg |= MPD_PW_EN; + } + bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); + } + + hfb_ctrl_reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL); + if (priv->wolopts & WAKE_FILTER) { + list_for_each_entry(rule, &priv->rxnfc_list, list) + if (rule->fs.ring_cookie == RX_CLS_FLOW_WAKE) + hfb_enable |= (1 << rule->fs.location); + reg = (hfb_ctrl_reg & ~RBUF_HFB_EN) | RBUF_ACPI_EN; + bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL); + } /* Do not leave UniMAC in MPD mode only */ retries = bcmgenet_poll_wol_status(priv); if (retries < 0) { reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); - reg &= ~MPD_EN; + reg &= ~(MPD_EN | MPD_PW_EN); bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); + bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL); return retries; } netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n", retries); + clk_prepare_enable(priv->clk_wol); + priv->wol_active = 1; + + if (hfb_enable) { + bcmgenet_hfb_reg_writel(priv, hfb_enable, + HFB_FLT_ENABLE_V3PLUS + 4); + hfb_ctrl_reg = RBUF_HFB_EN | RBUF_ACPI_EN; + bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL); + } + /* Enable CRC forward */ reg = bcmgenet_umac_readl(priv, UMAC_CMD); priv->crc_fwd_en = 1; @@ -173,6 +193,12 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv, bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT); } + reg = UMAC_IRQ_MPD_R; + if (hfb_enable) + reg |= UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM; + + bcmgenet_intrl2_0_writel(priv, reg, INTRL2_CPU_MASK_CLEAR); + return 0; } @@ -186,12 +212,22 @@ void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv, return; } + if (!priv->wol_active) + return; /* failed to suspend so skip the rest */ + + priv->wol_active = 0; + clk_disable_unprepare(priv->clk_wol); + + /* Disable Magic Packet Detection */ reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); - if (!(reg & MPD_EN)) - return; /* already powered up so skip the rest */ - reg &= ~MPD_EN; + reg &= ~(MPD_EN | MPD_PW_EN); bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); + /* Disable WAKE_FILTER Detection */ + reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL); + reg &= ~(RBUF_HFB_EN | RBUF_ACPI_EN); + bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL); + /* Disable CRC Forward */ reg = bcmgenet_umac_readl(priv, UMAC_CMD); reg &= ~CMD_CRC_FWD; diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index ff98a82b7bc4..7a3b22b35238 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -10797,17 +10797,15 @@ static int tg3_init_hw(struct tg3 *tp, bool reset_phy) #ifdef CONFIG_TIGON3_HWMON static void tg3_sd_scan_scratchpad(struct tg3 *tp, struct tg3_ocir *ocir) { + u32 off, len = TG3_OCIR_LEN; int i; - for (i = 0; i < TG3_SD_NUM_RECS; i++, ocir++) { - u32 off = i * TG3_OCIR_LEN, len = TG3_OCIR_LEN; - + for (i = 0, off = 0; i < TG3_SD_NUM_RECS; i++, ocir++, off += len) { tg3_ape_scratchpad_read(tp, (u32 *) ocir, off, len); - off += len; if (ocir->signature != TG3_OCIR_SIG_MAGIC || !(ocir->version_flags & TG3_OCIR_FLAG_ACTIVE)) - memset(ocir, 0, TG3_OCIR_LEN); + memset(ocir, 0, len); } } |