To configure tim with external clock, requested resolution should be
adjusted based on core and external clock frequency for accuracy.

tim_ext_clk is the devarg used to pass external clock frequency.

E.g. -a 0002:0e:00.0,tim_ext_clk=122880000

Signed-off-by: Shijith Thotton <sthot...@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavat...@marvell.com>
---
Depends-on: series-18039 ("eal: add macro to swap two numbers")

 doc/guides/eventdevs/cnxk.rst       |   9 ++
 drivers/common/cnxk/roc_platform.h  |   1 +
 drivers/event/cnxk/cnxk_tim_evdev.c | 124 ++++++++++++++++++++++++++--
 drivers/event/cnxk/cnxk_tim_evdev.h |   6 +-
 4 files changed, 131 insertions(+), 9 deletions(-)

diff --git a/doc/guides/eventdevs/cnxk.rst b/doc/guides/eventdevs/cnxk.rst
index 53560d3830..238b71f81f 100644
--- a/doc/guides/eventdevs/cnxk.rst
+++ b/doc/guides/eventdevs/cnxk.rst
@@ -164,6 +164,15 @@ Runtime Config Options
 
     -a 0002:0e:00.0,tim_ring_ctl=[2-1023-1-0]
 
+- ``TIM external clock frequency``
+
+  The ``tim_ext_clk`` devagrs can be used to pass external clock frequency when
+  external clock source is selected.
+
+  For Example::
+
+    -a 0002:0e:00.0,tim_ext_clk=122880000
+
 Debugging Options
 -----------------
 
diff --git a/drivers/common/cnxk/roc_platform.h 
b/drivers/common/cnxk/roc_platform.h
index 285b24b82d..7e73972b80 100644
--- a/drivers/common/cnxk/roc_platform.h
+++ b/drivers/common/cnxk/roc_platform.h
@@ -43,6 +43,7 @@
 #define PLT_MAX                         RTE_MAX
 #define PLT_DIM                         RTE_DIM
 #define PLT_SET_USED            RTE_SET_USED
+#define PLT_SWAP                RTE_SWAP
 #define PLT_STATIC_ASSERT(s)    _Static_assert(s, #s)
 #define PLT_ALIGN               RTE_ALIGN
 #define PLT_ALIGN_MUL_CEIL      RTE_ALIGN_MUL_CEIL
diff --git a/drivers/event/cnxk/cnxk_tim_evdev.c 
b/drivers/event/cnxk/cnxk_tim_evdev.c
index 9d40e336d7..702686e68e 100644
--- a/drivers/event/cnxk/cnxk_tim_evdev.c
+++ b/drivers/event/cnxk/cnxk_tim_evdev.c
@@ -2,6 +2,8 @@
  * Copyright(C) 2021 Marvell.
  */
 
+#include <math.h>
+
 #include "cnxk_eventdev.h"
 #include "cnxk_tim_evdev.h"
 
@@ -115,12 +117,88 @@ cnxk_tim_ring_info_get(const struct 
rte_event_timer_adapter *adptr,
                   sizeof(struct rte_event_timer_adapter_conf));
 }
 
