/* SPDX-License-Identifier: BSD-3-Clause * Copyright(C) 2020 Marvell International Ltd. */ #include #include "octeontx_ethdev.h" #include "octeontx_logs.h" #include "octeontx_rxtx.h" static int octeontx_vlan_hw_filter(struct octeontx_nic *nic, uint8_t flag) { struct octeontx_vlan_info *vlan = &nic->vlan_info; pki_port_vlan_filter_config_t fltr_conf; int rc = 0; if (vlan->filter_on == flag) return rc; fltr_conf.port_type = OCTTX_PORT_TYPE_NET; fltr_conf.fltr_conf = flag; rc = octeontx_pki_port_vlan_fltr_config(nic->port_id, &fltr_conf); if (rc != 0) { octeontx_log_err("Fail to configure vlan hw filter for port %d", nic->port_id); goto done; } vlan->filter_on = flag; done: return rc; } int octeontx_dev_vlan_offload_set(struct rte_eth_dev *dev, int mask) { struct octeontx_nic *nic = octeontx_pmd_priv(dev); struct rte_eth_rxmode *rxmode; int rc = 0; rxmode = &dev->data->dev_conf.rxmode; if (mask & ETH_VLAN_EXTEND_MASK) { octeontx_log_err("Extend offload not supported"); return -ENOTSUP; } if (mask & ETH_VLAN_STRIP_MASK) { octeontx_log_err("VLAN strip offload not supported"); return -ENOTSUP; } if (mask & ETH_VLAN_FILTER_MASK) { if (rxmode->offloads & DEV_RX_OFFLOAD_VLAN_FILTER) { rc = octeontx_vlan_hw_filter(nic, true); if (rc) goto done; nic->rx_offloads |= DEV_RX_OFFLOAD_VLAN_FILTER; nic->rx_offload_flags |= OCCTX_RX_VLAN_FLTR_F; } else { rc = octeontx_vlan_hw_filter(nic, false); if (rc) goto done; nic->rx_offloads &= ~DEV_RX_OFFLOAD_VLAN_FILTER; nic->rx_offload_flags &= ~OCCTX_RX_VLAN_FLTR_F; } } done: return rc; } int octeontx_dev_vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on) { struct octeontx_nic *nic = octeontx_pmd_priv(dev); struct octeontx_vlan_info *vlan = &nic->vlan_info; pki_port_vlan_filter_entry_config_t fltr_entry; struct vlan_entry *entry = NULL; int entry_count = 0; int rc = -EINVAL; if (on) { TAILQ_FOREACH(entry, &vlan->fltr_tbl, next) if (entry->vlan_id == vlan_id) { octeontx_log_dbg("Vlan Id is already set"); return 0; } } else { TAILQ_FOREACH(entry, &vlan->fltr_tbl, next) entry_count++; if (!entry_count) return 0; } fltr_entry.port_type = OCTTX_PORT_TYPE_NET; fltr_entry.vlan_tpid = RTE_ETHER_TYPE_VLAN; fltr_entry.vlan_id = vlan_id; fltr_entry.entry_conf = on; if (on) { entry = rte_zmalloc("octeontx_nic_vlan_entry", sizeof(struct vlan_entry), 0); if (!entry) { octeontx_log_err("Failed to allocate memory"); return -ENOMEM; } } rc = octeontx_pki_port_vlan_fltr_entry_config(nic->port_id, &fltr_entry); if (rc != 0) { octeontx_log_err("Fail to configure vlan filter entry " "for port %d", nic->port_id); if (entry) rte_free(entry); goto done; } if (on) { entry->vlan_id = vlan_id; TAILQ_INSERT_HEAD(&vlan->fltr_tbl, entry, next); } else { TAILQ_FOREACH(entry, &vlan->fltr_tbl, next) { if (entry->vlan_id == vlan_id) { TAILQ_REMOVE(&vlan->fltr_tbl, entry, next); rte_free(entry); break; } } } done: return rc; } int octeontx_dev_vlan_offload_init(struct rte_eth_dev *dev) { struct octeontx_nic *nic = octeontx_pmd_priv(dev); int rc; TAILQ_INIT(&nic->vlan_info.fltr_tbl); rc = octeontx_dev_vlan_offload_set(dev, ETH_VLAN_FILTER_MASK); if (rc) octeontx_log_err("Failed to set vlan offload rc=%d", rc); return rc; } int octeontx_dev_vlan_offload_fini(struct rte_eth_dev *dev) { struct octeontx_nic *nic = octeontx_pmd_priv(dev); struct octeontx_vlan_info *vlan = &nic->vlan_info; pki_port_vlan_filter_entry_config_t fltr_entry; struct vlan_entry *entry; int rc = 0; TAILQ_FOREACH(entry, &vlan->fltr_tbl, next) { fltr_entry.port_type = OCTTX_PORT_TYPE_NET; fltr_entry.vlan_tpid = RTE_ETHER_TYPE_VLAN; fltr_entry.vlan_id = entry->vlan_id; fltr_entry.entry_conf = 0; rc = octeontx_pki_port_vlan_fltr_entry_config(nic->port_id, &fltr_entry); if (rc != 0) { octeontx_log_err("Fail to configure vlan filter entry " "for port %d", nic->port_id); break; } } return rc; } int octeontx_dev_set_link_up(struct rte_eth_dev *eth_dev) { struct octeontx_nic *nic = octeontx_pmd_priv(eth_dev); int rc, i; rc = octeontx_bgx_port_set_link_state(nic->port_id, true); if (rc) goto done; /* Start tx queues */ for (i = 0; i < eth_dev->data->nb_tx_queues; i++) octeontx_dev_tx_queue_start(eth_dev, i); done: return rc; } int octeontx_dev_set_link_down(struct rte_eth_dev *eth_dev) { struct octeontx_nic *nic = octeontx_pmd_priv(eth_dev); int i; /* Stop tx queues */ for (i = 0; i < eth_dev->data->nb_tx_queues; i++) octeontx_dev_tx_queue_stop(eth_dev, i); return octeontx_bgx_port_set_link_state(nic->port_id, false); } int octeontx_dev_flow_ctrl_get(struct rte_eth_dev *dev, struct rte_eth_fc_conf *fc_conf) { struct octeontx_nic *nic = octeontx_pmd_priv(dev); octeontx_mbox_bgx_port_fc_cfg_t conf; int rc; memset(&conf, 0, sizeof(octeontx_mbox_bgx_port_fc_cfg_t)); rc = octeontx_bgx_port_flow_ctrl_cfg(nic->port_id, &conf); if (rc) return rc; if (conf.rx_pause && conf.tx_pause) fc_conf->mode = RTE_FC_FULL; else if (conf.rx_pause) fc_conf->mode = RTE_FC_RX_PAUSE; else if (conf.tx_pause) fc_conf->mode = RTE_FC_TX_PAUSE; else fc_conf->mode = RTE_FC_NONE; /* low_water & high_water values are in Bytes */ fc_conf->low_water = conf.low_water; fc_conf->high_water = conf.high_water; return rc; } int octeontx_dev_flow_ctrl_set(struct rte_eth_dev *dev, struct rte_eth_fc_conf *fc_conf) { struct octeontx_nic *nic = octeontx_pmd_priv(dev); struct octeontx_fc_info *fc = &nic->fc; octeontx_mbox_bgx_port_fc_cfg_t conf; uint8_t tx_pause, rx_pause; uint16_t max_high_water; int rc; if (fc_conf->pause_time || fc_conf->mac_ctrl_frame_fwd || fc_conf->autoneg) { octeontx_log_err("Below flowctrl parameters are not supported " "pause_time, mac_ctrl_frame_fwd and autoneg"); return -EINVAL; } if (fc_conf->high_water == fc->high_water && fc_conf->low_water == fc->low_water && fc_conf->mode == fc->mode) return 0; max_high_water = fc->rx_fifosz - OCTEONTX_BGX_RSVD_RX_FIFOBYTES; if (fc_conf->high_water > max_high_water || fc_conf->high_water < fc_conf->low_water) { octeontx_log_err("Invalid high/low water values " "High_water(in Bytes) must <= 0x%x ", max_high_water); return -EINVAL; } if (fc_conf->high_water % BIT(4) || fc_conf->low_water % BIT(4)) { octeontx_log_err("High/low water value must be multiple of 16"); return -EINVAL; } rx_pause = (fc_conf->mode == RTE_FC_FULL) || (fc_conf->mode == RTE_FC_RX_PAUSE); tx_pause = (fc_conf->mode == RTE_FC_FULL) || (fc_conf->mode == RTE_FC_TX_PAUSE); conf.high_water = fc_conf->high_water; conf.low_water = fc_conf->low_water; conf.fc_cfg = BGX_PORT_FC_CFG_SET; conf.rx_pause = rx_pause; conf.tx_pause = tx_pause; rc = octeontx_bgx_port_flow_ctrl_cfg(nic->port_id, &conf); if (rc) return rc; fc->high_water = fc_conf->high_water; fc->low_water = fc_conf->low_water; fc->mode = fc_conf->mode; return rc; } int octeontx_dev_flow_ctrl_init(struct rte_eth_dev *dev) { struct octeontx_nic *nic = octeontx_pmd_priv(dev); struct octeontx_fc_info *fc = &nic->fc; struct rte_eth_fc_conf fc_conf; int rc; rc = octeontx_dev_flow_ctrl_get(dev, &fc_conf); if (rc) { octeontx_log_err("Failed to get flow control info"); return rc; } fc->def_highmark = fc_conf.high_water; fc->def_lowmark = fc_conf.low_water; fc->def_mode = fc_conf.mode; return rc; } int octeontx_dev_flow_ctrl_fini(struct rte_eth_dev *dev) { struct octeontx_nic *nic = octeontx_pmd_priv(dev); struct octeontx_fc_info *fc = &nic->fc; struct rte_eth_fc_conf fc_conf; memset(&fc_conf, 0, sizeof(struct rte_eth_fc_conf)); /* Restore flow control parameters with default values */ fc_conf.high_water = fc->def_highmark; fc_conf.low_water = fc->def_lowmark; fc_conf.mode = fc->def_mode; return octeontx_dev_flow_ctrl_set(dev, &fc_conf); }