+ struct rte_intr_handle *intr_handle;
+
+ dpaa2_dev = container_of(rdev, struct rte_dpaa2_device, device);
+ intr_handle = dpaa2_dev->intr_handle;
+
+ PMD_INIT_FUNC_TRACE();
+ ret = dpni_enable(dpni, CMD_PRI_LOW, priv->token);
+ if (ret) {
+ DPAA2_PMD_ERR("Failure in enabling dpni %d device: err=%d",
+ priv->hw_id, ret);
+ return ret;
+ }
+
+ /* Power up the phy. Needed to make the link go UP */
+ dpaa2_dev_set_link_up(dev);
+
+ for (i = 0; i < data->nb_rx_queues; i++) {
+ dpaa2_q = (struct dpaa2_queue *)data->rx_queues[i];
+ ret = dpni_get_queue(dpni, CMD_PRI_LOW, priv->token,
+ DPNI_QUEUE_RX, dpaa2_q->tc_index,
+ dpaa2_q->flow_id, &cfg, &qid);
+ if (ret) {
+ DPAA2_PMD_ERR("Error in getting flow information: "
+ "err=%d", ret);
+ return ret;
+ }
+ dpaa2_q->fqid = qid.fqid;
+ }
+
+ if (dpaa2_enable_err_queue) {
+ ret = dpni_get_queue(dpni, CMD_PRI_LOW, priv->token,
+ DPNI_QUEUE_RX_ERR, 0, 0, &cfg, &qid);
+ if (ret) {
+ DPAA2_PMD_ERR("Error getting rx err flow information: err=%d",
+ ret);
+ return ret;
+ }
+ dpaa2_q = (struct dpaa2_queue *)priv->rx_err_vq;
+ dpaa2_q->fqid = qid.fqid;
+ dpaa2_q->eth_data = dev->data;
+
+ err_cfg.errors = DPNI_ERROR_DISC;
+ err_cfg.error_action = DPNI_ERROR_ACTION_SEND_TO_ERROR_QUEUE;
+ } else {
+ /* checksum errors, send them to normal path
+ * and set it in annotation
+ */
+ err_cfg.errors = DPNI_ERROR_L3CE | DPNI_ERROR_L4CE;
+
+ /* if packet with parse error are not to be dropped */
+ err_cfg.errors |= DPNI_ERROR_PHE;
+
+ err_cfg.error_action = DPNI_ERROR_ACTION_CONTINUE;
+ }
+ err_cfg.set_frame_annotation = true;
+
+ ret = dpni_set_errors_behavior(dpni, CMD_PRI_LOW,
+ priv->token, &err_cfg);
+ if (ret) {
+ DPAA2_PMD_ERR("Error to dpni_set_errors_behavior: code = %d",
+ ret);
+ return ret;
+ }
+
+ /* if the interrupts were configured on this devices*/
+ if (intr_handle && rte_intr_fd_get(intr_handle) &&
+ dev->data->dev_conf.intr_conf.lsc != 0) {
+ /* Registering LSC interrupt handler */
+ rte_intr_callback_register(intr_handle,
+ dpaa2_interrupt_handler,
+ (void *)dev);
+
+ /* enable vfio intr/eventfd mapping
+ * Interrupt index 0 is required, so we can not use
+ * rte_intr_enable.
+ */
+ rte_dpaa2_intr_enable(intr_handle, DPNI_IRQ_INDEX);
+
+ /* enable dpni_irqs */
+ dpaa2_eth_setup_irqs(dev, 1);
+ }
+
+ /* Change the tx burst function if ordered queues are used */
+ if (priv->en_ordered)
+ dev->tx_pkt_burst = dpaa2_dev_tx_ordered;
+
+ return 0;
+}
+
+/**
+ * This routine disables all traffic on the adapter by issuing a
+ * global reset on the MAC.
+ */
+static int
+dpaa2_dev_stop(struct rte_eth_dev *dev)
+{
+ struct dpaa2_dev_priv *priv = dev->data->dev_private;
+ struct fsl_mc_io *dpni = (struct fsl_mc_io *)dev->process_private;
+ int ret;
+ struct rte_eth_link link;
+ struct rte_device *rdev = dev->device;
+ struct rte_intr_handle *intr_handle;
+ struct rte_dpaa2_device *dpaa2_dev;
+
+ dpaa2_dev = container_of(rdev, struct rte_dpaa2_device, device);
+ intr_handle = dpaa2_dev->intr_handle;
+
+ PMD_INIT_FUNC_TRACE();
+
+ /* reset interrupt callback */
+ if (intr_handle && rte_intr_fd_get(intr_handle) &&
+ dev->data->dev_conf.intr_conf.lsc != 0) {
+ /*disable dpni irqs */
+ dpaa2_eth_setup_irqs(dev, 0);
+
+ /* disable vfio intr before callback unregister */
+ rte_dpaa2_intr_disable(intr_handle, DPNI_IRQ_INDEX);
+
+ /* Unregistering LSC interrupt handler */
+ rte_intr_callback_unregister(intr_handle,
+ dpaa2_interrupt_handler,
+ (void *)dev);
+ }
+
+ dpaa2_dev_set_link_down(dev);
+
+ ret = dpni_disable(dpni, CMD_PRI_LOW, priv->token);
+ if (ret) {
+ DPAA2_PMD_ERR("Failure (ret %d) in disabling dpni %d dev",
+ ret, priv->hw_id);
+ return ret;
+ }
+
+ /* clear the recorded link status */
+ memset(&link, 0, sizeof(link));
+ rte_eth_linkstatus_set(dev, &link);
+
+ return 0;
+}
+
+static int
+dpaa2_dev_close(struct rte_eth_dev *dev)
+{
+ struct dpaa2_dev_priv *priv = dev->data->dev_private;
+ struct fsl_mc_io *dpni = (struct fsl_mc_io *)dev->process_private;
+ int i, ret;
+ struct rte_eth_link link;
+
+ PMD_INIT_FUNC_TRACE();
+
+ if (rte_eal_process_type() != RTE_PROC_PRIMARY)
+ return 0;
+
+ if (!dpni) {
+ DPAA2_PMD_WARN("Already closed or not started");
+ return -1;
+ }
+
+ dpaa2_tm_deinit(dev);
+ dpaa2_flow_clean(dev);
+ /* Clean the device first */
+ ret = dpni_reset(dpni, CMD_PRI_LOW, priv->token);
+ if (ret) {
+ DPAA2_PMD_ERR("Failure cleaning dpni device: err=%d", ret);
+ return -1;
+ }
+
+ memset(&link, 0, sizeof(link));
+ rte_eth_linkstatus_set(dev, &link);
+
+ /* Free private queues memory */
+ dpaa2_free_rx_tx_queues(dev);
+ /* Close the device at underlying layer*/
+ ret = dpni_close(dpni, CMD_PRI_LOW, priv->token);
+ if (ret) {
+ DPAA2_PMD_ERR("Failure closing dpni device with err code %d",
+ ret);
+ }
+
+ /* Free the allocated memory for ethernet private data and dpni*/
+ priv->hw = NULL;
+ dev->process_private = NULL;
+ rte_free(dpni);
+
+ for (i = 0; i < MAX_TCS; i++)
+ rte_free((void *)(size_t)priv->extract.tc_extract_param[i]);
+
+ if (priv->extract.qos_extract_param)
+ rte_free((void *)(size_t)priv->extract.qos_extract_param);
+
+ DPAA2_PMD_INFO("%s: netdev deleted", dev->data->name);
+ return 0;
+}
+
+static int
+dpaa2_dev_promiscuous_enable(
+ struct rte_eth_dev *dev)
+{
+ int ret;
+ struct dpaa2_dev_priv *priv = dev->data->dev_private;
+ struct fsl_mc_io *dpni = (struct fsl_mc_io *)dev->process_private;
+
+ PMD_INIT_FUNC_TRACE();
+
+ if (dpni == NULL) {
+ DPAA2_PMD_ERR("dpni is NULL");
+ return -ENODEV;
+ }
+
+ ret = dpni_set_unicast_promisc(dpni, CMD_PRI_LOW, priv->token, true);
+ if (ret < 0)
+ DPAA2_PMD_ERR("Unable to enable U promisc mode %d", ret);
+
+ ret = dpni_set_multicast_promisc(dpni, CMD_PRI_LOW, priv->token, true);
+ if (ret < 0)
+ DPAA2_PMD_ERR("Unable to enable M promisc mode %d", ret);
+
+ return ret;
+}
+
+static int
+dpaa2_dev_promiscuous_disable(
+ struct rte_eth_dev *dev)
+{
+ int ret;
+ struct dpaa2_dev_priv *priv = dev->data->dev_private;
+ struct fsl_mc_io *dpni = (struct fsl_mc_io *)dev->process_private;
+
+ PMD_INIT_FUNC_TRACE();
+
+ if (dpni == NULL) {
+ DPAA2_PMD_ERR("dpni is NULL");
+ return -ENODEV;
+ }
+
+ ret = dpni_set_unicast_promisc(dpni, CMD_PRI_LOW, priv->token, false);
+ if (ret < 0)
+ DPAA2_PMD_ERR("Unable to disable U promisc mode %d", ret);
+
+ if (dev->data->all_multicast == 0) {
+ ret = dpni_set_multicast_promisc(dpni, CMD_PRI_LOW,
+ priv->token, false);
+ if (ret < 0)
+ DPAA2_PMD_ERR("Unable to disable M promisc mode %d",
+ ret);
+ }
+
+ return ret;
+}
+
+static int
+dpaa2_dev_allmulticast_enable(
+ struct rte_eth_dev *dev)
+{
+ int ret;
+ struct dpaa2_dev_priv *priv = dev->data->dev_private;
+ struct fsl_mc_io *dpni = (struct fsl_mc_io *)dev->process_private;
+
+ PMD_INIT_FUNC_TRACE();
+
+ if (dpni == NULL) {
+ DPAA2_PMD_ERR("dpni is NULL");
+ return -ENODEV;
+ }
+
+ ret = dpni_set_multicast_promisc(dpni, CMD_PRI_LOW, priv->token, true);
+ if (ret < 0)
+ DPAA2_PMD_ERR("Unable to enable multicast mode %d", ret);
+
+ return ret;
+}
+
+static int
+dpaa2_dev_allmulticast_disable(struct rte_eth_dev *dev)
+{
+ int ret;
+ struct dpaa2_dev_priv *priv = dev->data->dev_private;
+ struct fsl_mc_io *dpni = (struct fsl_mc_io *)dev->process_private;
+
+ PMD_INIT_FUNC_TRACE();
+
+ if (dpni == NULL) {
+ DPAA2_PMD_ERR("dpni is NULL");
+ return -ENODEV;
+ }
+
+ /* must remain on for all promiscuous */
+ if (dev->data->promiscuous == 1)
+ return 0;
+
+ ret = dpni_set_multicast_promisc(dpni, CMD_PRI_LOW, priv->token, false);
+ if (ret < 0)
+ DPAA2_PMD_ERR("Unable to disable multicast mode %d", ret);
+
+ return ret;
+}
+
+static int
+dpaa2_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
+{
+ int ret;
+ struct dpaa2_dev_priv *priv = dev->data->dev_private;
+ struct fsl_mc_io *dpni = (struct fsl_mc_io *)dev->process_private;
+ uint32_t frame_size = mtu + RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN
+ + VLAN_TAG_SIZE;
+
+ PMD_INIT_FUNC_TRACE();
+
+ if (dpni == NULL) {
+ DPAA2_PMD_ERR("dpni is NULL");
+ return -EINVAL;
+ }
+
+ /* Set the Max Rx frame length as 'mtu' +
+ * Maximum Ethernet header length
+ */
+ ret = dpni_set_max_frame_length(dpni, CMD_PRI_LOW, priv->token,
+ frame_size - RTE_ETHER_CRC_LEN);
+ if (ret) {
+ DPAA2_PMD_ERR("Setting the max frame length failed");
+ return -1;
+ }
+ DPAA2_PMD_INFO("MTU configured for the device: %d", mtu);
+ return 0;
+}
+
+static int
+dpaa2_dev_add_mac_addr(struct rte_eth_dev *dev,
+ struct rte_ether_addr *addr,
+ __rte_unused uint32_t index,
+ __rte_unused uint32_t pool)
+{
+ int ret;
+ struct dpaa2_dev_priv *priv = dev->data->dev_private;
+ struct fsl_mc_io *dpni = (struct fsl_mc_io *)dev->process_private;
+
+ PMD_INIT_FUNC_TRACE();
+
+ if (dpni == NULL) {
+ DPAA2_PMD_ERR("dpni is NULL");
+ return -1;
+ }
+
+ ret = dpni_add_mac_addr(dpni, CMD_PRI_LOW, priv->token,
+ addr->addr_bytes, 0, 0, 0);
+ if (ret)
+ DPAA2_PMD_ERR(
+ "error: Adding the MAC ADDR failed: err = %d", ret);
+ return 0;
+}
+
+static void
+dpaa2_dev_remove_mac_addr(struct rte_eth_dev *dev,
+ uint32_t index)
+{
+ int ret;
+ struct dpaa2_dev_priv *priv = dev->data->dev_private;
+ struct fsl_mc_io *dpni = (struct fsl_mc_io *)dev->process_private;
+ struct rte_eth_dev_data *data = dev->data;
+ struct rte_ether_addr *macaddr;
+
+ PMD_INIT_FUNC_TRACE();
+
+ macaddr = &data->mac_addrs[index];
+
+ if (dpni == NULL) {
+ DPAA2_PMD_ERR("dpni is NULL");
+ return;
+ }
+
+ ret = dpni_remove_mac_addr(dpni, CMD_PRI_LOW,
+ priv->token, macaddr->addr_bytes);
+ if (ret)
+ DPAA2_PMD_ERR(
+ "error: Removing the MAC ADDR failed: err = %d", ret);
+}
+
+static int
+dpaa2_dev_set_mac_addr(struct rte_eth_dev *dev,
+ struct rte_ether_addr *addr)
+{
+ int ret;
+ struct dpaa2_dev_priv *priv = dev->data->dev_private;
+ struct fsl_mc_io *dpni = (struct fsl_mc_io *)dev->process_private;
+
+ PMD_INIT_FUNC_TRACE();
+
+ if (dpni == NULL) {
+ DPAA2_PMD_ERR("dpni is NULL");
+ return -EINVAL;
+ }
+
+ ret = dpni_set_primary_mac_addr(dpni, CMD_PRI_LOW,
+ priv->token, addr->addr_bytes);
+
+ if (ret)
+ DPAA2_PMD_ERR(
+ "error: Setting the MAC ADDR failed %d", ret);
+
+ return ret;
+}
+
+static
+int dpaa2_dev_stats_get(struct rte_eth_dev *dev,
+ struct rte_eth_stats *stats)
+{
+ struct dpaa2_dev_priv *priv = dev->data->dev_private;
+ struct fsl_mc_io *dpni = (struct fsl_mc_io *)dev->process_private;
+ int32_t retcode;
+ uint8_t page0 = 0, page1 = 1, page2 = 2;
+ union dpni_statistics value;
+ int i;
+ struct dpaa2_queue *dpaa2_rxq, *dpaa2_txq;
+
+ memset(&value, 0, sizeof(union dpni_statistics));
+
+ PMD_INIT_FUNC_TRACE();
+
+ if (!dpni) {
+ DPAA2_PMD_ERR("dpni is NULL");
+ return -EINVAL;
+ }
+
+ if (!stats) {
+ DPAA2_PMD_ERR("stats is NULL");
+ return -EINVAL;
+ }
+
+ /*Get Counters from page_0*/
+ retcode = dpni_get_statistics(dpni, CMD_PRI_LOW, priv->token,
+ page0, 0, &value);
+ if (retcode)
+ goto err;
+
+ stats->ipackets = value.page_0.ingress_all_frames;
+ stats->ibytes = value.page_0.ingress_all_bytes;
+
+ /*Get Counters from page_1*/
+ retcode = dpni_get_statistics(dpni, CMD_PRI_LOW, priv->token,
+ page1, 0, &value);
+ if (retcode)
+ goto err;
+
+ stats->opackets = value.page_1.egress_all_frames;
+ stats->obytes = value.page_1.egress_all_bytes;
+
+ /*Get Counters from page_2*/
+ retcode = dpni_get_statistics(dpni, CMD_PRI_LOW, priv->token,
+ page2, 0, &value);
+ if (retcode)
+ goto err;
+
+ /* Ingress drop frame count due to configured rules */
+ stats->ierrors = value.page_2.ingress_filtered_frames;
+ /* Ingress drop frame count due to error */
+ stats->ierrors += value.page_2.ingress_discarded_frames;
+
+ stats->oerrors = value.page_2.egress_discarded_frames;
+ stats->imissed = value.page_2.ingress_nobuffer_discards;
+
+ /* Fill in per queue stats */
+ for (i = 0; (i < RTE_ETHDEV_QUEUE_STAT_CNTRS) &&
+ (i < priv->nb_rx_queues || i < priv->nb_tx_queues); ++i) {
+ dpaa2_rxq = (struct dpaa2_queue *)priv->rx_vq[i];
+ dpaa2_txq = (struct dpaa2_queue *)priv->tx_vq[i];
+ if (dpaa2_rxq)
+ stats->q_ipackets[i] = dpaa2_rxq->rx_pkts;
+ if (dpaa2_txq)
+ stats->q_opackets[i] = dpaa2_txq->tx_pkts;
+
+ /* Byte counting is not implemented */
+ stats->q_ibytes[i] = 0;
+ stats->q_obytes[i] = 0;
+ }
+
+ return 0;
+
+err:
+ DPAA2_PMD_ERR("Operation not completed:Error Code = %d", retcode);
+ return retcode;
+};
+
+static int
+dpaa2_dev_xstats_get(struct rte_eth_dev *dev, struct rte_eth_xstat *xstats,
+ unsigned int n)
+{
+ struct dpaa2_dev_priv *priv = dev->data->dev_private;
+ struct fsl_mc_io *dpni = (struct fsl_mc_io *)dev->process_private;
+ int32_t retcode;
+ union dpni_statistics value[5] = {};
+ unsigned int i = 0, num = RTE_DIM(dpaa2_xstats_strings);
+
+ if (n < num)
+ return num;
+
+ if (xstats == NULL)
+ return 0;
+
+ /* Get Counters from page_0*/
+ retcode = dpni_get_statistics(dpni, CMD_PRI_LOW, priv->token,
+ 0, 0, &value[0]);
+ if (retcode)
+ goto err;
+
+ /* Get Counters from page_1*/
+ retcode = dpni_get_statistics(dpni, CMD_PRI_LOW, priv->token,
+ 1, 0, &value[1]);
+ if (retcode)
+ goto err;
+
+ /* Get Counters from page_2*/
+ retcode = dpni_get_statistics(dpni, CMD_PRI_LOW, priv->token,
+ 2, 0, &value[2]);
+ if (retcode)
+ goto err;
+
+ for (i = 0; i < priv->max_cgs; i++) {
+ if (!priv->cgid_in_use[i]) {
+ /* Get Counters from page_4*/
+ retcode = dpni_get_statistics(dpni, CMD_PRI_LOW,
+ priv->token,
+ 4, 0, &value[4]);
+ if (retcode)
+ goto err;
+ break;
+ }
+ }
+
+ for (i = 0; i < num; i++) {
+ xstats[i].id = i;
+ xstats[i].value = value[dpaa2_xstats_strings[i].page_id].
+ raw.counter[dpaa2_xstats_strings[i].stats_id];
+ }
+ return i;
+err:
+ DPAA2_PMD_ERR("Error in obtaining extended stats (%d)", retcode);
+ return retcode;
+}
+
+static int
+dpaa2_xstats_get_names(__rte_unused struct rte_eth_dev *dev,
+ struct rte_eth_xstat_name *xstats_names,
+ unsigned int limit)
+{
+ unsigned int i, stat_cnt = RTE_DIM(dpaa2_xstats_strings);
+
+ if (limit < stat_cnt)
+ return stat_cnt;
+
+ if (xstats_names != NULL)
+ for (i = 0; i < stat_cnt; i++)
+ strlcpy(xstats_names[i].name,
+ dpaa2_xstats_strings[i].name,
+ sizeof(xstats_names[i].name));
+
+ return stat_cnt;
+}
+
+static int
+dpaa2_xstats_get_by_id(struct rte_eth_dev *dev, const uint64_t *ids,
+ uint64_t *values, unsigned int n)
+{
+ unsigned int i, stat_cnt = RTE_DIM(dpaa2_xstats_strings);
+ uint64_t values_copy[stat_cnt];
+
+ if (!ids) {
+ struct dpaa2_dev_priv *priv = dev->data->dev_private;
+ struct fsl_mc_io *dpni =
+ (struct fsl_mc_io *)dev->process_private;
+ int32_t retcode;
+ union dpni_statistics value[5] = {};
+
+ if (n < stat_cnt)
+ return stat_cnt;
+
+ if (!values)
+ return 0;
+
+ /* Get Counters from page_0*/
+ retcode = dpni_get_statistics(dpni, CMD_PRI_LOW, priv->token,
+ 0, 0, &value[0]);
+ if (retcode)
+ return 0;
+
+ /* Get Counters from page_1*/
+ retcode = dpni_get_statistics(dpni, CMD_PRI_LOW, priv->token,
+ 1, 0, &value[1]);
+ if (retcode)
+ return 0;
+
+ /* Get Counters from page_2*/
+ retcode = dpni_get_statistics(dpni, CMD_PRI_LOW, priv->token,
+ 2, 0, &value[2]);
+ if (retcode)
+ return 0;
+
+ /* Get Counters from page_4*/
+ retcode = dpni_get_statistics(dpni, CMD_PRI_LOW, priv->token,
+ 4, 0, &value[4]);
+ if (retcode)
+ return 0;
+
+ for (i = 0; i < stat_cnt; i++) {
+ values[i] = value[dpaa2_xstats_strings[i].page_id].
+ raw.counter[dpaa2_xstats_strings[i].stats_id];
+ }
+ return stat_cnt;
+ }
+
+ dpaa2_xstats_get_by_id(dev, NULL, values_copy, stat_cnt);
+
+ for (i = 0; i < n; i++) {
+ if (ids[i] >= stat_cnt) {
+ DPAA2_PMD_ERR("xstats id value isn't valid");
+ return -1;
+ }
+ values[i] = values_copy[ids[i]];
+ }
+ return n;
+}
+
+static int
+dpaa2_xstats_get_names_by_id(
+ struct rte_eth_dev *dev,
+ const uint64_t *ids,
+ struct rte_eth_xstat_name *xstats_names,
+ unsigned int limit)
+{
+ unsigned int i, stat_cnt = RTE_DIM(dpaa2_xstats_strings);
+ struct rte_eth_xstat_name xstats_names_copy[stat_cnt];
+
+ if (!ids)
+ return dpaa2_xstats_get_names(dev, xstats_names, limit);
+
+ dpaa2_xstats_get_names(dev, xstats_names_copy, limit);
+
+ for (i = 0; i < limit; i++) {
+ if (ids[i] >= stat_cnt) {
+ DPAA2_PMD_ERR("xstats id value isn't valid");
+ return -1;
+ }
+ strcpy(xstats_names[i].name, xstats_names_copy[ids[i]].name);
+ }
+ return limit;
+}
+
+static int
+dpaa2_dev_stats_reset(struct rte_eth_dev *dev)
+{
+ struct dpaa2_dev_priv *priv = dev->data->dev_private;
+ struct fsl_mc_io *dpni = (struct fsl_mc_io *)dev->process_private;
+ int retcode;
+ int i;
+ struct dpaa2_queue *dpaa2_q;
+
+ PMD_INIT_FUNC_TRACE();
+
+ if (dpni == NULL) {
+ DPAA2_PMD_ERR("dpni is NULL");
+ return -EINVAL;
+ }
+
+ retcode = dpni_reset_statistics(dpni, CMD_PRI_LOW, priv->token);
+ if (retcode)
+ goto error;
+
+ /* Reset the per queue stats in dpaa2_queue structure */
+ for (i = 0; i < priv->nb_rx_queues; i++) {
+ dpaa2_q = (struct dpaa2_queue *)priv->rx_vq[i];
+ if (dpaa2_q)
+ dpaa2_q->rx_pkts = 0;
+ }
+
+ for (i = 0; i < priv->nb_tx_queues; i++) {
+ dpaa2_q = (struct dpaa2_queue *)priv->tx_vq[i];
+ if (dpaa2_q)
+ dpaa2_q->tx_pkts = 0;
+ }
+
+ return 0;
+
+error:
+ DPAA2_PMD_ERR("Operation not completed:Error Code = %d", retcode);
+ return retcode;
+};
+
+/* return 0 means link status changed, -1 means not changed */
+static int
+dpaa2_dev_link_update(struct rte_eth_dev *dev,
+ int wait_to_complete)
+{
+ int ret;
+ struct dpaa2_dev_priv *priv = dev->data->dev_private;
+ struct fsl_mc_io *dpni = (struct fsl_mc_io *)dev->process_private;
+ struct rte_eth_link link;
+ struct dpni_link_state state = {0};
+ uint8_t count;
+
+ if (dpni == NULL) {
+ DPAA2_PMD_ERR("dpni is NULL");
+ return 0;
+ }
+
+ for (count = 0; count <= MAX_REPEAT_TIME; count++) {
+ ret = dpni_get_link_state(dpni, CMD_PRI_LOW, priv->token,
+ &state);
+ if (ret < 0) {
+ DPAA2_PMD_DEBUG("error: dpni_get_link_state %d", ret);
+ return -1;
+ }
+ if (state.up == RTE_ETH_LINK_DOWN &&
+ wait_to_complete)
+ rte_delay_ms(CHECK_INTERVAL);
+ else
+ break;
+ }
+
+ memset(&link, 0, sizeof(struct rte_eth_link));
+ link.link_status = state.up;
+ link.link_speed = state.rate;
+
+ if (state.options & DPNI_LINK_OPT_HALF_DUPLEX)
+ link.link_duplex = RTE_ETH_LINK_HALF_DUPLEX;
+ else
+ link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+
+ ret = rte_eth_linkstatus_set(dev, &link);
+ if (ret == -1)
+ DPAA2_PMD_DEBUG("No change in status");
+ else
+ DPAA2_PMD_INFO("Port %d Link is %s\n", dev->data->port_id,
+ link.link_status ? "Up" : "Down");
+
+ return ret;
+}
+
+/**
+ * Toggle the DPNI to enable, if not already enabled.
+ * This is not strictly PHY up/down - it is more of logical toggling.
+ */
+static int
+dpaa2_dev_set_link_up(struct rte_eth_dev *dev)
+{
+ int ret = -EINVAL;
+ struct dpaa2_dev_priv *priv;
+ struct fsl_mc_io *dpni;
+ int en = 0;
+ struct dpni_link_state state = {0};
+
+ priv = dev->data->dev_private;
+ dpni = (struct fsl_mc_io *)dev->process_private;
+
+ if (dpni == NULL) {
+ DPAA2_PMD_ERR("dpni is NULL");
+ return ret;
+ }
+
+ /* Check if DPNI is currently enabled */
+ ret = dpni_is_enabled(dpni, CMD_PRI_LOW, priv->token, &en);
+ if (ret) {
+ /* Unable to obtain dpni status; Not continuing */
+ DPAA2_PMD_ERR("Interface Link UP failed (%d)", ret);
+ return -EINVAL;
+ }
+
+ /* Enable link if not already enabled */
+ if (!en) {
+ ret = dpni_enable(dpni, CMD_PRI_LOW, priv->token);
+ if (ret) {
+ DPAA2_PMD_ERR("Interface Link UP failed (%d)", ret);
+ return -EINVAL;
+ }
+ }
+ ret = dpni_get_link_state(dpni, CMD_PRI_LOW, priv->token, &state);
+ if (ret < 0) {
+ DPAA2_PMD_DEBUG("Unable to get link state (%d)", ret);
+ return -1;
+ }
+
+ /* changing tx burst function to start enqueues */
+ dev->tx_pkt_burst = dpaa2_dev_tx;
+ dev->data->dev_link.link_status = state.up;
+ dev->data->dev_link.link_speed = state.rate;
+
+ if (state.up)
+ DPAA2_PMD_INFO("Port %d Link is Up", dev->data->port_id);
+ else
+ DPAA2_PMD_INFO("Port %d Link is Down", dev->data->port_id);
+ return ret;
+}
+
+/**
+ * Toggle the DPNI to disable, if not already disabled.
+ * This is not strictly PHY up/down - it is more of logical toggling.
+ */
+static int
+dpaa2_dev_set_link_down(struct rte_eth_dev *dev)
+{
+ int ret = -EINVAL;
+ struct dpaa2_dev_priv *priv;
+ struct fsl_mc_io *dpni;
+ int dpni_enabled = 0;
+ int retries = 10;
+
+ PMD_INIT_FUNC_TRACE();
+
+ priv = dev->data->dev_private;
+ dpni = (struct fsl_mc_io *)dev->process_private;
+
+ if (dpni == NULL) {
+ DPAA2_PMD_ERR("Device has not yet been configured");
+ return ret;
+ }
+
+ /*changing tx burst function to avoid any more enqueues */
+ dev->tx_pkt_burst = rte_eth_pkt_burst_dummy;
+
+ /* Loop while dpni_disable() attempts to drain the egress FQs
+ * and confirm them back to us.
+ */
+ do {
+ ret = dpni_disable(dpni, 0, priv->token);
+ if (ret) {
+ DPAA2_PMD_ERR("dpni disable failed (%d)", ret);
+ return ret;
+ }
+ ret = dpni_is_enabled(dpni, 0, priv->token, &dpni_enabled);
+ if (ret) {
+ DPAA2_PMD_ERR("dpni enable check failed (%d)", ret);
+ return ret;
+ }
+ if (dpni_enabled)
+ /* Allow the MC some slack */
+ rte_delay_us(100 * 1000);
+ } while (dpni_enabled && --retries);
+
+ if (!retries) {
+ DPAA2_PMD_WARN("Retry count exceeded disabling dpni");
+ /* todo- we may have to manually cleanup queues.
+ */
+ } else {
+ DPAA2_PMD_INFO("Port %d Link DOWN successful",
+ dev->data->port_id);
+ }
+
+ dev->data->dev_link.link_status = 0;
+
+ return ret;
+}
+
+static int
+dpaa2_flow_ctrl_get(struct rte_eth_dev *dev, struct rte_eth_fc_conf *fc_conf)
+{
+ int ret = -EINVAL;
+ struct dpaa2_dev_priv *priv;
+ struct fsl_mc_io *dpni;
+ struct dpni_link_state state = {0};
+
+ PMD_INIT_FUNC_TRACE();
+
+ priv = dev->data->dev_private;
+ dpni = (struct fsl_mc_io *)dev->process_private;
+
+ if (dpni == NULL || fc_conf == NULL) {
+ DPAA2_PMD_ERR("device not configured");
+ return ret;
+ }
+
+ ret = dpni_get_link_state(dpni, CMD_PRI_LOW, priv->token, &state);
+ if (ret) {
+ DPAA2_PMD_ERR("error: dpni_get_link_state %d", ret);
+ return ret;
+ }
+
+ memset(fc_conf, 0, sizeof(struct rte_eth_fc_conf));
+ if (state.options & DPNI_LINK_OPT_PAUSE) {
+ /* DPNI_LINK_OPT_PAUSE set
+ * if ASYM_PAUSE not set,
+ * RX Side flow control (handle received Pause frame)
+ * TX side flow control (send Pause frame)
+ * if ASYM_PAUSE set,
+ * RX Side flow control (handle received Pause frame)
+ * No TX side flow control (send Pause frame disabled)
+ */
+ if (!(state.options & DPNI_LINK_OPT_ASYM_PAUSE))
+ fc_conf->mode = RTE_ETH_FC_FULL;
+ else
+ fc_conf->mode = RTE_ETH_FC_RX_PAUSE;
+ } else {
+ /* DPNI_LINK_OPT_PAUSE not set
+ * if ASYM_PAUSE set,
+ * TX side flow control (send Pause frame)
+ * No RX side flow control (No action on pause frame rx)
+ * if ASYM_PAUSE not set,
+ * Flow control disabled
+ */
+ if (state.options & DPNI_LINK_OPT_ASYM_PAUSE)
+ fc_conf->mode = RTE_ETH_FC_TX_PAUSE;
+ else
+ fc_conf->mode = RTE_ETH_FC_NONE;
+ }
+
+ return ret;
+}
+
+static int
+dpaa2_flow_ctrl_set(struct rte_eth_dev *dev, struct rte_eth_fc_conf *fc_conf)
+{
+ int ret = -EINVAL;
+ struct dpaa2_dev_priv *priv;
+ struct fsl_mc_io *dpni;
+ struct dpni_link_state state = {0};
+ struct dpni_link_cfg cfg = {0};
+
+ PMD_INIT_FUNC_TRACE();
+
+ priv = dev->data->dev_private;
+ dpni = (struct fsl_mc_io *)dev->process_private;
+
+ if (dpni == NULL) {
+ DPAA2_PMD_ERR("dpni is NULL");
+ return ret;
+ }
+
+ /* It is necessary to obtain the current state before setting fc_conf
+ * as MC would return error in case rate, autoneg or duplex values are
+ * different.
+ */
+ ret = dpni_get_link_state(dpni, CMD_PRI_LOW, priv->token, &state);
+ if (ret) {
+ DPAA2_PMD_ERR("Unable to get link state (err=%d)", ret);
+ return -1;
+ }
+
+ /* Disable link before setting configuration */
+ dpaa2_dev_set_link_down(dev);
+
+ /* Based on fc_conf, update cfg */
+ cfg.rate = state.rate;
+ cfg.options = state.options;
+
+ /* update cfg with fc_conf */
+ switch (fc_conf->mode) {
+ case RTE_ETH_FC_FULL:
+ /* Full flow control;
+ * OPT_PAUSE set, ASYM_PAUSE not set
+ */
+ cfg.options |= DPNI_LINK_OPT_PAUSE;
+ cfg.options &= ~DPNI_LINK_OPT_ASYM_PAUSE;
+ break;
+ case RTE_ETH_FC_TX_PAUSE:
+ /* Enable RX flow control
+ * OPT_PAUSE not set;
+ * ASYM_PAUSE set;
+ */
+ cfg.options |= DPNI_LINK_OPT_ASYM_PAUSE;
+ cfg.options &= ~DPNI_LINK_OPT_PAUSE;
+ break;
+ case RTE_ETH_FC_RX_PAUSE:
+ /* Enable TX Flow control
+ * OPT_PAUSE set
+ * ASYM_PAUSE set
+ */
+ cfg.options |= DPNI_LINK_OPT_PAUSE;
+ cfg.options |= DPNI_LINK_OPT_ASYM_PAUSE;
+ break;
+ case RTE_ETH_FC_NONE:
+ /* Disable Flow control
+ * OPT_PAUSE not set
+ * ASYM_PAUSE not set
+ */
+ cfg.options &= ~DPNI_LINK_OPT_PAUSE;
+ cfg.options &= ~DPNI_LINK_OPT_ASYM_PAUSE;
+ break;
+ default:
+ DPAA2_PMD_ERR("Incorrect Flow control flag (%d)",
+ fc_conf->mode);
+ return -1;
+ }
+
+ ret = dpni_set_link_cfg(dpni, CMD_PRI_LOW, priv->token, &cfg);
+ if (ret)
+ DPAA2_PMD_ERR("Unable to set Link configuration (err=%d)",
+ ret);
+
+ /* Enable link */
+ dpaa2_dev_set_link_up(dev);
+
+ return ret;
+}
+
+static int
+dpaa2_dev_rss_hash_update(struct rte_eth_dev *dev,
+ struct rte_eth_rss_conf *rss_conf)
+{
+ struct rte_eth_dev_data *data = dev->data;
+ struct dpaa2_dev_priv *priv = data->dev_private;
+ struct rte_eth_conf *eth_conf = &data->dev_conf;
+ int ret, tc_index;
+
+ PMD_INIT_FUNC_TRACE();
+
+ if (rss_conf->rss_hf) {
+ for (tc_index = 0; tc_index < priv->num_rx_tc; tc_index++) {
+ ret = dpaa2_setup_flow_dist(dev, rss_conf->rss_hf,
+ tc_index);
+ if (ret) {
+ DPAA2_PMD_ERR("Unable to set flow dist on tc%d",
+ tc_index);
+ return ret;
+ }
+ }
+ } else {
+ for (tc_index = 0; tc_index < priv->num_rx_tc; tc_index++) {
+ ret = dpaa2_remove_flow_dist(dev, tc_index);
+ if (ret) {
+ DPAA2_PMD_ERR(
+ "Unable to remove flow dist on tc%d",
+ tc_index);
+ return ret;
+ }
+ }
+ }
+ eth_conf->rx_adv_conf.rss_conf.rss_hf = rss_conf->rss_hf;
+ return 0;
+}
+
+static int
+dpaa2_dev_rss_hash_conf_get(struct rte_eth_dev *dev,
+ struct rte_eth_rss_conf *rss_conf)
+{
+ struct rte_eth_dev_data *data = dev->data;
+ struct rte_eth_conf *eth_conf = &data->dev_conf;
+
+ /* dpaa2 does not support rss_key, so length should be 0*/
+ rss_conf->rss_key_len = 0;
+ rss_conf->rss_hf = eth_conf->rx_adv_conf.rss_conf.rss_hf;
+ return 0;
+}
+
+int dpaa2_eth_eventq_attach(const struct rte_eth_dev *dev,
+ int eth_rx_queue_id,
+ struct dpaa2_dpcon_dev *dpcon,
+ const struct rte_event_eth_rx_adapter_queue_conf *queue_conf)
+{
+ struct dpaa2_dev_priv *eth_priv = dev->data->dev_private;
+ struct fsl_mc_io *dpni = (struct fsl_mc_io *)dev->process_private;
+ struct dpaa2_queue *dpaa2_ethq = eth_priv->rx_vq[eth_rx_queue_id];
+ uint8_t flow_id = dpaa2_ethq->flow_id;
+ struct dpni_queue cfg;
+ uint8_t options, priority;
+ int ret;