+static inline void
+sort_multi_array(double ref_arr[], uint64_t arr1[], uint64_t arr2[],
+                uint64_t arr3[], uint8_t sz)
+{
+       int x;
+
+       for (x = 0; x < sz - 1; x++) {
+               if (ref_arr[x] > ref_arr[x + 1]) {
+                       PLT_SWAP(ref_arr[x], ref_arr[x + 1]);
+                       PLT_SWAP(arr1[x], arr1[x + 1]);
+                       PLT_SWAP(arr2[x], arr2[x + 1]);
+                       PLT_SWAP(arr3[x], arr3[x + 1]);
+                       x = -1;
+               }
+       }
+}
+
+static inline void
+populate_sample(uint64_t tck[], uint64_t ns[], double diff[], uint64_t dst[],
+               uint64_t req_tck, uint64_t clk_freq, double tck_ns, uint8_t sz,
+               bool mov_fwd)
+{
+       int i;
+
+       for (i = 0; i < sz; i++) {
+               tck[i] = i ? tck[i - 1] : req_tck;
+               do {
+                       mov_fwd ? tck[i]++ : tck[i]--;
+                       ns[i] = round((double)tck[i] * tck_ns);
+                       if (round((double)tck[i] * tck_ns) >
+                           ((double)tck[i] * tck_ns))
+                               continue;
+               } while (ns[i] % cnxk_tim_min_resolution_ns(clk_freq));
+               diff[i] = PLT_MAX((double)ns[i], (double)tck[i] * tck_ns) -
+                         PLT_MIN((double)ns[i], (double)tck[i] * tck_ns);
+               dst[i] = mov_fwd ? tck[i] - req_tck : req_tck - tck[i];
+       }
+}
+
+static void
+tim_adjust_resolution(uint64_t *req_ns, uint64_t *req_tck, double tck_ns,
+                     uint64_t clk_freq, uint64_t max_tmo)
+{
+#define MAX_SAMPLES 5
+       double rmax_diff[MAX_SAMPLES], rmin_diff[MAX_SAMPLES];
+       uint64_t min_tck[MAX_SAMPLES], max_tck[MAX_SAMPLES];
+       uint64_t min_dst[MAX_SAMPLES], max_dst[MAX_SAMPLES];
+       uint64_t min_ns[MAX_SAMPLES], max_ns[MAX_SAMPLES];
+       uint64_t m_tck = cnxk_tim_min_tmo_ticks(cnxk_tim_cntfrq());
+       int i;
+
+       populate_sample(max_tck, max_ns, rmax_diff, max_dst, *req_tck, clk_freq,
+                       tck_ns, MAX_SAMPLES, true);
+       sort_multi_array(rmax_diff, max_dst, max_tck, max_ns, MAX_SAMPLES);
+
+       populate_sample(min_tck, min_ns, rmin_diff, min_dst, *req_tck, clk_freq,
+                       tck_ns, MAX_SAMPLES, false);
+       sort_multi_array(rmin_diff, min_dst, min_tck, min_ns, MAX_SAMPLES);
+
+       for (i = 0; i < MAX_SAMPLES; i++) {
+               if (min_dst[i] < max_dst[i] && min_tck[i] > m_tck &&
+                   (max_tmo / min_ns[i]) <=
+                           (TIM_MAX_BUCKET_SIZE - TIM_MIN_BUCKET_SIZE)) {
+                       *req_tck = min_tck[i];
+                       *req_ns = min_ns[i];
+                       break;
+               } else if ((max_tmo / max_ns[i]) <
+                          (TIM_MAX_BUCKET_SIZE - TIM_MIN_BUCKET_SIZE)) {
+                       *req_tck = max_tck[i];
+                       *req_ns = max_ns[i];
+                       break;
+               }
+       }
+}
+
 static int
 cnxk_tim_ring_create(struct rte_event_timer_adapter *adptr)
 {
        struct rte_event_timer_adapter_conf *rcfg = &adptr->data->conf;
        struct cnxk_tim_evdev *dev = cnxk_tim_priv_get();
        struct cnxk_tim_ring *tim_ring;
+       uint64_t interval;
        int i, rc;
 
        if (dev == NULL)
@@ -153,11 +231,35 @@ cnxk_tim_ring_create(struct rte_event_timer_adapter 
*adptr)
                        goto tim_hw_free;
                }
        }
+
        tim_ring->ring_id = adptr->data->id;
        tim_ring->clk_src = (int)rcfg->clk_src;
-       tim_ring->tck_nsec = RTE_ALIGN_MUL_CEIL(
-               rcfg->timer_tick_ns,
-               cnxk_tim_min_resolution_ns(cnxk_tim_cntfrq()));
+       if (rcfg->clk_src == RTE_EVENT_TIMER_ADAPTER_CPU_CLK) {
+               rcfg->timer_tick_ns = RTE_ALIGN_MUL_CEIL(
+                       rcfg->timer_tick_ns,
+                       cnxk_tim_min_resolution_ns(cnxk_tim_cntfrq()));
+       } else { /* External clock */
+               uint64_t req_ns, req_tck;
+               double tck_ns;
+
+               if (dev->ext_clk_frq == 0) {
+                       rc = -ENODEV;
+                       goto tim_hw_free;
+               }
+
+               req_ns = rcfg->timer_tick_ns;
+               tck_ns = NSECPERSEC / dev->ext_clk_frq;
+               req_tck = round(rcfg->timer_tick_ns / tck_ns);
+               tim_adjust_resolution(&req_ns, &req_tck, tck_ns,
+                                     cnxk_tim_cntfrq(), rcfg->max_tmo_ns);
+               if ((rcfg->timer_tick_ns != req_ns) &&
+                   !(rcfg->flags & RTE_EVENT_TIMER_ADAPTER_F_ADJUST_RES)) {
+                       rc = -ERANGE;
+                       goto tim_hw_free;
+               }
+               rcfg->timer_tick_ns = ceil(req_tck * tck_ns);
+       }
+       tim_ring->tck_nsec = rcfg->timer_tick_ns;
        tim_ring->max_tout = rcfg->max_tmo_ns;
        tim_ring->nb_bkts = (tim_ring->max_tout / tim_ring->tck_nsec);
        tim_ring->nb_timers = rcfg->nb_timers;
