net/octeontx2: add remaining PTP operations
[dpdk.git] / drivers / net / octeontx2 / otx2_ethdev.c
index 92f008b..834b052 100644 (file)
@@ -47,6 +47,7 @@ nix_get_tx_offload_capa(struct otx2_eth_dev *dev)
 
 static const struct otx2_dev_ops otx2_dev_ops = {
        .link_status_update = otx2_eth_dev_link_status_update,
+       .ptp_info_update = otx2_eth_dev_ptp_info_update
 };
 
 static int
@@ -120,6 +121,32 @@ nix_lf_free(struct otx2_eth_dev *dev)
        return otx2_mbox_process(mbox);
 }
 
+int
+otx2_cgx_rxtx_start(struct otx2_eth_dev *dev)
+{
+       struct otx2_mbox *mbox = dev->mbox;
+
+       if (otx2_dev_is_vf(dev))
+               return 0;
+
+       otx2_mbox_alloc_msg_cgx_start_rxtx(mbox);
+
+       return otx2_mbox_process(mbox);
+}
+
+int
+otx2_cgx_rxtx_stop(struct otx2_eth_dev *dev)
+{
+       struct otx2_mbox *mbox = dev->mbox;
+
+       if (otx2_dev_is_vf(dev))
+               return 0;
+
+       otx2_mbox_alloc_msg_cgx_stop_rxtx(mbox);
+
+       return otx2_mbox_process(mbox);
+}
+
 static inline void
 nix_rx_queue_reset(struct otx2_eth_rxq *rxq)
 {
@@ -190,6 +217,14 @@ nix_cq_rq_init(struct rte_eth_dev *eth_dev, struct otx2_eth_dev *dev,
        aq->cq.cq_err_int_ena = BIT(NIX_CQERRINT_CQE_FAULT);
        aq->cq.cq_err_int_ena |= BIT(NIX_CQERRINT_DOOR_ERR);
 
+       /* TX pause frames enable flowctrl on RX side */
+       if (dev->fc_info.tx_pause) {
+               /* Single bpid is allocated for all rx channels for now */
+               aq->cq.bpid = dev->fc_info.bpid[0];
+               aq->cq.bp = NIX_CQ_BP_LEVEL;
+               aq->cq.bp_ena = 1;
+       }
+
        /* Many to one reduction */
        aq->cq.qint_idx = qid % dev->qints;
 
@@ -252,6 +287,26 @@ fail:
        return rc;
 }
 
+static int
+nix_rq_enb_dis(struct rte_eth_dev *eth_dev,
+              struct otx2_eth_rxq *rxq, const bool enb)
+{
+       struct otx2_eth_dev *dev = otx2_eth_pmd_priv(eth_dev);
+       struct otx2_mbox *mbox = dev->mbox;
+       struct nix_aq_enq_req *aq;
+
+       /* Pkts will be dropped silently if RQ is disabled */
+       aq = otx2_mbox_alloc_msg_nix_aq_enq(mbox);
+       aq->qidx = rxq->rq;
+       aq->ctype = NIX_AQ_CTYPE_RQ;
+       aq->op = NIX_AQ_INSTOP_WRITE;
+
+       aq->rq.ena = enb;
+       aq->rq_mask.ena = ~(aq->rq_mask.ena);
+
+       return otx2_mbox_process(mbox);
+}
+
 static int
 nix_cq_rq_uninit(struct rte_eth_dev *eth_dev, struct otx2_eth_rxq *rxq)
 {
@@ -282,9 +337,7 @@ nix_cq_rq_uninit(struct rte_eth_dev *eth_dev, struct otx2_eth_rxq *rxq)
 static inline int
 nix_get_data_off(struct otx2_eth_dev *dev)
 {
-       RTE_SET_USED(dev);
-
-       return 0;
+       return otx2_ethdev_is_ptp_en(dev) ? NIX_TIMESYNC_RX_OFFSET : 0;
 }
 
 uint64_t
@@ -395,6 +448,8 @@ otx2_nix_rx_queue_setup(struct rte_eth_dev *eth_dev, uint16_t rq,
        rxq->pool = mp;
        rxq->qlen = nix_qsize_to_val(qsize);
        rxq->qsize = qsize;
+       rxq->lookup_mem = otx2_nix_fastpath_lookup_mem_get();
+       rxq->tstamp = &dev->tstamp;
 
        /* Alloc completion queue */
        rc = nix_cq_rq_init(eth_dev, dev, rq, rxq, mp);
@@ -441,16 +496,27 @@ nix_sq_init(struct otx2_eth_txq *txq)
        struct otx2_eth_dev *dev = txq->dev;
        struct otx2_mbox *mbox = dev->mbox;
        struct nix_aq_enq_req *sq;
+       uint32_t rr_quantum;
+       uint16_t smq;
+       int rc;
 
        if (txq->sqb_pool->pool_id == 0)
                return -EINVAL;
 
+       rc = otx2_nix_tm_get_leaf_data(dev, txq->sq, &rr_quantum, &smq);
+       if (rc) {
+               otx2_err("Failed to get sq->smq(leaf node), rc=%d", rc);
+               return rc;
+       }
+
        sq = otx2_mbox_alloc_msg_nix_aq_enq(mbox);
        sq->qidx = txq->sq;
        sq->ctype = NIX_AQ_CTYPE_SQ;
        sq->op = NIX_AQ_INSTOP_INIT;
        sq->sq.max_sqe_size = nix_sq_max_sqe_sz(txq);
 
+       sq->sq.smq = smq;
+       sq->sq.smq_rr_quantum = rr_quantum;
        sq->sq.default_chan = dev->tx_chan_base;
        sq->sq.sqe_stype = NIX_STYPE_STF;
        sq->sq.ena = 1;
@@ -670,6 +736,7 @@ otx2_nix_form_default_desc(struct otx2_eth_txq *txq)
                        send_mem->dsz = 0x0;
                        send_mem->wmem = 0x1;
                        send_mem->alg = NIX_SENDMEMALG_SETTSTMP;
+                       send_mem->addr = txq->dev->tstamp.tx_tstamp_iova;
                }
                sg = (union nix_send_sg_s *)&txq->cmd[4];
        } else {
@@ -691,12 +758,18 @@ static void
 otx2_nix_tx_queue_release(void *_txq)
 {
        struct otx2_eth_txq *txq = _txq;
+       struct rte_eth_dev *eth_dev;
 
        if (!txq)
                return;
 
+       eth_dev = txq->dev->eth_dev;
+
        otx2_nix_dbg("Releasing txq %u", txq->sq);
 
+       /* Flush and disable tm */
+       otx2_nix_tm_sw_xoff(txq, eth_dev->data->dev_started);
+
        /* Free sqb's and disable sq */
        nix_sq_uninit(txq);
 
@@ -807,6 +880,172 @@ fail:
        return rc;
 }
 
+static int
+nix_store_queue_cfg_and_then_release(struct rte_eth_dev *eth_dev)
+{
+       struct otx2_eth_dev *dev = otx2_eth_pmd_priv(eth_dev);
+       struct otx2_eth_qconf *tx_qconf = NULL;
+       struct otx2_eth_qconf *rx_qconf = NULL;
+       struct otx2_eth_txq **txq;
+       struct otx2_eth_rxq **rxq;
+       int i, nb_rxq, nb_txq;
+
+       nb_rxq = RTE_MIN(dev->configured_nb_rx_qs, eth_dev->data->nb_rx_queues);
+       nb_txq = RTE_MIN(dev->configured_nb_tx_qs, eth_dev->data->nb_tx_queues);
+
+       tx_qconf = malloc(nb_txq * sizeof(*tx_qconf));
+       if (tx_qconf == NULL) {
+               otx2_err("Failed to allocate memory for tx_qconf");
+               goto fail;
+       }
+
+       rx_qconf = malloc(nb_rxq * sizeof(*rx_qconf));
+       if (rx_qconf == NULL) {
+               otx2_err("Failed to allocate memory for rx_qconf");
+               goto fail;
+       }
+
+       txq = (struct otx2_eth_txq **)eth_dev->data->tx_queues;
+       for (i = 0; i < nb_txq; i++) {
+               if (txq[i] == NULL) {
+                       otx2_err("txq[%d] is already released", i);
+                       goto fail;
+               }
+               memcpy(&tx_qconf[i], &txq[i]->qconf, sizeof(*tx_qconf));
+               otx2_nix_tx_queue_release(txq[i]);
+               eth_dev->data->tx_queues[i] = NULL;
+       }
+
+       rxq = (struct otx2_eth_rxq **)eth_dev->data->rx_queues;
+       for (i = 0; i < nb_rxq; i++) {
+               if (rxq[i] == NULL) {
+                       otx2_err("rxq[%d] is already released", i);
+                       goto fail;
+               }
+               memcpy(&rx_qconf[i], &rxq[i]->qconf, sizeof(*rx_qconf));
+               otx2_nix_rx_queue_release(rxq[i]);
+               eth_dev->data->rx_queues[i] = NULL;
+       }
+
+       dev->tx_qconf = tx_qconf;
+       dev->rx_qconf = rx_qconf;
+       return 0;
+
+fail:
+       if (tx_qconf)
+               free(tx_qconf);
+       if (rx_qconf)
+               free(rx_qconf);
+
+       return -ENOMEM;
+}
+
+static int
+nix_restore_queue_cfg(struct rte_eth_dev *eth_dev)
+{
+       struct otx2_eth_dev *dev = otx2_eth_pmd_priv(eth_dev);
+       struct otx2_eth_qconf *tx_qconf = dev->tx_qconf;
+       struct otx2_eth_qconf *rx_qconf = dev->rx_qconf;
+       struct otx2_eth_txq **txq;
+       struct otx2_eth_rxq **rxq;
+       int rc, i, nb_rxq, nb_txq;
+
+       nb_rxq = RTE_MIN(dev->configured_nb_rx_qs, eth_dev->data->nb_rx_queues);
+       nb_txq = RTE_MIN(dev->configured_nb_tx_qs, eth_dev->data->nb_tx_queues);
+
+       rc = -ENOMEM;
+       /* Setup tx & rx queues with previous configuration so
+        * that the queues can be functional in cases like ports
+        * are started without re configuring queues.
+        *
+        * Usual re config sequence is like below:
+        * port_configure() {
+        *      if(reconfigure) {
+        *              queue_release()
+        *              queue_setup()
+        *      }
+        *      queue_configure() {
+        *              queue_release()
+        *              queue_setup()
+        *      }
+        * }
+        * port_start()
+        *
+        * In some application's control path, queue_configure() would
+        * NOT be invoked for TXQs/RXQs in port_configure().
+        * In such cases, queues can be functional after start as the
+        * queues are already setup in port_configure().
+        */
+       for (i = 0; i < nb_txq; i++) {
+               rc = otx2_nix_tx_queue_setup(eth_dev, i, tx_qconf[i].nb_desc,
+                                            tx_qconf[i].socket_id,
+                                            &tx_qconf[i].conf.tx);
+               if (rc) {
+                       otx2_err("Failed to setup tx queue rc=%d", rc);
+                       txq = (struct otx2_eth_txq **)eth_dev->data->tx_queues;
+                       for (i -= 1; i >= 0; i--)
+                               otx2_nix_tx_queue_release(txq[i]);
+                       goto fail;
+               }
+       }
+
+       free(tx_qconf); tx_qconf = NULL;
+
+       for (i = 0; i < nb_rxq; i++) {
+               rc = otx2_nix_rx_queue_setup(eth_dev, i, rx_qconf[i].nb_desc,
+                                            rx_qconf[i].socket_id,
+                                            &rx_qconf[i].conf.rx,
+                                            rx_qconf[i].mempool);
+               if (rc) {
+                       otx2_err("Failed to setup rx queue rc=%d", rc);
+                       rxq = (struct otx2_eth_rxq **)eth_dev->data->rx_queues;
+                       for (i -= 1; i >= 0; i--)
+                               otx2_nix_rx_queue_release(rxq[i]);
+                       goto release_tx_queues;
+               }
+       }
+
+       free(rx_qconf); rx_qconf = NULL;
+
+       return 0;
+
+release_tx_queues:
+       txq = (struct otx2_eth_txq **)eth_dev->data->tx_queues;
+       for (i = 0; i < eth_dev->data->nb_tx_queues; i++)
+               otx2_nix_tx_queue_release(txq[i]);
+fail:
+       if (tx_qconf)
+               free(tx_qconf);
+       if (rx_qconf)
+               free(rx_qconf);
+
+       return rc;
+}
+
+static uint16_t
+nix_eth_nop_burst(void *queue, struct rte_mbuf **mbufs, uint16_t pkts)
+{
+       RTE_SET_USED(queue);
+       RTE_SET_USED(mbufs);
+       RTE_SET_USED(pkts);
+
+       return 0;
+}
+
+static void
+nix_set_nop_rxtx_function(struct rte_eth_dev *eth_dev)
+{
+       /* These dummy functions are required for supporting
+        * some applications which reconfigure queues without
+        * stopping tx burst and rx burst threads(eg kni app)
+        * When the queues context is saved, txq/rxqs are released
+        * which caused app crash since rx/tx burst is still
+        * on different lcores
+        */
+       eth_dev->tx_pkt_burst = nix_eth_nop_burst;
+       eth_dev->rx_pkt_burst = nix_eth_nop_burst;
+       rte_mb();
+}
 
 static int
 otx2_nix_configure(struct rte_eth_dev *eth_dev)
@@ -862,7 +1101,13 @@ otx2_nix_configure(struct rte_eth_dev *eth_dev)
 
        /* Free the resources allocated from the previous configure */
        if (dev->configured == 1) {
+               otx2_nix_rxchan_bpid_cfg(eth_dev, false);
                oxt2_nix_unregister_queue_irqs(eth_dev);
+               nix_set_nop_rxtx_function(eth_dev);
+               rc = nix_store_queue_cfg_and_then_release(eth_dev);
+               if (rc)
+                       goto fail;
+               otx2_nix_tm_fini(eth_dev);
                nix_lf_free(dev);
        }
 
@@ -896,6 +1141,13 @@ otx2_nix_configure(struct rte_eth_dev *eth_dev)
                goto free_nix_lf;
        }
 
+       /* Init the default TM scheduler hierarchy */
+       rc = otx2_nix_tm_init_default(eth_dev);
+       if (rc) {
+               otx2_err("Failed to init traffic manager rc=%d", rc);
+               goto free_nix_lf;
+       }
+
        /* Register queue IRQs */
        rc = oxt2_nix_register_queue_irqs(eth_dev);
        if (rc) {
@@ -903,6 +1155,32 @@ otx2_nix_configure(struct rte_eth_dev *eth_dev)
                goto free_nix_lf;
        }
 
+       rc = otx2_nix_rxchan_bpid_cfg(eth_dev, true);
+       if (rc) {
+               otx2_err("Failed to configure nix rx chan bpid cfg rc=%d", rc);
+               goto free_nix_lf;
+       }
+
+       /* Enable PTP if it was requested by the app or if it is already
+        * enabled in PF owning this VF
+        */
+       memset(&dev->tstamp, 0, sizeof(struct otx2_timesync_info));
+       if ((dev->rx_offloads & DEV_RX_OFFLOAD_TIMESTAMP) ||
+           otx2_ethdev_is_ptp_en(dev))
+               otx2_nix_timesync_enable(eth_dev);
+       else
+               otx2_nix_timesync_disable(eth_dev);
+
+       /*
+        * Restore queue config when reconfigure followed by
+        * reconfigure and no queue configure invoked from application case.
+        */
+       if (dev->configured == 1) {
+               rc = nix_restore_queue_cfg(eth_dev);
+               if (rc)
+                       goto free_nix_lf;
+       }
+
        /* Update the mac address */
        ea = eth_dev->data->mac_addrs;
        memcpy(ea, dev->mac_addr, RTE_ETHER_ADDR_LEN);
@@ -930,6 +1208,102 @@ fail:
        return rc;
 }
 
