net/sfc: support main MAC address change
[dpdk.git] / drivers / net / sfc / sfc_port.c
index 844363c..dc6ecdf 100644 (file)
 #include "sfc.h"
 #include "sfc_log.h"
 
+/**
+ * Update MAC statistics in the buffer.
+ *
+ * @param      sa      Adapter
+ *
+ * @return Status code
+ * @retval     0       Success
+ * @retval     EAGAIN  Try again
+ * @retval     ENOMEM  Memory allocation failure
+ */
+int
+sfc_port_update_mac_stats(struct sfc_adapter *sa)
+{
+       struct sfc_port *port = &sa->port;
+       int rc;
+
+       SFC_ASSERT(rte_spinlock_is_locked(&port->mac_stats_lock));
+
+       if (sa->state != SFC_ADAPTER_STARTED)
+               return EINVAL;
+
+       rc = efx_mac_stats_update(sa->nic, &port->mac_stats_dma_mem,
+                                 port->mac_stats_buf, NULL);
+       if (rc != 0)
+               return rc;
+
+       return 0;
+}
 
 int
 sfc_port_start(struct sfc_adapter *sa)
@@ -51,6 +79,18 @@ sfc_port_start(struct sfc_adapter *sa)
        if (rc != 0)
                goto fail_port_init;
 
+       sfc_log_init(sa, "set flow control to %#x autoneg=%u",
+                    port->flow_ctrl, port->flow_ctrl_autoneg);
+       rc = efx_mac_fcntl_set(sa->nic, port->flow_ctrl,
+                              port->flow_ctrl_autoneg);
+       if (rc != 0)
+               goto fail_mac_fcntl_set;
+
+       sfc_log_init(sa, "set phy adv caps to %#x", port->phy_adv_cap);
+       rc = efx_phy_adv_cap_set(sa->nic, port->phy_adv_cap);
+       if (rc != 0)
+               goto fail_phy_adv_cap_set;
+
        sfc_log_init(sa, "set MAC PDU %u", (unsigned int)port->pdu);
        rc = efx_mac_pdu_set(sa->nic, port->pdu);
        if (rc != 0)
@@ -63,10 +103,27 @@ sfc_port_start(struct sfc_adapter *sa)
                goto fail_mac_addr_set;
 
        sfc_log_init(sa, "set MAC filters");
-       rc = efx_mac_filter_set(sa->nic, B_TRUE, B_TRUE, B_TRUE, B_TRUE);
+       port->promisc = (sa->eth_dev->data->promiscuous != 0) ?
+                       B_TRUE : B_FALSE;
+       port->allmulti = (sa->eth_dev->data->all_multicast != 0) ?
+                        B_TRUE : B_FALSE;
+       rc = sfc_set_rx_mode(sa);
        if (rc != 0)
                goto fail_mac_filter_set;
 
+       efx_mac_stats_get_mask(sa->nic, port->mac_stats_mask,
+                              sizeof(port->mac_stats_mask));
+
+       /* Update MAC stats using periodic DMA.
+        * Common code always uses 1000ms update period, so period_ms
+        * parameter only needs to be non-zero to start updates.
+        */
+       sfc_log_init(sa, "request MAC stats DMA'ing");
+       rc = efx_mac_stats_periodic(sa->nic, &port->mac_stats_dma_mem,
+                                   1000, B_FALSE);
+       if (rc != 0)
+               goto fail_mac_stats_periodic;
+
        sfc_log_init(sa, "disable MAC drain");
        rc = efx_mac_drain(sa->nic, B_FALSE);
        if (rc != 0)
@@ -76,9 +133,15 @@ sfc_port_start(struct sfc_adapter *sa)
        return 0;
 
 fail_mac_drain:
+       (void)efx_mac_stats_periodic(sa->nic, &port->mac_stats_dma_mem,
+                                    0, B_FALSE);
+
+fail_mac_stats_periodic:
 fail_mac_filter_set:
 fail_mac_addr_set:
 fail_mac_pdu_set:
+fail_phy_adv_cap_set:
+fail_mac_fcntl_set:
        efx_port_fini(sa->nic);
 
 fail_port_init:
@@ -95,6 +158,10 @@ sfc_port_stop(struct sfc_adapter *sa)
        sfc_log_init(sa, "entry");
 
        efx_mac_drain(sa->nic, B_TRUE);
+
+       (void)efx_mac_stats_periodic(sa->nic, &sa->port.mac_stats_dma_mem,
+                                    0, B_FALSE);
+
        efx_port_fini(sa->nic);
        efx_filter_fini(sa->nic);
 
