// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) Meta Platforms, Inc. and affiliates. */
#include <linux/etherdevice.h>
#include <linux/ipv6.h>
#include <linux/types.h>
#include <net/netdev_queues.h>
#include "fbnic.h"
#include "fbnic_netdev.h"
#include "fbnic_txrx.h"
int __fbnic_open(struct fbnic_net *fbn)
{
struct fbnic_dev *fbd = fbn->fbd;
int err;
err = fbnic_alloc_napi_vectors(fbn);
if (err)
return err;
err = fbnic_alloc_resources(fbn);
if (err)
goto free_napi_vectors;
err = fbnic_set_netif_queues(fbn);
if (err)
goto free_resources;
/* Send ownership message and flush to verify FW has seen it */
err = fbnic_fw_xmit_ownership_msg(fbd, true);
if (err) {
dev_warn(fbd->dev,
"Error %d sending host ownership message to the firmware\n",
err);
goto err_reset_queues;
}
err = fbnic_time_start(fbn);
if (err)
goto release_ownership;
err = fbnic_fw_init_heartbeat(fbd, false);
if (err)
goto time_stop;
err = fbnic_mac_request_irq(fbd);
if (err)
goto time_stop;
/* Pull the BMC config and initialize the RPC */
fbnic_bmc_rpc_init(fbd);
fbnic_rss_reinit(fbd, fbn);
phylink_resume(fbn->phylink);
return 0;
time_stop:
fbnic_time_stop(fbn);
release_ownership:
fbnic_fw_xmit_ownership_msg(fbn->fbd, false);
err_reset_queues:
fbnic_reset_netif_queues(fbn);
free_resources:
fbnic_free_resources(fbn);
free_napi_vectors:
fbnic_free_napi_vectors(fbn);
return err;
}
static int fbnic_open(struct net_device *netdev)
{
struct fbnic_net *fbn = netdev_priv(netdev);
int err;
fbnic_napi_name_irqs(fbn->fbd);
err = __fbnic_open(fbn);
if (!err)
fbnic_up(fbn);
return err;
}
static int fbnic_stop(struct net_device *netdev)
{
struct fbnic_net *fbn = netdev_priv(netdev);
fbnic_mac_free_irq(fbn->fbd);
phylink_suspend(fbn->phylink, fbnic_bmc_present(fbn->fbd));
fbnic_down(fbn);
fbnic_time_stop(fbn);
fbnic_fw_xmit_ownership_msg(fbn->fbd, false);
fbnic_reset_netif_queues(fbn);
fbnic_free_resources(fbn);
fbnic_free_napi_vectors(fbn);
return 0;
}
static int fbnic_uc_sync(struct net_device *netdev, const unsigned char *addr)
{
struct fbnic_net *fbn = netdev_priv(netdev);
struct fbnic_mac_addr *avail_addr;
if (WARN_ON(!is_valid_ether_addr(addr)))
return -EADDRNOTAVAIL;
avail_addr = __fbnic_uc_sync(fbn->fbd, addr);
if (!avail_addr)
return -ENOSPC;
/* Add type flag indicating this address is in use by the host */
set_bit(FBNIC_MAC_ADDR_T_UNICAST, avail_addr->act_tcam);
return 0;
}
static int fbnic_uc_unsync(struct net_device *netdev, const unsigned char *addr)
{
struct fbnic_net *fbn = netdev_priv(netdev);
struct fbnic_dev *fbd = fbn->fbd;
int i, ret;
/* Scan from middle of list to bottom, filling bottom up.
* Skip the first entry which is reserved for dev_addr and
* leave the last entry to use for promiscuous filtering.
*/
for (i = fbd->mac_addr_boundary, ret = -ENOENT;
i < FBNIC_RPC_TCAM_MACDA_HOST_ADDR_IDX && ret; i++) {
struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[i];
if (!ether_addr_equal(mac_addr->value.addr8, addr))
continue;
ret = __fbnic_uc_unsync(mac_addr);
}
return ret;
}
static int fbnic_mc_sync(struct net_device *netdev, const unsigned char *addr)
{
struct fbnic_net *fbn = netdev_priv(netdev);
struct fbnic_mac_addr *avail_addr;
if (WARN_ON(!is_multicast_ether_addr(addr)))
return -EADDRNOTAVAIL;
avail_addr = __fbnic_mc_sync(fbn->fbd, addr);
if (!avail_addr)
return -ENOSPC;
/* Add type flag indicating this address is in use by the host */
set_bit(FBNIC_MAC_ADDR_T_MULTICAST, avail_addr->act_tcam);
return 0;
}
static int fbnic_mc_unsync(struct net_device *netdev, const unsigned char *addr)
{
struct fbnic_net *fbn = netdev_priv(netdev);
struct fbnic_dev *fbd = fbn->fbd;
int i, ret;
/* Scan from middle of list to top, filling top down.
* Skip over the address reserved for the BMC MAC and
* exclude index 0 as that belongs to the broadcast address
*/
for (i = fbd->mac_addr_boundary, ret = -ENOENT;
--i > FBNIC_RPC_TCAM_MACDA_BROADCAST_IDX && ret;) {
struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[i