diff --git a/doc/guides/rel_notes/release_20_11.rst b/doc/guides/rel_notes/release_20_11.rst
index f8686a5..7708e91 100644
--- a/doc/guides/rel_notes/release_20_11.rst
+++ b/doc/guides/rel_notes/release_20_11.rst
@@ -206,6 +206,15 @@ New Features
   * Extern objects and functions can be plugged into the pipeline.
   * Transaction-oriented table updates.
 
+* **Updated Mellanox mlx5 driver.**
+
+  Updated Mellanox mlx5 driver with new features and improvements, including:
+
+  * Updated the supported timeout for Age action to the maximal value supported
+    by rte_flow API.
+
+  * Added support of Age action query.
+
 * **Add new AVX512 specific classify algorithms for ACL library.**
 
   * Added new ``RTE_ACL_CLASSIFY_AVX512X16`` vector implementation,
diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h
index e528833..b08de80 100644
--- a/drivers/net/mlx5/mlx5.h
+++ b/drivers/net/mlx5/mlx5.h
@@ -276,7 +276,6 @@ struct mlx5_drop {
 #define CNT_SIZE (sizeof(struct mlx5_flow_counter))
 #define CNTEXT_SIZE (sizeof(struct mlx5_flow_counter_ext))
 #define AGE_SIZE (sizeof(struct mlx5_age_param))
-#define MLX5_AGING_TIME_DELAY	7
 #define CNT_POOL_TYPE_EXT	(1 << 0)
 #define CNT_POOL_TYPE_AGE	(1 << 1)
 #define IS_EXT_POOL(pool) (((pool)->type) & CNT_POOL_TYPE_EXT)
@@ -315,9 +314,7 @@ struct mlx5_drop {
  */
 #define POOL_IDX_INVALID UINT16_MAX
 
-struct mlx5_flow_counter_pool;
-
-/*age status*/
+/* Age status. */
 enum {
 	AGE_FREE, /* Initialized state. */
 	AGE_CANDIDATE, /* Counter assigned to flows. */
@@ -337,10 +334,10 @@ enum {
 
 /* Counter age parameter. */
 struct mlx5_age_param {
-	rte_atomic16_t state; /**< Age state. */
+	uint16_t state; /**< Age state (atomically accessed). */
 	uint16_t port_id; /**< Port id of the counter. */
-	uint32_t timeout:15; /**< Age timeout in unit of 0.1sec. */
-	uint32_t expire:16; /**< Expire time(0.1sec) in the future. */
+	uint32_t timeout:24; /**< Aging timeout in seconds. */
+	uint32_t sec_since_last_hit:25; /**< Time in seconds since last hit. */
 	void *context; /**< Flow counter age context. */
 };
 
@@ -349,7 +346,6 @@ struct flow_counter_stats {
 	uint64_t bytes;
 };
 
-struct mlx5_flow_counter_pool;
 /* Generic counters information. */
 struct mlx5_flow_counter {
 	TAILQ_ENTRY(mlx5_flow_counter) next;
@@ -391,6 +387,8 @@ struct mlx5_flow_counter_pool {
 		rte_atomic64_t a64_dcs;
 	};
 	/* The devx object of the minimum counter ID. */
+	uint64_t time_of_last_age_check;
+	/* System time (from rte_rdtsc()) read in the last aging check. */
 	uint32_t index:28; /* Pool index in container. */
 	uint32_t type:2; /* Memory type behind the counter array. */
 	uint32_t skip_cnt:1; /* Pool contains skipped counter. */
@@ -400,8 +398,6 @@ struct mlx5_flow_counter_pool {
 	struct mlx5_counter_stats_raw *raw_hw; /* The raw on HW working. */
 };
 
-struct mlx5_counter_stats_raw;
-
 /* Memory management structure for group of counter statistics raws. */
 struct mlx5_counter_stats_mem_mng {
 	LIST_ENTRY(mlx5_counter_stats_mem_mng) next;
@@ -463,10 +459,12 @@ struct mlx5_flow_default_miss_resource {
 	((age_info)->flags & (1 << (BIT)))
 #define GET_PORT_AGE_INFO(priv) \
 	(&((priv)->sh->port[(priv)->dev_port - 1].age_info))
+/* Current time in seconds. */
+#define MLX5_CURR_TIME_SEC	(rte_rdtsc() / rte_get_tsc_hz())
 
 /* Aging information for per port. */
 struct mlx5_age_info {
-	uint8_t flags; /*Indicate if is new event or need be trigered*/
+	uint8_t flags; /* Indicate if is new event or need to be triggered. */
 	struct mlx5_counters aged_counters; /* Aged flow counter list. */
 	rte_spinlock_t aged_sl; /* Aged flow counter list lock. */
 };
diff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c
index 2729629..89cb56f 100644
--- a/drivers/net/mlx5/mlx5_flow.c
+++ b/drivers/net/mlx5/mlx5_flow.c
@@ -6670,7 +6670,7 @@ struct mlx5_meter_domains_infos *
 	offset = batch ? 0 : dcs->id % MLX5_COUNTERS_PER_POOL;
 	/*
 	 * Identify the counters released between query trigger and query
-	 * handle more effiecntly. The counter released in this gap period
+	 * handle more efficiently. The counter released in this gap period
 	 * should wait for a new round of query as the new arrived packets
 	 * will not be taken into account.
 	 */
@@ -6724,19 +6724,24 @@ struct mlx5_meter_domains_infos *
 	struct mlx5_age_param *age_param;
 	struct mlx5_counter_stats_raw *cur = pool->raw_hw;
 	struct mlx5_counter_stats_raw *prev = pool->raw;
-	uint16_t curr = rte_rdtsc() / (rte_get_tsc_hz() / 10);
+	const uint64_t curr_time = MLX5_CURR_TIME_SEC;
+	const uint32_t time_delta = curr_time - pool->time_of_last_age_check;
+	uint32_t expected = AGE_CANDIDATE;
 	uint32_t i;
 
+	pool->time_of_last_age_check = curr_time;
 	for (i = 0; i < MLX5_COUNTERS_PER_POOL; ++i) {
 		cnt = MLX5_POOL_GET_CNT(pool, i);
 		age_param = MLX5_CNT_TO_AGE(cnt);
-		if (rte_atomic16_read(&age_param->state) != AGE_CANDIDATE)
+		if (__atomic_load_n(&age_param->state,
+				    __ATOMIC_RELAXED) != AGE_CANDIDATE)
 			continue;
 		if (cur->data[i].hits != prev->data[i].hits) {
-			age_param->expire = curr + age_param->timeout;
+			age_param->sec_since_last_hit = 0;
 			continue;
 		}
-		if ((uint16_t)(curr - age_param->expire) >= (UINT16_MAX / 2))
+		age_param->sec_since_last_hit += time_delta;
+		if (age_param->sec_since_last_hit <= age_param->timeout)
 			continue;
 		/**
 		 * Hold the lock first, or if between the
@@ -6747,8 +6752,10 @@ struct mlx5_meter_domains_infos *
 		priv = rte_eth_devices[age_param->port_id].data->dev_private;
 		age_info = GET_PORT_AGE_INFO(priv);
 		rte_spinlock_lock(&age_info->aged_sl);
-		if (rte_atomic16_cmpset((volatile uint16_t *)&age_param->state,
-					AGE_CANDIDATE, AGE_TMOUT)) {
+		if (__atomic_compare_exchange_n(&age_param->state, &expected,
+						AGE_TMOUT, false,
+						__ATOMIC_RELAXED,
+						__ATOMIC_RELAXED)) {
 			TAILQ_INSERT_TAIL(&age_info->aged_counters, cnt, next);
 			MLX5_AGE_SET(age_info, MLX5_AGE_EVENT_NEW);
 		}
diff --git a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c
index dc020c8..520e35e 100644
--- a/drivers/net/mlx5/mlx5_flow_dv.c
+++ b/drivers/net/mlx5/mlx5_flow_dv.c
@@ -3950,14 +3950,14 @@ struct field_modify_info modify_tcp[] = {
 		return rte_flow_error_set(error, EINVAL,
 					  RTE_FLOW_ERROR_TYPE_ACTION, action,
 					  "configuration cannot be null");
-	if (age->timeout >= UINT16_MAX / 2 / 10)
-		return rte_flow_error_set(error, ENOTSUP,
+	if (!(age->timeout))
+		return rte_flow_error_set(error, EINVAL,
 					  RTE_FLOW_ERROR_TYPE_ACTION, action,
-					  "Max age time: 3275 seconds");
+					  "invalid timeout value 0");
 	if (action_flags & MLX5_FLOW_ACTION_AGE)
 		return rte_flow_error_set(error, EINVAL,
 					  RTE_FLOW_ERROR_TYPE_ACTION, NULL,
-					  "Duplicate age ctions set");
+					  "duplicate age actions set");
 	return 0;
 }
 
@@ -4697,6 +4697,7 @@ struct field_modify_info modify_tcp[] = {
 	TAILQ_INIT(&pool->counters[1]);
 	TAILQ_INSERT_HEAD(&cont->pool_list, pool, next);
 	pool->index = n_valid;
+	pool->time_of_last_age_check = MLX5_CURR_TIME_SEC;
 	cont->pools[n_valid] = pool;
 	if (!batch) {
 		int base = RTE_ALIGN_FLOOR(dcs->id, MLX5_COUNTERS_PER_POOL);
@@ -4819,7 +4820,7 @@ struct field_modify_info modify_tcp[] = {
 			       (MLX5_CNT_CONTAINER
 			       (priv->sh, batch, (age ^ 0x1)), dcs->id);
 			/*
-			 * Pool eixsts, counter will be added to the other
+			 * Pool exists, counter will be added to the other
 			 * container, need to reallocate it later.
 			 */
 			if (pool) {
@@ -5100,11 +5101,13 @@ struct field_modify_info modify_tcp[] = {
 	struct mlx5_age_info *age_info;
 	struct mlx5_age_param *age_param;
 	struct mlx5_priv *priv = dev->data->dev_private;
+	uint32_t expected = AGE_CANDIDATE;
 
 	age_info = GET_PORT_AGE_INFO(priv);
 	age_param = flow_dv_counter_idx_get_age(dev, counter);
-	if (!rte_atomic16_cmpset((volatile uint16_t *)&age_param->state,
-				 AGE_CANDIDATE, AGE_FREE)) {
+	if (!__atomic_compare_exchange_n(&age_param->state, &expected,
+					 AGE_FREE, false, __ATOMIC_RELAXED,
+					 __ATOMIC_RELAXED)) {
 		/**
 		 * We need the lock even it is age timeout,
 		 * since counter may still in process.
@@ -5112,9 +5115,10 @@ struct field_modify_info modify_tcp[] = {
 		rte_spinlock_lock(&age_info->aged_sl);
 		TAILQ_REMOVE(&age_info->aged_counters, cnt, next);
 		rte_spinlock_unlock(&age_info->aged_sl);
-		rte_atomic16_set(&age_param->state, AGE_FREE);
+		__atomic_store_n(&age_param->state, AGE_FREE, __ATOMIC_RELAXED);
 	}
 }
+
 /**
  * Release a flow counter.
  *
@@ -8241,22 +8245,12 @@ struct field_modify_info modify_tcp[] = {
 	if (!counter || age == NULL)
 		return counter;
 	age_param  = flow_dv_counter_idx_get_age(dev, counter);
-	/*
-	 * The counter age accuracy may have a bit delay. Have 3/4
-	 * second bias on the timeount in order to let it age in time.
-	 */
 	age_param->context = age->context ? age->context :
 		(void *)(uintptr_t)(dev_flow->flow_idx);
-	/*
-	 * The counter age accuracy may have a bit delay. Have 3/4
-	 * second bias on the timeount in order to let it age in time.
-	 */
-	age_param->timeout = age->timeout * 10 - MLX5_AGING_TIME_DELAY;
-	/* Set expire time in unit of 0.1 sec. */
+	age_param->timeout = age->timeout;
 	age_param->port_id = dev->data->port_id;
-	age_param->expire = age_param->timeout +
-			rte_rdtsc() / (rte_get_tsc_hz() / 10);
-	rte_atomic16_set(&age_param->state, AGE_CANDIDATE);
+	age_param->sec_since_last_hit = 0;
+	__atomic_store_n(&age_param->state, AGE_CANDIDATE, __ATOMIC_RELAXED);
 	return counter;
 }
 /**
@@ -10644,6 +10638,51 @@ struct field_modify_info modify_tcp[] = {
 }
 
 /**
+ * Query a flow rule AGE action for aging information.
+ *
+ * @param[in] dev
+ *   Pointer to Ethernet device.
+ * @param[in] flow
+ *   Pointer to the sub flow.
+ * @param[out] data
+ *   data retrieved by the query.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+flow_dv_query_age(struct rte_eth_dev *dev, struct rte_flow *flow,
+		  void *data, struct rte_flow_error *error)
+{
+	struct rte_flow_query_age *resp = data;
+
+	if (flow->counter) {
+		struct mlx5_age_param *age_param =
+				flow_dv_counter_idx_get_age(dev, flow->counter);
+
+		if (!age_param || !age_param->timeout)
+			return rte_flow_error_set
+					(error, EINVAL,
+					 RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+					 NULL, "cannot read age data");
+		resp->aged = __atomic_load_n(&age_param->state,
+					     __ATOMIC_RELAXED) ==
+							AGE_TMOUT ? 1 : 0;
+		resp->sec_since_last_hit_valid = !resp->aged;
+		if (resp->sec_since_last_hit_valid)
+			resp->sec_since_last_hit =
+					age_param->sec_since_last_hit;
+		return 0;
+	}
+	return rte_flow_error_set(error, EINVAL,
+				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				  NULL,
+				  "age data not available");
+}
+
+/**
  * Query a flow.
  *
  * @see rte_flow_query()
@@ -10665,6 +10704,9 @@ struct field_modify_info modify_tcp[] = {
 		case RTE_FLOW_ACTION_TYPE_COUNT:
 			ret = flow_dv_query_count(dev, flow, data, error);
 			break;
+		case RTE_FLOW_ACTION_TYPE_AGE:
+			ret = flow_dv_query_age(dev, flow, data, error);
+			break;
 		default:
 			return rte_flow_error_set(error, ENOTSUP,
 						  RTE_FLOW_ERROR_TYPE_ACTION,