@@ -106,6 +173,7 @@ sfc_port_init(struct sfc_adapter *sa)
 {
        const struct rte_eth_dev_data *dev_data = sa->eth_dev->data;
        struct sfc_port *port = &sa->port;
+       int rc;
 
        sfc_log_init(sa, "entry");
 
@@ -118,14 +186,109 @@ sfc_port_init(struct sfc_adapter *sa)
        else
                port->pdu = EFX_MAC_PDU(dev_data->mtu);
 
+       rte_spinlock_init(&port->mac_stats_lock);
+
+       rc = ENOMEM;
+       port->mac_stats_buf = rte_calloc_socket("mac_stats_buf", EFX_MAC_NSTATS,
+                                               sizeof(uint64_t), 0,
+                                               sa->socket_id);
+       if (port->mac_stats_buf == NULL)
+               goto fail_mac_stats_buf_alloc;
+
+       rc = sfc_dma_alloc(sa, "mac_stats", 0, EFX_MAC_STATS_SIZE,
+                          sa->socket_id, &port->mac_stats_dma_mem);
+       if (rc != 0)
+               goto fail_mac_stats_dma_alloc;
+
        sfc_log_init(sa, "done");
        return 0;
+
+fail_mac_stats_dma_alloc:
+       rte_free(port->mac_stats_buf);
+fail_mac_stats_buf_alloc:
+       sfc_log_init(sa, "failed %d", rc);
+       return rc;
 }
 
 void
 sfc_port_fini(struct sfc_adapter *sa)
 {
+       struct sfc_port *port = &sa->port;
+
        sfc_log_init(sa, "entry");
 
+       sfc_dma_free(sa, &port->mac_stats_dma_mem);
+       rte_free(port->mac_stats_buf);
+
        sfc_log_init(sa, "done");
 }
+
+int
+sfc_set_rx_mode(struct sfc_adapter *sa)
+{
+       struct sfc_port *port = &sa->port;
+       int rc;
+
+       rc = efx_mac_filter_set(sa->nic, port->promisc, B_TRUE,
+                               port->promisc || port->allmulti, B_TRUE);
+
+       return rc;
+}
+
+void
+sfc_port_link_mode_to_info(efx_link_mode_t link_mode,
+                          struct rte_eth_link *link_info)
+{
+       SFC_ASSERT(link_mode < EFX_LINK_NMODES);
+
+       memset(link_info, 0, sizeof(*link_info));
+       if ((link_mode == EFX_LINK_DOWN) || (link_mode == EFX_LINK_UNKNOWN))
+               link_info->link_status = ETH_LINK_DOWN;
+       else
+               link_info->link_status = ETH_LINK_UP;
+
+       switch (link_mode) {
+       case EFX_LINK_10HDX:
+               link_info->link_speed  = ETH_SPEED_NUM_10M;
+               link_info->link_duplex = ETH_LINK_HALF_DUPLEX;
+               break;
+       case EFX_LINK_10FDX:
+               link_info->link_speed  = ETH_SPEED_NUM_10M;
+               link_info->link_duplex = ETH_LINK_FULL_DUPLEX;
+               break;
+       case EFX_LINK_100HDX:
+               link_info->link_speed  = ETH_SPEED_NUM_100M;
+               link_info->link_duplex = ETH_LINK_HALF_DUPLEX;
+               break;
+       case EFX_LINK_100FDX:
+               link_info->link_speed  = ETH_SPEED_NUM_100M;
+               link_info->link_duplex = ETH_LINK_FULL_DUPLEX;
+               break;
+       case EFX_LINK_1000HDX:
+               link_info->link_speed  = ETH_SPEED_NUM_1G;
+               link_info->link_duplex = ETH_LINK_HALF_DUPLEX;
+               break;
+       case EFX_LINK_1000FDX:
+               link_info->link_speed  = ETH_SPEED_NUM_1G;
+               link_info->link_duplex = ETH_LINK_FULL_DUPLEX;
+               break;
+       case EFX_LINK_10000FDX:
+               link_info->link_speed  = ETH_SPEED_NUM_10G;
+               link_info->link_duplex = ETH_LINK_FULL_DUPLEX;
+               break;
+       case EFX_LINK_40000FDX:
+               link_info->link_speed  = ETH_SPEED_NUM_40G;
+               link_info->link_duplex = ETH_LINK_FULL_DUPLEX;
+               break;
+       default:
+               SFC_ASSERT(B_FALSE);
+               /* FALLTHROUGH */
+       case EFX_LINK_UNKNOWN:
+       case EFX_LINK_DOWN:
+               link_info->link_speed  = ETH_SPEED_NUM_NONE;
+               link_info->link_duplex = 0;
+               break;
+       }
+
+       link_info->link_autoneg = ETH_LINK_AUTONEG;
+}