+int
+otx2_nix_tx_queue_start(struct rte_eth_dev *eth_dev, uint16_t qidx)
+{
+       struct rte_eth_dev_data *data = eth_dev->data;
+       struct otx2_eth_txq *txq;
+       int rc = -EINVAL;
+
+       txq = eth_dev->data->tx_queues[qidx];
+
+       if (data->tx_queue_state[qidx] == RTE_ETH_QUEUE_STATE_STARTED)
+               return 0;
+
+       rc = otx2_nix_sq_sqb_aura_fc(txq, true);
+       if (rc) {
+               otx2_err("Failed to enable sqb aura fc, txq=%u, rc=%d",
+                        qidx, rc);
+               goto done;
+       }
+
+       data->tx_queue_state[qidx] = RTE_ETH_QUEUE_STATE_STARTED;
+
+done:
+       return rc;
+}
+
+int
+otx2_nix_tx_queue_stop(struct rte_eth_dev *eth_dev, uint16_t qidx)
+{
+       struct rte_eth_dev_data *data = eth_dev->data;
+       struct otx2_eth_txq *txq;
+       int rc;
+
+       txq = eth_dev->data->tx_queues[qidx];
+
+       if (data->tx_queue_state[qidx] == RTE_ETH_QUEUE_STATE_STOPPED)
+               return 0;
+
+       txq->fc_cache_pkts = 0;
+
+       rc = otx2_nix_sq_sqb_aura_fc(txq, false);
+       if (rc) {
+               otx2_err("Failed to disable sqb aura fc, txq=%u, rc=%d",
+                        qidx, rc);
+               goto done;
+       }
+
+       data->tx_queue_state[qidx] = RTE_ETH_QUEUE_STATE_STOPPED;
+
+done:
+       return rc;
+}
+
+static int
+otx2_nix_rx_queue_start(struct rte_eth_dev *eth_dev, uint16_t qidx)
+{
+       struct otx2_eth_rxq *rxq = eth_dev->data->rx_queues[qidx];
+       struct rte_eth_dev_data *data = eth_dev->data;
+       int rc;
+
+       if (data->rx_queue_state[qidx] == RTE_ETH_QUEUE_STATE_STARTED)
+               return 0;
+
+       rc = nix_rq_enb_dis(rxq->eth_dev, rxq, true);
+       if (rc) {
+               otx2_err("Failed to enable rxq=%u, rc=%d", qidx, rc);
+               goto done;
+       }
+
+       data->rx_queue_state[qidx] = RTE_ETH_QUEUE_STATE_STARTED;
+
+done:
+       return rc;
+}
+
+static int
+otx2_nix_rx_queue_stop(struct rte_eth_dev *eth_dev, uint16_t qidx)
+{
+       struct otx2_eth_rxq *rxq = eth_dev->data->rx_queues[qidx];
+       struct rte_eth_dev_data *data = eth_dev->data;
+       int rc;
+
+       if (data->rx_queue_state[qidx] == RTE_ETH_QUEUE_STATE_STOPPED)
+               return 0;
+
+       rc = nix_rq_enb_dis(rxq->eth_dev, rxq, false);
+       if (rc) {
+               otx2_err("Failed to disable rxq=%u, rc=%d", qidx, rc);
+               goto done;
+       }
+
+       data->rx_queue_state[qidx] = RTE_ETH_QUEUE_STATE_STOPPED;
+
+done:
+       return rc;
+}
+
 /* Initialize and register driver with DPDK Application */
 static const struct eth_dev_ops otx2_eth_dev_ops = {
        .dev_infos_get            = otx2_nix_info_get,
@@ -939,6 +1313,11 @@ static const struct eth_dev_ops otx2_eth_dev_ops = {
        .tx_queue_release         = otx2_nix_tx_queue_release,
        .rx_queue_setup           = otx2_nix_rx_queue_setup,
        .rx_queue_release         = otx2_nix_rx_queue_release,
+       .tx_queue_start           = otx2_nix_tx_queue_start,
+       .tx_queue_stop            = otx2_nix_tx_queue_stop,
+       .rx_queue_start           = otx2_nix_rx_queue_start,
+       .rx_queue_stop            = otx2_nix_rx_queue_stop,
+       .dev_supported_ptypes_get = otx2_nix_supported_ptypes_get,
        .stats_get                = otx2_nix_dev_stats_get,
        .stats_reset              = otx2_nix_dev_stats_reset,
        .get_reg                  = otx2_nix_dev_get_reg,
@@ -959,6 +1338,24 @@ static const struct eth_dev_ops otx2_eth_dev_ops = {
        .xstats_reset             = otx2_nix_xstats_reset,
        .xstats_get_by_id         = otx2_nix_xstats_get_by_id,
        .xstats_get_names_by_id   = otx2_nix_xstats_get_names_by_id,
+       .rxq_info_get             = otx2_nix_rxq_info_get,
+       .txq_info_get             = otx2_nix_txq_info_get,
+       .rx_queue_count           = otx2_nix_rx_queue_count,
+       .rx_descriptor_done       = otx2_nix_rx_descriptor_done,
+       .rx_descriptor_status     = otx2_nix_rx_descriptor_status,
+       .tx_done_cleanup          = otx2_nix_tx_done_cleanup,
+       .pool_ops_supported       = otx2_nix_pool_ops_supported,
+       .get_module_info          = otx2_nix_get_module_info,
+       .get_module_eeprom        = otx2_nix_get_module_eeprom,
+       .flow_ctrl_get            = otx2_nix_flow_ctrl_get,
+       .flow_ctrl_set            = otx2_nix_flow_ctrl_set,
+       .timesync_enable          = otx2_nix_timesync_enable,
+       .timesync_disable         = otx2_nix_timesync_disable,
+       .timesync_read_rx_timestamp = otx2_nix_timesync_read_rx_timestamp,
+       .timesync_read_tx_timestamp = otx2_nix_timesync_read_tx_timestamp,
+       .timesync_adjust_time     = otx2_nix_timesync_adjust_time,
+       .timesync_read_time       = otx2_nix_timesync_read_time,
+       .timesync_write_time      = otx2_nix_timesync_write_time,
 };
 
 static inline int
@@ -1116,6 +1513,9 @@ otx2_eth_dev_init(struct rte_eth_dev *eth_dev)
        /* Also sync same MAC address to CGX table */
        otx2_cgx_mac_addr_set(eth_dev, &eth_dev->data->mac_addrs[0]);
 
+       /* Initialize the tm data structures */
+       otx2_nix_tm_conf_init(eth_dev);
+
        dev->tx_offload_capa = nix_get_tx_offload_capa(dev);
        dev->rx_offload_capa = nix_get_rx_offload_capa(dev);
 
@@ -1157,6 +1557,13 @@ otx2_eth_dev_uninit(struct rte_eth_dev *eth_dev, bool mbox_close)
        if (rte_eal_process_type() != RTE_PROC_PRIMARY)
                return 0;
 
+       /* Disable nix bpid config */
+       otx2_nix_rxchan_bpid_cfg(eth_dev, false);
+
+       /* Disable PTP if already enabled */
+       if (otx2_ethdev_is_ptp_en(dev))
+               otx2_nix_timesync_disable(eth_dev);
+
        /* Free up SQs */
        for (i = 0; i < eth_dev->data->nb_tx_queues; i++) {
                otx2_nix_tx_queue_release(eth_dev->data->tx_queues[i]);
@@ -1171,6 +1578,11 @@ otx2_eth_dev_uninit(struct rte_eth_dev *eth_dev, bool mbox_close)
        }
        eth_dev->data->nb_rx_queues = 0;
 
+       /* Free tm resources */
+       rc = otx2_nix_tm_fini(eth_dev);
+       if (rc)
+               otx2_err("Failed to cleanup tm, rc=%d", rc);
+
        /* Unregister queue irqs */
        oxt2_nix_unregister_queue_irqs(eth_dev);