+static int
+ngbe_vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on)
+{
+ struct ngbe_hw *hw = ngbe_dev_hw(dev);
+ struct ngbe_vfta *shadow_vfta = NGBE_DEV_VFTA(dev);
+ uint32_t vfta;
+ uint32_t vid_idx;
+ uint32_t vid_bit;
+
+ vid_idx = (uint32_t)((vlan_id >> 5) & 0x7F);
+ vid_bit = (uint32_t)(1 << (vlan_id & 0x1F));
+ vfta = rd32(hw, NGBE_VLANTBL(vid_idx));
+ if (on)
+ vfta |= vid_bit;
+ else
+ vfta &= ~vid_bit;
+ wr32(hw, NGBE_VLANTBL(vid_idx), vfta);
+
+ /* update local VFTA copy */
+ shadow_vfta->vfta[vid_idx] = vfta;
+
+ return 0;
+}
+
+static void
+ngbe_vlan_strip_queue_set(struct rte_eth_dev *dev, uint16_t queue, int on)
+{
+ struct ngbe_hw *hw = ngbe_dev_hw(dev);
+ struct ngbe_rx_queue *rxq;
+ bool restart;
+ uint32_t rxcfg, rxbal, rxbah;
+
+ if (on)
+ ngbe_vlan_hw_strip_enable(dev, queue);
+ else
+ ngbe_vlan_hw_strip_disable(dev, queue);
+
+ rxq = dev->data->rx_queues[queue];
+ rxbal = rd32(hw, NGBE_RXBAL(rxq->reg_idx));
+ rxbah = rd32(hw, NGBE_RXBAH(rxq->reg_idx));
+ rxcfg = rd32(hw, NGBE_RXCFG(rxq->reg_idx));
+ if (rxq->offloads & RTE_ETH_RX_OFFLOAD_VLAN_STRIP) {
+ restart = (rxcfg & NGBE_RXCFG_ENA) &&
+ !(rxcfg & NGBE_RXCFG_VLAN);
+ rxcfg |= NGBE_RXCFG_VLAN;
+ } else {
+ restart = (rxcfg & NGBE_RXCFG_ENA) &&
+ (rxcfg & NGBE_RXCFG_VLAN);
+ rxcfg &= ~NGBE_RXCFG_VLAN;
+ }
+ rxcfg &= ~NGBE_RXCFG_ENA;
+
+ if (restart) {
+ /* set vlan strip for ring */
+ ngbe_dev_rx_queue_stop(dev, queue);
+ wr32(hw, NGBE_RXBAL(rxq->reg_idx), rxbal);
+ wr32(hw, NGBE_RXBAH(rxq->reg_idx), rxbah);
+ wr32(hw, NGBE_RXCFG(rxq->reg_idx), rxcfg);
+ ngbe_dev_rx_queue_start(dev, queue);
+ }
+}
+
+static int
+ngbe_vlan_tpid_set(struct rte_eth_dev *dev,
+ enum rte_vlan_type vlan_type,
+ uint16_t tpid)
+{
+ struct ngbe_hw *hw = ngbe_dev_hw(dev);
+ int ret = 0;
+ uint32_t portctrl, vlan_ext, qinq;
+
+ portctrl = rd32(hw, NGBE_PORTCTL);
+
+ vlan_ext = (portctrl & NGBE_PORTCTL_VLANEXT);
+ qinq = vlan_ext && (portctrl & NGBE_PORTCTL_QINQ);
+ switch (vlan_type) {
+ case RTE_ETH_VLAN_TYPE_INNER:
+ if (vlan_ext) {
+ wr32m(hw, NGBE_VLANCTL,
+ NGBE_VLANCTL_TPID_MASK,
+ NGBE_VLANCTL_TPID(tpid));
+ wr32m(hw, NGBE_DMATXCTRL,
+ NGBE_DMATXCTRL_TPID_MASK,
+ NGBE_DMATXCTRL_TPID(tpid));
+ } else {
+ ret = -ENOTSUP;
+ PMD_DRV_LOG(ERR,
+ "Inner type is not supported by single VLAN");
+ }
+
+ if (qinq) {
+ wr32m(hw, NGBE_TAGTPID(0),
+ NGBE_TAGTPID_LSB_MASK,
+ NGBE_TAGTPID_LSB(tpid));
+ }
+ break;
+ case RTE_ETH_VLAN_TYPE_OUTER:
+ if (vlan_ext) {
+ /* Only the high 16-bits is valid */
+ wr32m(hw, NGBE_EXTAG,
+ NGBE_EXTAG_VLAN_MASK,
+ NGBE_EXTAG_VLAN(tpid));
+ } else {
+ wr32m(hw, NGBE_VLANCTL,
+ NGBE_VLANCTL_TPID_MASK,
+ NGBE_VLANCTL_TPID(tpid));
+ wr32m(hw, NGBE_DMATXCTRL,
+ NGBE_DMATXCTRL_TPID_MASK,
+ NGBE_DMATXCTRL_TPID(tpid));
+ }
+
+ if (qinq) {
+ wr32m(hw, NGBE_TAGTPID(0),
+ NGBE_TAGTPID_MSB_MASK,
+ NGBE_TAGTPID_MSB(tpid));
+ }
+ break;
+ default:
+ PMD_DRV_LOG(ERR, "Unsupported VLAN type %d", vlan_type);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+void
+ngbe_vlan_hw_filter_disable(struct rte_eth_dev *dev)
+{
+ struct ngbe_hw *hw = ngbe_dev_hw(dev);
+ uint32_t vlnctrl;
+
+ PMD_INIT_FUNC_TRACE();
+
+ /* Filter Table Disable */
+ vlnctrl = rd32(hw, NGBE_VLANCTL);
+ vlnctrl &= ~NGBE_VLANCTL_VFE;
+ wr32(hw, NGBE_VLANCTL, vlnctrl);
+}
+
+void
+ngbe_vlan_hw_filter_enable(struct rte_eth_dev *dev)
+{
+ struct ngbe_hw *hw = ngbe_dev_hw(dev);
+ struct ngbe_vfta *shadow_vfta = NGBE_DEV_VFTA(dev);
+ uint32_t vlnctrl;
+ uint16_t i;
+
+ PMD_INIT_FUNC_TRACE();
+
+ /* Filter Table Enable */
+ vlnctrl = rd32(hw, NGBE_VLANCTL);
+ vlnctrl &= ~NGBE_VLANCTL_CFIENA;
+ vlnctrl |= NGBE_VLANCTL_VFE;
+ wr32(hw, NGBE_VLANCTL, vlnctrl);
+
+ /* write whatever is in local vfta copy */
+ for (i = 0; i < NGBE_VFTA_SIZE; i++)
+ wr32(hw, NGBE_VLANTBL(i), shadow_vfta->vfta[i]);
+}
+
+void
+ngbe_vlan_hw_strip_bitmap_set(struct rte_eth_dev *dev, uint16_t queue, bool on)
+{
+ struct ngbe_hwstrip *hwstrip = NGBE_DEV_HWSTRIP(dev);
+ struct ngbe_rx_queue *rxq;
+
+ if (queue >= NGBE_MAX_RX_QUEUE_NUM)
+ return;
+
+ if (on)
+ NGBE_SET_HWSTRIP(hwstrip, queue);
+ else
+ NGBE_CLEAR_HWSTRIP(hwstrip, queue);
+
+ if (queue >= dev->data->nb_rx_queues)
+ return;
+
+ rxq = dev->data->rx_queues[queue];
+
+ if (on) {
+ rxq->vlan_flags = RTE_MBUF_F_RX_VLAN | RTE_MBUF_F_RX_VLAN_STRIPPED;
+ rxq->offloads |= RTE_ETH_RX_OFFLOAD_VLAN_STRIP;
+ } else {
+ rxq->vlan_flags = RTE_MBUF_F_RX_VLAN;
+ rxq->offloads &= ~RTE_ETH_RX_OFFLOAD_VLAN_STRIP;
+ }
+}
+
+static void
+ngbe_vlan_hw_strip_disable(struct rte_eth_dev *dev, uint16_t queue)
+{
+ struct ngbe_hw *hw = ngbe_dev_hw(dev);
+ uint32_t ctrl;
+
+ PMD_INIT_FUNC_TRACE();
+
+ ctrl = rd32(hw, NGBE_RXCFG(queue));
+ ctrl &= ~NGBE_RXCFG_VLAN;
+ wr32(hw, NGBE_RXCFG(queue), ctrl);
+
+ /* record those setting for HW strip per queue */
+ ngbe_vlan_hw_strip_bitmap_set(dev, queue, 0);
+}
+
+static void
+ngbe_vlan_hw_strip_enable(struct rte_eth_dev *dev, uint16_t queue)
+{
+ struct ngbe_hw *hw = ngbe_dev_hw(dev);
+ uint32_t ctrl;
+
+ PMD_INIT_FUNC_TRACE();
+
+ ctrl = rd32(hw, NGBE_RXCFG(queue));
+ ctrl |= NGBE_RXCFG_VLAN;
+ wr32(hw, NGBE_RXCFG(queue), ctrl);
+
+ /* record those setting for HW strip per queue */
+ ngbe_vlan_hw_strip_bitmap_set(dev, queue, 1);
+}
+
+static void
+ngbe_vlan_hw_extend_disable(struct rte_eth_dev *dev)
+{
+ struct ngbe_hw *hw = ngbe_dev_hw(dev);
+ uint32_t ctrl;
+
+ PMD_INIT_FUNC_TRACE();
+
+ ctrl = rd32(hw, NGBE_PORTCTL);
+ ctrl &= ~NGBE_PORTCTL_VLANEXT;
+ ctrl &= ~NGBE_PORTCTL_QINQ;
+ wr32(hw, NGBE_PORTCTL, ctrl);
+}
+
+static void
+ngbe_vlan_hw_extend_enable(struct rte_eth_dev *dev)
+{
+ struct ngbe_hw *hw = ngbe_dev_hw(dev);
+ uint32_t ctrl;
+
+ PMD_INIT_FUNC_TRACE();
+
+ ctrl = rd32(hw, NGBE_PORTCTL);
+ ctrl |= NGBE_PORTCTL_VLANEXT | NGBE_PORTCTL_QINQ;
+ wr32(hw, NGBE_PORTCTL, ctrl);
+}
+
+static void
+ngbe_qinq_hw_strip_disable(struct rte_eth_dev *dev)
+{
+ struct ngbe_hw *hw = ngbe_dev_hw(dev);
+ uint32_t ctrl;
+
+ PMD_INIT_FUNC_TRACE();
+
+ ctrl = rd32(hw, NGBE_PORTCTL);
+ ctrl &= ~NGBE_PORTCTL_QINQ;
+ wr32(hw, NGBE_PORTCTL, ctrl);
+}
+
+static void
+ngbe_qinq_hw_strip_enable(struct rte_eth_dev *dev)
+{
+ struct ngbe_hw *hw = ngbe_dev_hw(dev);
+ uint32_t ctrl;
+
+ PMD_INIT_FUNC_TRACE();
+
+ ctrl = rd32(hw, NGBE_PORTCTL);
+ ctrl |= NGBE_PORTCTL_QINQ | NGBE_PORTCTL_VLANEXT;
+ wr32(hw, NGBE_PORTCTL, ctrl);
+}
+
+void
+ngbe_vlan_hw_strip_config(struct rte_eth_dev *dev)
+{
+ struct ngbe_rx_queue *rxq;
+ uint16_t i;
+
+ PMD_INIT_FUNC_TRACE();
+
+ for (i = 0; i < dev->data->nb_rx_queues; i++) {
+ rxq = dev->data->rx_queues[i];
+
+ if (rxq->offloads & RTE_ETH_RX_OFFLOAD_VLAN_STRIP)
+ ngbe_vlan_hw_strip_enable(dev, i);
+ else
+ ngbe_vlan_hw_strip_disable(dev, i);
+ }
+}
+
+void
+ngbe_config_vlan_strip_on_all_queues(struct rte_eth_dev *dev, int mask)
+{
+ uint16_t i;
+ struct rte_eth_rxmode *rxmode;
+ struct ngbe_rx_queue *rxq;
+
+ if (mask & RTE_ETH_VLAN_STRIP_MASK) {
+ rxmode = &dev->data->dev_conf.rxmode;
+ if (rxmode->offloads & RTE_ETH_RX_OFFLOAD_VLAN_STRIP)
+ for (i = 0; i < dev->data->nb_rx_queues; i++) {
+ rxq = dev->data->rx_queues[i];
+ rxq->offloads |= RTE_ETH_RX_OFFLOAD_VLAN_STRIP;
+ }
+ else
+ for (i = 0; i < dev->data->nb_rx_queues; i++) {
+ rxq = dev->data->rx_queues[i];
+ rxq->offloads &= ~RTE_ETH_RX_OFFLOAD_VLAN_STRIP;
+ }
+ }
+}
+
+static int
+ngbe_vlan_offload_config(struct rte_eth_dev *dev, int mask)
+{
+ struct rte_eth_rxmode *rxmode;
+ rxmode = &dev->data->dev_conf.rxmode;
+
+ if (mask & RTE_ETH_VLAN_STRIP_MASK)
+ ngbe_vlan_hw_strip_config(dev);
+
+ if (mask & RTE_ETH_VLAN_FILTER_MASK) {
+ if (rxmode->offloads & RTE_ETH_RX_OFFLOAD_VLAN_FILTER)
+ ngbe_vlan_hw_filter_enable(dev);
+ else
+ ngbe_vlan_hw_filter_disable(dev);
+ }
+
+ if (mask & RTE_ETH_VLAN_EXTEND_MASK) {
+ if (rxmode->offloads & RTE_ETH_RX_OFFLOAD_VLAN_EXTEND)
+ ngbe_vlan_hw_extend_enable(dev);
+ else
+ ngbe_vlan_hw_extend_disable(dev);
+ }
+
+ if (mask & RTE_ETH_QINQ_STRIP_MASK) {
+ if (rxmode->offloads & RTE_ETH_RX_OFFLOAD_QINQ_STRIP)
+ ngbe_qinq_hw_strip_enable(dev);
+ else
+ ngbe_qinq_hw_strip_disable(dev);
+ }
+
+ return 0;
+}
+
+static int
+ngbe_vlan_offload_set(struct rte_eth_dev *dev, int mask)
+{
+ ngbe_config_vlan_strip_on_all_queues(dev, mask);
+
+ ngbe_vlan_offload_config(dev, mask);
+
+ return 0;
+}
+