diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c index 322705202d49..8cf38c426dae 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c @@ -213,6 +213,193 @@ static int enetc_vf_setup_tc(struct net_device *ndev, enum tc_setup_type type, } } +static int enetc_vf_set_mac_promisc(struct enetc_si *si, int type, + bool promisc, bool flush_macs) +{ + struct enetc_msg_mac_promisc_mode *msg; + struct device *dev = &si->pdev->dev; + struct enetc_msg_swbd msg_swbd; + + if (!(type & ENETC_MAC_FILTER_TYPE_ALL)) + return -EINVAL; + + msg_swbd.size = ALIGN(sizeof(*msg), ENETC_MSG_ALIGN); + msg_swbd.vaddr = dma_alloc_coherent(dev, msg_swbd.size, + &msg_swbd.dma, GFP_KERNEL); + if (!msg_swbd.vaddr) + return -ENOMEM; + + msg = (struct enetc_msg_mac_promisc_mode *)msg_swbd.vaddr; + msg->config = FIELD_PREP(ENETC_MSG_MAC_TYPE, + type & ENETC_MAC_FILTER_TYPE_ALL); + msg->config |= FIELD_PREP(ENETC_MSG_MAC_PROMISC_MODE, promisc); + msg->config |= FIELD_PREP(ENETC_MSG_MAC_FLUSH_MACS, flush_macs); + enetc_msg_fill_common_hdr(&msg_swbd, ENETC_MSG_CLASS_ID_MAC_FILTER, + ENETC_MSG_SET_MAC_PROMISC_MODE, 0, 0); + + return enetc_msg_vsi_send(si, &msg_swbd); +} + +static int enetc_vf_set_mac_hash_filter(struct enetc_si *si, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) +{ + struct enetc_msg_mac_hash_filter *msg; + struct enetc_mac_filter *mac_filter; + struct device *dev = &si->pdev->dev; + struct enetc_msg_swbd msg_swbd; + struct netdev_hw_addr *ha; + int mac_filter_type = 0; + u32 tbl_cnt = 0; + u32 msg_size; + int i = 0; + + if (!uc && !mc) + return 0; + + if (uc) { + tbl_cnt += 2; + mac_filter_type |= ENETC_MAC_FILTER_TYPE_UC; + } + + if (mc) { + tbl_cnt += 2; + mac_filter_type |= ENETC_MAC_FILTER_TYPE_MC; + } + + msg_size = struct_size(msg, hash_tbl, tbl_cnt); + msg_swbd.size = ALIGN(msg_size, ENETC_MSG_ALIGN); + msg_swbd.vaddr = dma_alloc_coherent(dev, msg_swbd.size, + &msg_swbd.dma, GFP_KERNEL); + if (!msg_swbd.vaddr) + return -ENOMEM; + + msg = (struct enetc_msg_mac_hash_filter *)msg_swbd.vaddr; + msg->sz_type = FIELD_PREP(ENETC_MSG_MAC_TYPE, mac_filter_type); + msg->sz_type |= FIELD_PREP(ENETC_MSG_MAC_HASH_SIZE, + ENETC_MAC_HASH_TABLE_SIZE_64); + + if (uc) { + mac_filter = &si->mac_filter[UC]; + enetc_reset_mac_addr_filter(mac_filter); + netdev_hw_addr_list_for_each(ha, uc) + enetc_add_mac_addr_ht_filter(mac_filter, ha->addr); + + bitmap_to_arr32(&msg->hash_tbl[i], mac_filter->mac_hash_table, + ENETC_MADDR_HASH_TBL_SZ); + i += 2; + } + + if (mc) { + mac_filter = &si->mac_filter[MC]; + enetc_reset_mac_addr_filter(mac_filter); + netdev_hw_addr_list_for_each(ha, mc) + enetc_add_mac_addr_ht_filter(mac_filter, ha->addr); + + bitmap_to_arr32(&msg->hash_tbl[i], mac_filter->mac_hash_table, + ENETC_MADDR_HASH_TBL_SZ); + } + + enetc_msg_fill_common_hdr(&msg_swbd, ENETC_MSG_CLASS_ID_MAC_FILTER, + ENETC_MSG_SET_MAC_HASH_TABLE, 0, 0); + + return enetc_msg_vsi_send(si, &msg_swbd); +} + +static int enetc_vf_enable_iff_promisc(struct enetc_si *si) +{ + int err; + + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL, + true, true); + if (err) + dev_err(&si->pdev->dev, + "Failed to enable promiscuous mode, err: %pe\n", + ERR_PTR(err)); + + return err; +} + +static int enetc_vf_disable_iff_promisc(struct enetc_si *si, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) +{ + int err; + + err = enetc_vf_set_mac_hash_filter(si, uc, mc); + if (err) { + dev_err_once(&si->pdev->dev, + "Failed to set MAC hash filters, err: %pe\n", + ERR_PTR(err)); + return err; + } + + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL, + false, false); + if (err) + dev_err_once(&si->pdev->dev, + "Failed to disable promiscuous mode, err: %pe\n", + ERR_PTR(err)); + + return err; +} + +static int enetc_vf_enable_iff_allmulti(struct enetc_si *si, + struct netdev_hw_addr_list *uc) +{ + int err; + + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_MC, + true, true); + if (err) { + dev_err(&si->pdev->dev, + "Failed to enable multicast promiscuous mode, err: %pe\n", + ERR_PTR(err)); + return err; + } + + err = enetc_vf_set_mac_hash_filter(si, uc, NULL); + if (err) { + dev_err(&si->pdev->dev, + "Failed to set unicast filter, err: %pe\n", + ERR_PTR(err)); + return err; + } + + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_UC, + false, false); + if (err) + dev_err(&si->pdev->dev, + "Failed to disable unicast promiscuous mode, err: %pe\n", + ERR_PTR(err)); + + return err; +} + +static int enetc_vf_set_rx_mode(struct net_device *ndev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) +{ + struct enetc_ndev_priv *priv = netdev_priv(ndev); + struct enetc_si *si = priv->si; + int err; + + if (ndev->flags & IFF_PROMISC) + err = enetc_vf_enable_iff_promisc(si); + else if (ndev->flags & IFF_ALLMULTI) + err = enetc_vf_enable_iff_allmulti(si, uc); + else + err = enetc_vf_disable_iff_promisc(si, uc, mc); + + /* If the error code is -EOPNOTSUPP or -EACCES or -EPERM, return 0 + * directly to avoid meaningless retries. + */ + if (err == -EOPNOTSUPP || err == -EACCES || err == -EPERM) + return 0; + + return err; +} + /* Probing/ Init */ static const struct net_device_ops enetc_ndev_ops = { .ndo_open = enetc_open, @@ -227,6 +414,20 @@ static const struct net_device_ops enetc_ndev_ops = { .ndo_hwtstamp_set = enetc_hwtstamp_set, }; +static const struct net_device_ops enetc4_ndev_ops = { + .ndo_open = enetc_open, + .ndo_stop = enetc_close, + .ndo_start_xmit = enetc_xmit, + .ndo_get_stats = enetc_get_stats, + .ndo_set_mac_address = enetc_vf_set_mac_addr, + .ndo_set_features = enetc_vf_set_features, + .ndo_eth_ioctl = enetc_ioctl, + .ndo_setup_tc = enetc_vf_setup_tc, + .ndo_hwtstamp_get = enetc_hwtstamp_get, + .ndo_hwtstamp_set = enetc_hwtstamp_set, + .ndo_set_rx_mode_async = enetc_vf_set_rx_mode, +}; + static void enetc_vf_get_revision(struct enetc_si *si) { int ip_mn; @@ -280,6 +481,9 @@ static void enetc_vf_netdev_setup(struct enetc_si *si, struct net_device *ndev, ndev->vlan_features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO | NETIF_F_TSO6; + if (!is_enetc_rev1(si)) + ndev->priv_flags |= IFF_UNICAST_FLT; + if (si->num_rss) { ndev->hw_features |= NETIF_F_RXHASH; ndev->features |= NETIF_F_RXHASH; @@ -352,7 +556,10 @@ static int enetc_vf_probe(struct pci_dev *pdev, goto err_alloc_netdev; } - enetc_vf_netdev_setup(si, ndev, &enetc_ndev_ops); + if (is_enetc_rev1(si)) + enetc_vf_netdev_setup(si, ndev, &enetc_ndev_ops); + else + enetc_vf_netdev_setup(si, ndev, &enetc4_ndev_ops); priv = netdev_priv(ndev); @@ -416,6 +623,11 @@ static void enetc_vf_remove(struct pci_dev *pdev) priv = netdev_priv(si->ndev); unregister_netdev(si->ndev); + /* Disable promiscuous mode and clear MAC filters */ + if (!is_enetc_rev1(si)) + enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL, + false, true); + enetc_free_msix(priv); enetc_free_si_resources(priv);