18c2ecf20Sopenharmony_ci/* Broadcom NetXtreme-C/E network driver.
28c2ecf20Sopenharmony_ci *
38c2ecf20Sopenharmony_ci * Copyright (c) 2016-2017 Broadcom Limited
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
68c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by
78c2ecf20Sopenharmony_ci * the Free Software Foundation.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <linux/pci.h>
108c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
128c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
138c2ecf20Sopenharmony_ci#include <linux/jhash.h>
148c2ecf20Sopenharmony_ci#include <net/pkt_cls.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "bnxt_hsi.h"
178c2ecf20Sopenharmony_ci#include "bnxt.h"
188c2ecf20Sopenharmony_ci#include "bnxt_vfr.h"
198c2ecf20Sopenharmony_ci#include "bnxt_devlink.h"
208c2ecf20Sopenharmony_ci#include "bnxt_tc.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#ifdef CONFIG_BNXT_SRIOV
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define CFA_HANDLE_INVALID		0xffff
258c2ecf20Sopenharmony_ci#define VF_IDX_INVALID			0xffff
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int hwrm_cfa_vfr_alloc(struct bnxt *bp, u16 vf_idx,
288c2ecf20Sopenharmony_ci			      u16 *tx_cfa_action, u16 *rx_cfa_code)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	struct hwrm_cfa_vfr_alloc_output *resp = bp->hwrm_cmd_resp_addr;
318c2ecf20Sopenharmony_ci	struct hwrm_cfa_vfr_alloc_input req = { 0 };
328c2ecf20Sopenharmony_ci	int rc;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_VFR_ALLOC, -1, -1);
358c2ecf20Sopenharmony_ci	req.vf_id = cpu_to_le16(vf_idx);
368c2ecf20Sopenharmony_ci	sprintf(req.vfr_name, "vfr%d", vf_idx);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	mutex_lock(&bp->hwrm_cmd_lock);
398c2ecf20Sopenharmony_ci	rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
408c2ecf20Sopenharmony_ci	if (!rc) {
418c2ecf20Sopenharmony_ci		*tx_cfa_action = le16_to_cpu(resp->tx_cfa_action);
428c2ecf20Sopenharmony_ci		*rx_cfa_code = le16_to_cpu(resp->rx_cfa_code);
438c2ecf20Sopenharmony_ci		netdev_dbg(bp->dev, "tx_cfa_action=0x%x, rx_cfa_code=0x%x",
448c2ecf20Sopenharmony_ci			   *tx_cfa_action, *rx_cfa_code);
458c2ecf20Sopenharmony_ci	} else {
468c2ecf20Sopenharmony_ci		netdev_info(bp->dev, "%s error rc=%d\n", __func__, rc);
478c2ecf20Sopenharmony_ci	}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	mutex_unlock(&bp->hwrm_cmd_lock);
508c2ecf20Sopenharmony_ci	return rc;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int hwrm_cfa_vfr_free(struct bnxt *bp, u16 vf_idx)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct hwrm_cfa_vfr_free_input req = { 0 };
568c2ecf20Sopenharmony_ci	int rc;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_VFR_FREE, -1, -1);
598c2ecf20Sopenharmony_ci	sprintf(req.vfr_name, "vfr%d", vf_idx);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
628c2ecf20Sopenharmony_ci	if (rc)
638c2ecf20Sopenharmony_ci		netdev_info(bp->dev, "%s error rc=%d\n", __func__, rc);
648c2ecf20Sopenharmony_ci	return rc;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int bnxt_hwrm_vfr_qcfg(struct bnxt *bp, struct bnxt_vf_rep *vf_rep,
688c2ecf20Sopenharmony_ci			      u16 *max_mtu)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct hwrm_func_qcfg_output *resp = bp->hwrm_cmd_resp_addr;
718c2ecf20Sopenharmony_ci	struct hwrm_func_qcfg_input req = {0};
728c2ecf20Sopenharmony_ci	u16 mtu;
738c2ecf20Sopenharmony_ci	int rc;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_QCFG, -1, -1);
768c2ecf20Sopenharmony_ci	req.fid = cpu_to_le16(bp->pf.vf[vf_rep->vf_idx].fw_fid);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	mutex_lock(&bp->hwrm_cmd_lock);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
818c2ecf20Sopenharmony_ci	if (!rc) {
828c2ecf20Sopenharmony_ci		mtu = le16_to_cpu(resp->max_mtu_configured);
838c2ecf20Sopenharmony_ci		if (!mtu)
848c2ecf20Sopenharmony_ci			*max_mtu = BNXT_MAX_MTU;
858c2ecf20Sopenharmony_ci		else
868c2ecf20Sopenharmony_ci			*max_mtu = mtu;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci	mutex_unlock(&bp->hwrm_cmd_lock);
898c2ecf20Sopenharmony_ci	return rc;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int bnxt_vf_rep_open(struct net_device *dev)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	struct bnxt_vf_rep *vf_rep = netdev_priv(dev);
958c2ecf20Sopenharmony_ci	struct bnxt *bp = vf_rep->bp;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	/* Enable link and TX only if the parent PF is open. */
988c2ecf20Sopenharmony_ci	if (netif_running(bp->dev)) {
998c2ecf20Sopenharmony_ci		netif_carrier_on(dev);
1008c2ecf20Sopenharmony_ci		netif_tx_start_all_queues(dev);
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci	return 0;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int bnxt_vf_rep_close(struct net_device *dev)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	netif_carrier_off(dev);
1088c2ecf20Sopenharmony_ci	netif_tx_disable(dev);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	return 0;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic netdev_tx_t bnxt_vf_rep_xmit(struct sk_buff *skb,
1148c2ecf20Sopenharmony_ci				    struct net_device *dev)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct bnxt_vf_rep *vf_rep = netdev_priv(dev);
1178c2ecf20Sopenharmony_ci	int rc, len = skb->len;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	skb_dst_drop(skb);
1208c2ecf20Sopenharmony_ci	dst_hold((struct dst_entry *)vf_rep->dst);
1218c2ecf20Sopenharmony_ci	skb_dst_set(skb, (struct dst_entry *)vf_rep->dst);
1228c2ecf20Sopenharmony_ci	skb->dev = vf_rep->dst->u.port_info.lower_dev;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	rc = dev_queue_xmit(skb);
1258c2ecf20Sopenharmony_ci	if (!rc) {
1268c2ecf20Sopenharmony_ci		vf_rep->tx_stats.packets++;
1278c2ecf20Sopenharmony_ci		vf_rep->tx_stats.bytes += len;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci	return rc;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic void
1338c2ecf20Sopenharmony_cibnxt_vf_rep_get_stats64(struct net_device *dev,
1348c2ecf20Sopenharmony_ci			struct rtnl_link_stats64 *stats)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct bnxt_vf_rep *vf_rep = netdev_priv(dev);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	stats->rx_packets = vf_rep->rx_stats.packets;
1398c2ecf20Sopenharmony_ci	stats->rx_bytes = vf_rep->rx_stats.bytes;
1408c2ecf20Sopenharmony_ci	stats->tx_packets = vf_rep->tx_stats.packets;
1418c2ecf20Sopenharmony_ci	stats->tx_bytes = vf_rep->tx_stats.bytes;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int bnxt_vf_rep_setup_tc_block_cb(enum tc_setup_type type,
1458c2ecf20Sopenharmony_ci					 void *type_data,
1468c2ecf20Sopenharmony_ci					 void *cb_priv)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct bnxt_vf_rep *vf_rep = cb_priv;
1498c2ecf20Sopenharmony_ci	struct bnxt *bp = vf_rep->bp;
1508c2ecf20Sopenharmony_ci	int vf_fid = bp->pf.vf[vf_rep->vf_idx].fw_fid;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (!bnxt_tc_flower_enabled(vf_rep->bp) ||
1538c2ecf20Sopenharmony_ci	    !tc_cls_can_offload_and_chain0(bp->dev, type_data))
1548c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	switch (type) {
1578c2ecf20Sopenharmony_ci	case TC_SETUP_CLSFLOWER:
1588c2ecf20Sopenharmony_ci		return bnxt_tc_setup_flower(bp, vf_fid, type_data);
1598c2ecf20Sopenharmony_ci	default:
1608c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic LIST_HEAD(bnxt_vf_block_cb_list);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic int bnxt_vf_rep_setup_tc(struct net_device *dev, enum tc_setup_type type,
1678c2ecf20Sopenharmony_ci				void *type_data)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct bnxt_vf_rep *vf_rep = netdev_priv(dev);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	switch (type) {
1728c2ecf20Sopenharmony_ci	case TC_SETUP_BLOCK:
1738c2ecf20Sopenharmony_ci		return flow_block_cb_setup_simple(type_data,
1748c2ecf20Sopenharmony_ci						  &bnxt_vf_block_cb_list,
1758c2ecf20Sopenharmony_ci						  bnxt_vf_rep_setup_tc_block_cb,
1768c2ecf20Sopenharmony_ci						  vf_rep, vf_rep, true);
1778c2ecf20Sopenharmony_ci	default:
1788c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistruct net_device *bnxt_get_vf_rep(struct bnxt *bp, u16 cfa_code)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	u16 vf_idx;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	if (cfa_code && bp->cfa_code_map && BNXT_PF(bp)) {
1878c2ecf20Sopenharmony_ci		vf_idx = bp->cfa_code_map[cfa_code];
1888c2ecf20Sopenharmony_ci		if (vf_idx != VF_IDX_INVALID)
1898c2ecf20Sopenharmony_ci			return bp->vf_reps[vf_idx]->dev;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci	return NULL;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_civoid bnxt_vf_rep_rx(struct bnxt *bp, struct sk_buff *skb)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct bnxt_vf_rep *vf_rep = netdev_priv(skb->dev);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	vf_rep->rx_stats.bytes += skb->len;
1998c2ecf20Sopenharmony_ci	vf_rep->rx_stats.packets++;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	netif_receive_skb(skb);
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic int bnxt_vf_rep_get_phys_port_name(struct net_device *dev, char *buf,
2058c2ecf20Sopenharmony_ci					  size_t len)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct bnxt_vf_rep *vf_rep = netdev_priv(dev);
2088c2ecf20Sopenharmony_ci	struct pci_dev *pf_pdev = vf_rep->bp->pdev;
2098c2ecf20Sopenharmony_ci	int rc;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	rc = snprintf(buf, len, "pf%dvf%d", PCI_FUNC(pf_pdev->devfn),
2128c2ecf20Sopenharmony_ci		      vf_rep->vf_idx);
2138c2ecf20Sopenharmony_ci	if (rc >= len)
2148c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2158c2ecf20Sopenharmony_ci	return 0;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic void bnxt_vf_rep_get_drvinfo(struct net_device *dev,
2198c2ecf20Sopenharmony_ci				    struct ethtool_drvinfo *info)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver));
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic int bnxt_vf_rep_get_port_parent_id(struct net_device *dev,
2258c2ecf20Sopenharmony_ci					  struct netdev_phys_item_id *ppid)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	struct bnxt_vf_rep *vf_rep = netdev_priv(dev);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	/* as only PORT_PARENT_ID is supported currently use common code
2308c2ecf20Sopenharmony_ci	 * between PF and VF-rep for now.
2318c2ecf20Sopenharmony_ci	 */
2328c2ecf20Sopenharmony_ci	return bnxt_get_port_parent_id(vf_rep->bp->dev, ppid);
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic const struct ethtool_ops bnxt_vf_rep_ethtool_ops = {
2368c2ecf20Sopenharmony_ci	.get_drvinfo		= bnxt_vf_rep_get_drvinfo
2378c2ecf20Sopenharmony_ci};
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic const struct net_device_ops bnxt_vf_rep_netdev_ops = {
2408c2ecf20Sopenharmony_ci	.ndo_open		= bnxt_vf_rep_open,
2418c2ecf20Sopenharmony_ci	.ndo_stop		= bnxt_vf_rep_close,
2428c2ecf20Sopenharmony_ci	.ndo_start_xmit		= bnxt_vf_rep_xmit,
2438c2ecf20Sopenharmony_ci	.ndo_get_stats64	= bnxt_vf_rep_get_stats64,
2448c2ecf20Sopenharmony_ci	.ndo_setup_tc		= bnxt_vf_rep_setup_tc,
2458c2ecf20Sopenharmony_ci	.ndo_get_port_parent_id	= bnxt_vf_rep_get_port_parent_id,
2468c2ecf20Sopenharmony_ci	.ndo_get_phys_port_name = bnxt_vf_rep_get_phys_port_name
2478c2ecf20Sopenharmony_ci};
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cibool bnxt_dev_is_vf_rep(struct net_device *dev)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	return dev->netdev_ops == &bnxt_vf_rep_netdev_ops;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci/* Called when the parent PF interface is closed:
2558c2ecf20Sopenharmony_ci * As the mode transition from SWITCHDEV to LEGACY
2568c2ecf20Sopenharmony_ci * happens under the rtnl_lock() this routine is safe
2578c2ecf20Sopenharmony_ci * under the rtnl_lock()
2588c2ecf20Sopenharmony_ci */
2598c2ecf20Sopenharmony_civoid bnxt_vf_reps_close(struct bnxt *bp)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct bnxt_vf_rep *vf_rep;
2628c2ecf20Sopenharmony_ci	u16 num_vfs, i;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV)
2658c2ecf20Sopenharmony_ci		return;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	num_vfs = pci_num_vf(bp->pdev);
2688c2ecf20Sopenharmony_ci	for (i = 0; i < num_vfs; i++) {
2698c2ecf20Sopenharmony_ci		vf_rep = bp->vf_reps[i];
2708c2ecf20Sopenharmony_ci		if (netif_running(vf_rep->dev))
2718c2ecf20Sopenharmony_ci			bnxt_vf_rep_close(vf_rep->dev);
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci/* Called when the parent PF interface is opened (re-opened):
2768c2ecf20Sopenharmony_ci * As the mode transition from SWITCHDEV to LEGACY
2778c2ecf20Sopenharmony_ci * happen under the rtnl_lock() this routine is safe
2788c2ecf20Sopenharmony_ci * under the rtnl_lock()
2798c2ecf20Sopenharmony_ci */
2808c2ecf20Sopenharmony_civoid bnxt_vf_reps_open(struct bnxt *bp)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	int i;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV)
2858c2ecf20Sopenharmony_ci		return;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	for (i = 0; i < pci_num_vf(bp->pdev); i++)
2888c2ecf20Sopenharmony_ci		bnxt_vf_rep_open(bp->vf_reps[i]->dev);
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic void __bnxt_vf_reps_destroy(struct bnxt *bp)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	u16 num_vfs = pci_num_vf(bp->pdev);
2948c2ecf20Sopenharmony_ci	struct bnxt_vf_rep *vf_rep;
2958c2ecf20Sopenharmony_ci	int i;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	for (i = 0; i < num_vfs; i++) {
2988c2ecf20Sopenharmony_ci		vf_rep = bp->vf_reps[i];
2998c2ecf20Sopenharmony_ci		if (vf_rep) {
3008c2ecf20Sopenharmony_ci			dst_release((struct dst_entry *)vf_rep->dst);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci			if (vf_rep->tx_cfa_action != CFA_HANDLE_INVALID)
3038c2ecf20Sopenharmony_ci				hwrm_cfa_vfr_free(bp, vf_rep->vf_idx);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci			if (vf_rep->dev) {
3068c2ecf20Sopenharmony_ci				/* if register_netdev failed, then netdev_ops
3078c2ecf20Sopenharmony_ci				 * would have been set to NULL
3088c2ecf20Sopenharmony_ci				 */
3098c2ecf20Sopenharmony_ci				if (vf_rep->dev->netdev_ops)
3108c2ecf20Sopenharmony_ci					unregister_netdev(vf_rep->dev);
3118c2ecf20Sopenharmony_ci				free_netdev(vf_rep->dev);
3128c2ecf20Sopenharmony_ci			}
3138c2ecf20Sopenharmony_ci		}
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	kfree(bp->vf_reps);
3178c2ecf20Sopenharmony_ci	bp->vf_reps = NULL;
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_civoid bnxt_vf_reps_destroy(struct bnxt *bp)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	bool closed = false;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV)
3258c2ecf20Sopenharmony_ci		return;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (!bp->vf_reps)
3288c2ecf20Sopenharmony_ci		return;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	/* Ensure that parent PF's and VF-reps' RX/TX has been quiesced
3318c2ecf20Sopenharmony_ci	 * before proceeding with VF-rep cleanup.
3328c2ecf20Sopenharmony_ci	 */
3338c2ecf20Sopenharmony_ci	rtnl_lock();
3348c2ecf20Sopenharmony_ci	if (netif_running(bp->dev)) {
3358c2ecf20Sopenharmony_ci		bnxt_close_nic(bp, false, false);
3368c2ecf20Sopenharmony_ci		closed = true;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci	/* un-publish cfa_code_map so that RX path can't see it anymore */
3398c2ecf20Sopenharmony_ci	kfree(bp->cfa_code_map);
3408c2ecf20Sopenharmony_ci	bp->cfa_code_map = NULL;
3418c2ecf20Sopenharmony_ci	bp->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if (closed)
3448c2ecf20Sopenharmony_ci		bnxt_open_nic(bp, false, false);
3458c2ecf20Sopenharmony_ci	rtnl_unlock();
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	/* Need to call vf_reps_destroy() outside of rntl_lock
3488c2ecf20Sopenharmony_ci	 * as unregister_netdev takes rtnl_lock
3498c2ecf20Sopenharmony_ci	 */
3508c2ecf20Sopenharmony_ci	__bnxt_vf_reps_destroy(bp);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci/* Use the OUI of the PF's perm addr and report the same mac addr
3548c2ecf20Sopenharmony_ci * for the same VF-rep each time
3558c2ecf20Sopenharmony_ci */
3568c2ecf20Sopenharmony_cistatic void bnxt_vf_rep_eth_addr_gen(u8 *src_mac, u16 vf_idx, u8 *mac)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	u32 addr;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	ether_addr_copy(mac, src_mac);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	addr = jhash(src_mac, ETH_ALEN, 0) + vf_idx;
3638c2ecf20Sopenharmony_ci	mac[3] = (u8)(addr & 0xFF);
3648c2ecf20Sopenharmony_ci	mac[4] = (u8)((addr >> 8) & 0xFF);
3658c2ecf20Sopenharmony_ci	mac[5] = (u8)((addr >> 16) & 0xFF);
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic void bnxt_vf_rep_netdev_init(struct bnxt *bp, struct bnxt_vf_rep *vf_rep,
3698c2ecf20Sopenharmony_ci				    struct net_device *dev)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	struct net_device *pf_dev = bp->dev;
3728c2ecf20Sopenharmony_ci	u16 max_mtu;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	dev->netdev_ops = &bnxt_vf_rep_netdev_ops;
3758c2ecf20Sopenharmony_ci	dev->ethtool_ops = &bnxt_vf_rep_ethtool_ops;
3768c2ecf20Sopenharmony_ci	/* Just inherit all the featues of the parent PF as the VF-R
3778c2ecf20Sopenharmony_ci	 * uses the RX/TX rings of the parent PF
3788c2ecf20Sopenharmony_ci	 */
3798c2ecf20Sopenharmony_ci	dev->hw_features = pf_dev->hw_features;
3808c2ecf20Sopenharmony_ci	dev->gso_partial_features = pf_dev->gso_partial_features;
3818c2ecf20Sopenharmony_ci	dev->vlan_features = pf_dev->vlan_features;
3828c2ecf20Sopenharmony_ci	dev->hw_enc_features = pf_dev->hw_enc_features;
3838c2ecf20Sopenharmony_ci	dev->features |= pf_dev->features;
3848c2ecf20Sopenharmony_ci	bnxt_vf_rep_eth_addr_gen(bp->pf.mac_addr, vf_rep->vf_idx,
3858c2ecf20Sopenharmony_ci				 dev->perm_addr);
3868c2ecf20Sopenharmony_ci	ether_addr_copy(dev->dev_addr, dev->perm_addr);
3878c2ecf20Sopenharmony_ci	/* Set VF-Rep's max-mtu to the corresponding VF's max-mtu */
3888c2ecf20Sopenharmony_ci	if (!bnxt_hwrm_vfr_qcfg(bp, vf_rep, &max_mtu))
3898c2ecf20Sopenharmony_ci		dev->max_mtu = max_mtu;
3908c2ecf20Sopenharmony_ci	dev->min_mtu = ETH_ZLEN;
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic int bnxt_vf_reps_create(struct bnxt *bp)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	u16 *cfa_code_map = NULL, num_vfs = pci_num_vf(bp->pdev);
3968c2ecf20Sopenharmony_ci	struct bnxt_vf_rep *vf_rep;
3978c2ecf20Sopenharmony_ci	struct net_device *dev;
3988c2ecf20Sopenharmony_ci	int rc, i;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if (!(bp->flags & BNXT_FLAG_DSN_VALID))
4018c2ecf20Sopenharmony_ci		return -ENODEV;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	bp->vf_reps = kcalloc(num_vfs, sizeof(vf_rep), GFP_KERNEL);
4048c2ecf20Sopenharmony_ci	if (!bp->vf_reps)
4058c2ecf20Sopenharmony_ci		return -ENOMEM;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	/* storage for cfa_code to vf-idx mapping */
4088c2ecf20Sopenharmony_ci	cfa_code_map = kmalloc_array(MAX_CFA_CODE, sizeof(*bp->cfa_code_map),
4098c2ecf20Sopenharmony_ci				     GFP_KERNEL);
4108c2ecf20Sopenharmony_ci	if (!cfa_code_map) {
4118c2ecf20Sopenharmony_ci		rc = -ENOMEM;
4128c2ecf20Sopenharmony_ci		goto err;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_CFA_CODE; i++)
4158c2ecf20Sopenharmony_ci		cfa_code_map[i] = VF_IDX_INVALID;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	for (i = 0; i < num_vfs; i++) {
4188c2ecf20Sopenharmony_ci		dev = alloc_etherdev(sizeof(*vf_rep));
4198c2ecf20Sopenharmony_ci		if (!dev) {
4208c2ecf20Sopenharmony_ci			rc = -ENOMEM;
4218c2ecf20Sopenharmony_ci			goto err;
4228c2ecf20Sopenharmony_ci		}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci		vf_rep = netdev_priv(dev);
4258c2ecf20Sopenharmony_ci		bp->vf_reps[i] = vf_rep;
4268c2ecf20Sopenharmony_ci		vf_rep->dev = dev;
4278c2ecf20Sopenharmony_ci		vf_rep->bp = bp;
4288c2ecf20Sopenharmony_ci		vf_rep->vf_idx = i;
4298c2ecf20Sopenharmony_ci		vf_rep->tx_cfa_action = CFA_HANDLE_INVALID;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		/* get cfa handles from FW */
4328c2ecf20Sopenharmony_ci		rc = hwrm_cfa_vfr_alloc(bp, vf_rep->vf_idx,
4338c2ecf20Sopenharmony_ci					&vf_rep->tx_cfa_action,
4348c2ecf20Sopenharmony_ci					&vf_rep->rx_cfa_code);
4358c2ecf20Sopenharmony_ci		if (rc) {
4368c2ecf20Sopenharmony_ci			rc = -ENOLINK;
4378c2ecf20Sopenharmony_ci			goto err;
4388c2ecf20Sopenharmony_ci		}
4398c2ecf20Sopenharmony_ci		cfa_code_map[vf_rep->rx_cfa_code] = vf_rep->vf_idx;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci		vf_rep->dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX,
4428c2ecf20Sopenharmony_ci						 GFP_KERNEL);
4438c2ecf20Sopenharmony_ci		if (!vf_rep->dst) {
4448c2ecf20Sopenharmony_ci			rc = -ENOMEM;
4458c2ecf20Sopenharmony_ci			goto err;
4468c2ecf20Sopenharmony_ci		}
4478c2ecf20Sopenharmony_ci		/* only cfa_action is needed to mux a packet while TXing */
4488c2ecf20Sopenharmony_ci		vf_rep->dst->u.port_info.port_id = vf_rep->tx_cfa_action;
4498c2ecf20Sopenharmony_ci		vf_rep->dst->u.port_info.lower_dev = bp->dev;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci		bnxt_vf_rep_netdev_init(bp, vf_rep, dev);
4528c2ecf20Sopenharmony_ci		rc = register_netdev(dev);
4538c2ecf20Sopenharmony_ci		if (rc) {
4548c2ecf20Sopenharmony_ci			/* no need for unregister_netdev in cleanup */
4558c2ecf20Sopenharmony_ci			dev->netdev_ops = NULL;
4568c2ecf20Sopenharmony_ci			goto err;
4578c2ecf20Sopenharmony_ci		}
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/* publish cfa_code_map only after all VF-reps have been initialized */
4618c2ecf20Sopenharmony_ci	bp->cfa_code_map = cfa_code_map;
4628c2ecf20Sopenharmony_ci	bp->eswitch_mode = DEVLINK_ESWITCH_MODE_SWITCHDEV;
4638c2ecf20Sopenharmony_ci	netif_keep_dst(bp->dev);
4648c2ecf20Sopenharmony_ci	return 0;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cierr:
4678c2ecf20Sopenharmony_ci	netdev_info(bp->dev, "%s error=%d\n", __func__, rc);
4688c2ecf20Sopenharmony_ci	kfree(cfa_code_map);
4698c2ecf20Sopenharmony_ci	__bnxt_vf_reps_destroy(bp);
4708c2ecf20Sopenharmony_ci	return rc;
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci/* Devlink related routines */
4748c2ecf20Sopenharmony_ciint bnxt_dl_eswitch_mode_get(struct devlink *devlink, u16 *mode)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	struct bnxt *bp = bnxt_get_bp_from_dl(devlink);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	*mode = bp->eswitch_mode;
4798c2ecf20Sopenharmony_ci	return 0;
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ciint bnxt_dl_eswitch_mode_set(struct devlink *devlink, u16 mode,
4838c2ecf20Sopenharmony_ci			     struct netlink_ext_ack *extack)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	struct bnxt *bp = bnxt_get_bp_from_dl(devlink);
4868c2ecf20Sopenharmony_ci	int rc = 0;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	mutex_lock(&bp->sriov_lock);
4898c2ecf20Sopenharmony_ci	if (bp->eswitch_mode == mode) {
4908c2ecf20Sopenharmony_ci		netdev_info(bp->dev, "already in %s eswitch mode\n",
4918c2ecf20Sopenharmony_ci			    mode == DEVLINK_ESWITCH_MODE_LEGACY ?
4928c2ecf20Sopenharmony_ci			    "legacy" : "switchdev");
4938c2ecf20Sopenharmony_ci		rc = -EINVAL;
4948c2ecf20Sopenharmony_ci		goto done;
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	switch (mode) {
4988c2ecf20Sopenharmony_ci	case DEVLINK_ESWITCH_MODE_LEGACY:
4998c2ecf20Sopenharmony_ci		bnxt_vf_reps_destroy(bp);
5008c2ecf20Sopenharmony_ci		break;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	case DEVLINK_ESWITCH_MODE_SWITCHDEV:
5038c2ecf20Sopenharmony_ci		if (bp->hwrm_spec_code < 0x10803) {
5048c2ecf20Sopenharmony_ci			netdev_warn(bp->dev, "FW does not support SRIOV E-Switch SWITCHDEV mode\n");
5058c2ecf20Sopenharmony_ci			rc = -ENOTSUPP;
5068c2ecf20Sopenharmony_ci			goto done;
5078c2ecf20Sopenharmony_ci		}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci		if (pci_num_vf(bp->pdev) == 0) {
5108c2ecf20Sopenharmony_ci			netdev_info(bp->dev, "Enable VFs before setting switchdev mode\n");
5118c2ecf20Sopenharmony_ci			rc = -EPERM;
5128c2ecf20Sopenharmony_ci			goto done;
5138c2ecf20Sopenharmony_ci		}
5148c2ecf20Sopenharmony_ci		rc = bnxt_vf_reps_create(bp);
5158c2ecf20Sopenharmony_ci		break;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	default:
5188c2ecf20Sopenharmony_ci		rc = -EINVAL;
5198c2ecf20Sopenharmony_ci		goto done;
5208c2ecf20Sopenharmony_ci	}
5218c2ecf20Sopenharmony_cidone:
5228c2ecf20Sopenharmony_ci	mutex_unlock(&bp->sriov_lock);
5238c2ecf20Sopenharmony_ci	return rc;
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci#endif
527