@@ -201,11 +303,15 @@ cnxk_tim_ring_create(struct rte_event_timer_adapter 
*adptr)
        if (rc < 0)
                goto tim_bkt_free;
 
-       rc = roc_tim_lf_config(
-               &dev->tim, tim_ring->ring_id,
-               cnxk_tim_convert_clk_src(tim_ring->clk_src), 0, 0,
-               tim_ring->nb_bkts, tim_ring->chunk_sz,
-               NSEC2TICK(tim_ring->tck_nsec, cnxk_tim_cntfrq()));
+       if (rcfg->clk_src == RTE_EVENT_TIMER_ADAPTER_CPU_CLK)
+               interval = NSEC2TICK(tim_ring->tck_nsec, cnxk_tim_cntfrq());
+       else
+               interval = NSEC2TICK(tim_ring->tck_nsec, dev->ext_clk_frq);
+
+       rc = roc_tim_lf_config(&dev->tim, tim_ring->ring_id,
+                              cnxk_tim_convert_clk_src(tim_ring->clk_src), 0,
+                              0, tim_ring->nb_bkts, tim_ring->chunk_sz,
+                              interval);
        if (rc < 0) {
                plt_err("Failed to configure timer ring");
                goto tim_chnk_free;
@@ -480,6 +586,8 @@ cnxk_tim_parse_devargs(struct rte_devargs *devargs, struct 
cnxk_tim_evdev *dev)
                           &dev->enable_stats);
        rte_kvargs_process(kvlist, CNXK_TIM_RINGS_LMT, &parse_kvargs_value,
                           &dev->min_ring_cnt);
+       rte_kvargs_process(kvlist, CNXK_TIM_EXT_CLK, &parse_kvargs_value,
+                          &dev->ext_clk_frq);
        rte_kvargs_process(kvlist, CNXK_TIM_RING_CTL,
                           &cnxk_tim_parse_kvargs_dict, &dev);
 
diff --git a/drivers/event/cnxk/cnxk_tim_evdev.h 
b/drivers/event/cnxk/cnxk_tim_evdev.h
index c369f6f472..b6e975dd50 100644
--- a/drivers/event/cnxk/cnxk_tim_evdev.h
+++ b/drivers/event/cnxk/cnxk_tim_evdev.h
@@ -39,6 +39,7 @@
 #define CNXK_TIM_STATS_ENA   "tim_stats_ena"
 #define CNXK_TIM_RINGS_LMT   "tim_rings_lmt"
 #define CNXK_TIM_RING_CTL    "tim_ring_ctl"
+#define CNXK_TIM_EXT_CLK     "tim_ext_clk"
 
 #define CNXK_TIM_SP       0x1
 #define CNXK_TIM_MP       0x2
@@ -94,6 +95,7 @@ struct cnxk_tim_evdev {
        uint16_t min_ring_cnt;
        uint8_t enable_stats;
        uint16_t ring_ctl_cnt;
+       uint64_t ext_clk_frq;
        struct cnxk_tim_ctl *ring_ctl_data;
 };
 
@@ -187,7 +189,9 @@ cnxk_tim_convert_clk_src(enum cnxk_tim_clk_src clk_src)
        switch (clk_src) {
        case RTE_EVENT_TIMER_ADAPTER_CPU_CLK:
                return roc_model_runtime_is_cn9k() ? ROC_TIM_CLK_SRC_10NS :
-                                                          ROC_TIM_CLK_SRC_GTI;
+                                                    ROC_TIM_CLK_SRC_GTI;
+       case RTE_EVENT_TIMER_ADAPTER_EXT_CLK0:
+               return ROC_TIM_CLK_SRC_GPIO;
        default:
                return ROC_TIM_CLK_SRC_INVALID;
        }
-- 
2.25.1

Reply via email to