diff --git a/MAINTAINERS b/MAINTAINERS
index a5219926ab..82f490c5c0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1551,6 +1551,12 @@ F: doc/guides/tools/pdump.rst
 F: app/dumpcap/
 F: doc/guides/tools/dumpcap.rst
 
+PDCP - EXPERIMENTAL
+M: Anoob Joseph <anoobj@marvell.com>
+M: Volodymyr Fialko <vfialko@marvell.com>
+T: git://dpdk.org/next/dpdk-next-crypto
+F: lib/pdcp/
+
 
 Packet Framework
 ----------------
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index debbe4134f..cd7a6cae44 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -128,7 +128,8 @@ The public API headers are grouped by topics:
   [eCPRI](@ref rte_ecpri.h),
   [L2TPv2](@ref rte_l2tpv2.h),
   [PPP](@ref rte_ppp.h),
-  [PDCP hdr](@ref rte_pdcp_hdr.h)
+  [PDCP hdr](@ref rte_pdcp_hdr.h),
+  [PDCP](@ref rte_pdcp.h)
 
 - **QoS**:
   [metering](@ref rte_meter.h),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index d230a19e1f..58789308a9 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -62,6 +62,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/net \
                           @TOPDIR@/lib/pcapng \
                           @TOPDIR@/lib/pci \
+                          @TOPDIR@/lib/pdcp \
                           @TOPDIR@/lib/pdump \
                           @TOPDIR@/lib/pipeline \
                           @TOPDIR@/lib/port \
diff --git a/lib/meson.build b/lib/meson.build
index dc8aa4ac84..a6a54c196c 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -64,6 +64,7 @@ libraries = [
         'flow_classify', # flow_classify lib depends on pkt framework table lib
         'graph',
         'node',
+        'pdcp', # pdcp lib depends on crypto and security
 ]
 
 optional_libs = [
diff --git a/lib/pdcp/meson.build b/lib/pdcp/meson.build
new file mode 100644
index 0000000000..ccaf426240
--- /dev/null
+++ b/lib/pdcp/meson.build
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2023 Marvell.
+
+if is_windows
+    build = false
+    reason = 'not supported on Windows'
+    subdir_done()
+endif
+
+sources = files(
+        'pdcp_crypto.c',
+        'pdcp_process.c',
+        'rte_pdcp.c',
+        )
+headers = files('rte_pdcp.h')
+
+deps += ['mbuf', 'net', 'cryptodev', 'security']
diff --git a/lib/pdcp/pdcp_crypto.c b/lib/pdcp/pdcp_crypto.c
new file mode 100644
index 0000000000..755e27ec9e
--- /dev/null
+++ b/lib/pdcp/pdcp_crypto.c
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+
+#include <rte_pdcp.h>
+
+#include "pdcp_crypto.h"
+
+int
+pdcp_crypto_sess_create(struct rte_pdcp_entity *entity, const struct rte_pdcp_entity_conf *conf)
+{
+	RTE_SET_USED(entity);
+	RTE_SET_USED(conf);
+	return 0;
+}
+
+void
+pdcp_crypto_sess_destroy(struct rte_pdcp_entity *entity)
+{
+	RTE_SET_USED(entity);
+}
diff --git a/lib/pdcp/pdcp_crypto.h b/lib/pdcp/pdcp_crypto.h
new file mode 100644
index 0000000000..6563331d37
--- /dev/null
+++ b/lib/pdcp/pdcp_crypto.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+
+#ifndef PDCP_CRYPTO_H
+#define PDCP_CRYPTO_H
+
+#include <rte_pdcp.h>
+
+int pdcp_crypto_sess_create(struct rte_pdcp_entity *entity,
+			    const struct rte_pdcp_entity_conf *conf);
+
+void pdcp_crypto_sess_destroy(struct rte_pdcp_entity *entity);
+
+#endif /* PDCP_CRYPTO_H */
diff --git a/lib/pdcp/pdcp_entity.h b/lib/pdcp/pdcp_entity.h
new file mode 100644
index 0000000000..000297588f
--- /dev/null
+++ b/lib/pdcp/pdcp_entity.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+
+#ifndef PDCP_ENTITY_H
+#define PDCP_ENTITY_H
+
+#include <rte_common.h>
+#include <rte_crypto_sym.h>
+#include <rte_mempool.h>
+#include <rte_pdcp.h>
+#include <rte_security.h>
+
+struct entity_priv;
+
+/* IV generation function based on the entity configuration */
+typedef void (*iv_gen_t)(struct rte_crypto_op *cop, const struct entity_priv *en_priv,
+			 uint32_t count);
+
+struct entity_state {
+	uint32_t rx_next;
+	uint32_t tx_next;
+	uint32_t rx_deliv;
+	uint32_t rx_reord;
+};
+
+/*
+ * Layout of PDCP entity: [rte_pdcp_entity] [entity_priv] [entity_dl/ul]
+ */
+
+struct entity_priv {
+	/** Crypto sym session. */
+	struct rte_cryptodev_sym_session *crypto_sess;
+	/** Entity specific IV generation function. */
+	iv_gen_t iv_gen;
+	/** Entity state variables. */
+	struct entity_state state;
+	/** Flags. */
+	struct {
+		/** PDCP PDU has 4 byte MAC-I. */
+		uint64_t is_authenticated : 1;
+		/** Cipher offset & length in bits. */
+		uint64_t is_cipher_in_bits : 1;
+		/** Auth offset & length in bits. */
+		uint64_t is_auth_in_bits : 1;
+		/** Is UL/transmitting PDCP entity. */
+		uint64_t is_ul_entity : 1;
+		/** Is NULL auth. */
+		uint64_t is_null_auth : 1;
+	} flags;
+	/** Crypto op pool. */
+	struct rte_mempool *cop_pool;
+	/** PDCP header size. */
+	uint8_t hdr_sz;
+	/** PDCP AAD size. For AES-CMAC, additional message is prepended for the operation. */
+	uint8_t aad_sz;
+	/** Device ID of the device to be used for offload. */
+	uint8_t dev_id;
+};
+
+struct entity_priv_dl_part {
+	/* NOTE: when in-order-delivery is supported, post PDCP packets would need to cached. */
+	uint8_t dummy;
+};
+
+struct entity_priv_ul_part {
+	/*
+	 * NOTE: when re-establish is supported, plain PDCP packets & COUNT values need to be
+	 * cached.
+	 */
+	uint8_t dummy;
+};
+
+static inline struct entity_priv *
+entity_priv_get(const struct rte_pdcp_entity *entity) {
+	return RTE_PTR_ADD(entity, sizeof(struct rte_pdcp_entity));
+}
+
+static inline struct entity_priv_dl_part *
+entity_dl_part_get(const struct rte_pdcp_entity *entity) {
+	return RTE_PTR_ADD(entity, sizeof(struct rte_pdcp_entity) + sizeof(struct entity_priv));
+}
+
+static inline struct entity_priv_ul_part *
+entity_ul_part_get(const struct rte_pdcp_entity *entity) {
+	return RTE_PTR_ADD(entity, sizeof(struct rte_pdcp_entity) + sizeof(struct entity_priv));
+}
+
+static inline int
+pdcp_hdr_size_get(enum rte_security_pdcp_sn_size sn_size)
+{
+	return RTE_ALIGN_MUL_CEIL(sn_size, 8) / 8;
+}
+
+static inline uint32_t
+pdcp_sn_mask_get(enum rte_security_pdcp_sn_size sn_size)
+{
+	return (1 << sn_size) - 1;
+}
+
+static inline uint32_t
+pdcp_hfn_mask_get(enum rte_security_pdcp_sn_size sn_size)
+{
+	return ~pdcp_sn_mask_get(sn_size);
+}
+
+static inline uint32_t
+pdcp_count_from_hfn_sn_get(uint32_t hfn, uint32_t sn, enum rte_security_pdcp_sn_size sn_size)
+{
+	return (((hfn << sn_size) & pdcp_hfn_mask_get(sn_size)) | (sn & pdcp_sn_mask_get(sn_size)));
+}
+
+#endif /* PDCP_ENTITY_H */
diff --git a/lib/pdcp/pdcp_process.c b/lib/pdcp/pdcp_process.c
new file mode 100644
index 0000000000..79f5dce5db
--- /dev/null
+++ b/lib/pdcp/pdcp_process.c
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+
+#include <rte_crypto.h>
+#include <rte_crypto_sym.h>
+#include <rte_cryptodev.h>
+#include <rte_memcpy.h>
+#include <rte_pdcp.h>
+#include <rte_pdcp_hdr.h>
+
+#include "pdcp_crypto.h"
+#include "pdcp_entity.h"
+#include "pdcp_process.h"
+
+static int
+pdcp_crypto_xfrm_get(const struct rte_pdcp_entity_conf *conf, struct rte_crypto_sym_xform **c_xfrm,
+		     struct rte_crypto_sym_xform **a_xfrm)
+{
+	*c_xfrm = NULL;
+	*a_xfrm = NULL;
+
+	if (conf->crypto_xfrm == NULL)
+		return -EINVAL;
+
+	if (conf->crypto_xfrm->type == RTE_CRYPTO_SYM_XFORM_CIPHER) {
+		*c_xfrm = conf->crypto_xfrm;
+		*a_xfrm = conf->crypto_xfrm->next;
+	} else if (conf->crypto_xfrm->type == RTE_CRYPTO_SYM_XFORM_AUTH) {
+		*a_xfrm = conf->crypto_xfrm;
+		*c_xfrm = conf->crypto_xfrm->next;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+pdcp_entity_priv_populate(struct entity_priv *en_priv, const struct rte_pdcp_entity_conf *conf)
+{
+	struct rte_crypto_sym_xform *c_xfrm, *a_xfrm;
+	int ret;
+
+	ret = pdcp_crypto_xfrm_get(conf, &c_xfrm, &a_xfrm);
+	if (ret)
+		return ret;
+
+	/**
+	 * flags.is_authenticated
+	 *
+	 * MAC-I would be added in case of control plane packets and when authentication
+	 * transform is not NULL.
+	 */
+
+	if ((conf->pdcp_xfrm.domain == RTE_SECURITY_PDCP_MODE_CONTROL) && (a_xfrm == NULL))
+		return -EINVAL;
+
+	if (a_xfrm != NULL)
+		en_priv->flags.is_authenticated = 1;
+
+	/**
+	 * flags.is_cipher_in_bits
+	 *
+	 * For ZUC & SNOW3G cipher algos, offset & length need to be provided in bits.
+	 */
+
+	if ((c_xfrm->cipher.algo == RTE_CRYPTO_CIPHER_SNOW3G_UEA2) ||
+	    (c_xfrm->cipher.algo == RTE_CRYPTO_CIPHER_ZUC_EEA3))
+		en_priv->flags.is_cipher_in_bits = 1;
+
+	/**
+	 * flags.is_auth_in_bits
+	 *
+	 * For ZUC & SNOW3G authentication algos, offset & length need to be provided in bits.
+	 */
+
+	if (a_xfrm != NULL) {
+		if ((a_xfrm->auth.algo == RTE_CRYPTO_AUTH_SNOW3G_UIA2) ||
+		    (a_xfrm->auth.algo == RTE_CRYPTO_AUTH_ZUC_EIA3))
+			en_priv->flags.is_auth_in_bits = 1;
+	}
+
+	/**
+	 * flags.is_ul_entity
+	 *
+	 * Indicate whether the entity is UL/transmitting PDCP entity.
+	 */
+	if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_UPLINK)
+		en_priv->flags.is_ul_entity = 1;
+
+	/**
+	 * flags.is_null_auth
+	 *
+	 * For NULL auth, 4B zeros need to be added by lib PDCP. Indicate that
+	 * algo is NULL auth to perform the same.
+	 */
+	if (a_xfrm != NULL && a_xfrm->auth.algo == RTE_CRYPTO_AUTH_NULL)
+		en_priv->flags.is_null_auth = 1;
+
+	/**
+	 * hdr_sz
+	 *
+	 * PDCP header size of the entity
+	 */
+	en_priv->hdr_sz = pdcp_hdr_size_get(conf->pdcp_xfrm.sn_size);
+
+	/**
+	 * aad_sz
+	 *
+	 * For AES-CMAC, additional message is prepended for processing. Need to be trimmed after
+	 * crypto processing is done.
+	 */
+	if (a_xfrm != NULL && a_xfrm->auth.algo == RTE_CRYPTO_AUTH_AES_CMAC)
+		en_priv->aad_sz = 8;
+	else
+		en_priv->aad_sz = 0;
+
+	return 0;
+}
+
+int
+pdcp_process_func_set(struct rte_pdcp_entity *entity, const struct rte_pdcp_entity_conf *conf)
+{
+	struct entity_priv *en_priv;
+	int ret;
+
+	if (entity == NULL || conf == NULL)
+		return -EINVAL;
+
+	en_priv = entity_priv_get(entity);
+
+	ret = pdcp_entity_priv_populate(en_priv, conf);
+	if (ret)
+		return ret;
+
+	return 0;
+}
diff --git a/lib/pdcp/pdcp_process.h b/lib/pdcp/pdcp_process.h
new file mode 100644
index 0000000000..fd53fff0aa
--- /dev/null
+++ b/lib/pdcp/pdcp_process.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+
+#ifndef PDCP_PROCESS_H
+#define PDCP_PROCESS_H
+
+#include <rte_pdcp.h>
+
+int
+pdcp_process_func_set(struct rte_pdcp_entity *entity, const struct rte_pdcp_entity_conf *conf);
+
+#endif /* PDCP_PROCESS_H */
diff --git a/lib/pdcp/rte_pdcp.c b/lib/pdcp/rte_pdcp.c
new file mode 100644
index 0000000000..adcad5dd25
--- /dev/null
+++ b/lib/pdcp/rte_pdcp.c
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+
+#include <rte_errno.h>
+#include <rte_pdcp.h>
+#include <rte_malloc.h>
+
+#include "pdcp_crypto.h"
+#include "pdcp_entity.h"
+#include "pdcp_process.h"
+
+static int
+pdcp_entity_size_get(const struct rte_pdcp_entity_conf *conf)
+{
+	int size;
+
+	size = sizeof(struct rte_pdcp_entity) + sizeof(struct entity_priv);
+
+	if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_DOWNLINK)
+		size += sizeof(struct entity_priv_dl_part);
+	else if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_UPLINK)
+		size += sizeof(struct entity_priv_ul_part);
+	else
+		return -EINVAL;
+
+	return RTE_ALIGN_CEIL(size, RTE_CACHE_LINE_SIZE);
+}
+
+struct rte_pdcp_entity *
+rte_pdcp_entity_establish(const struct rte_pdcp_entity_conf *conf)
+{
+	struct rte_pdcp_entity *entity = NULL;
+	struct entity_priv *en_priv;
+	int ret, entity_size;
+	uint32_t count;
+
+	if (conf == NULL || conf->cop_pool == NULL) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	if (conf->pdcp_xfrm.en_ordering || conf->pdcp_xfrm.remove_duplicates || conf->is_slrb ||
+	    conf->en_sec_offload) {
+		rte_errno = ENOTSUP;
+		return NULL;
+	}
+
+	/*
+	 * 6.3.2 PDCP SN
+	 * Length: 12 or 18 bits as indicated in table 6.3.2-1. The length of the PDCP SN is
+	 * configured by upper layers (pdcp-SN-SizeUL, pdcp-SN-SizeDL, or sl-PDCP-SN-Size in
+	 * TS 38.331 [3])
+	 */
+	if ((conf->pdcp_xfrm.sn_size != RTE_SECURITY_PDCP_SN_SIZE_12) &&
+	    (conf->pdcp_xfrm.sn_size != RTE_SECURITY_PDCP_SN_SIZE_18)) {
+		rte_errno = ENOTSUP;
+		return NULL;
+	}
+
+	if (conf->pdcp_xfrm.hfn_threshold) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	entity_size = pdcp_entity_size_get(conf);
+	if (entity_size < 0) {
+		rte_errno = EINVAL;
+		return NULL;
+	}
+
+	entity = rte_zmalloc_socket("pdcp_entity", entity_size, RTE_CACHE_LINE_SIZE, SOCKET_ID_ANY);
+	if (entity == NULL) {
+		rte_errno = ENOMEM;
+		return NULL;
+	}
+
+	en_priv = entity_priv_get(entity);
+
+	count = pdcp_count_from_hfn_sn_get(conf->pdcp_xfrm.hfn, conf->sn, conf->pdcp_xfrm.sn_size);
+
+	en_priv->state.rx_deliv = count;
+	en_priv->state.tx_next = count;
+	en_priv->cop_pool = conf->cop_pool;
+
+	/* Setup crypto session */
+	ret = pdcp_crypto_sess_create(entity, conf);
+	if (ret)
+		goto entity_free;
+
+	ret = pdcp_process_func_set(entity, conf);
+	if (ret)
+		goto crypto_sess_destroy;
+
+	return entity;
+
+crypto_sess_destroy:
+	pdcp_crypto_sess_destroy(entity);
+entity_free:
+	rte_free(entity);
+	rte_errno = -ret;
+	return NULL;
+}
+
+int
+rte_pdcp_entity_release(struct rte_pdcp_entity *pdcp_entity, struct rte_mbuf *out_mb[])
+{
+	if (pdcp_entity == NULL)
+		return -EINVAL;
+
+	/* Teardown crypto sessions */
+	pdcp_crypto_sess_destroy(pdcp_entity);
+
+	rte_free(pdcp_entity);
+
+	RTE_SET_USED(out_mb);
+	return 0;
+}
+
+int
+rte_pdcp_entity_suspend(struct rte_pdcp_entity *pdcp_entity,
+			struct rte_mbuf *out_mb[])
+{
+	struct entity_priv *en_priv;
+
+	if (pdcp_entity == NULL)
+		return -EINVAL;
+
+	en_priv = entity_priv_get(pdcp_entity);
+
+	if (en_priv->flags.is_ul_entity) {
+		en_priv->state.tx_next = 0;
+	} else {
+		en_priv->state.rx_next = 0;
+		en_priv->state.rx_deliv = 0;
+	}
+
+	RTE_SET_USED(out_mb);
+
+	return 0;
+}
diff --git a/lib/pdcp/rte_pdcp.h b/lib/pdcp/rte_pdcp.h
new file mode 100644
index 0000000000..4489b9526b
--- /dev/null
+++ b/lib/pdcp/rte_pdcp.h
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+
+#ifndef RTE_PDCP_H
+#define RTE_PDCP_H
+
+/**
+ * @file rte_pdcp.h
+ *
+ * RTE PDCP support.
+ *
+ * A framework for PDCP protocol processing.
+ */
+
+#include <rte_compat.h>
+#include <rte_common.h>
+#include <rte_mempool.h>
+#include <rte_security.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * PDCP entity.
+ *
+ * 4.2.2 PDCP entities
+ *
+ * The PDCP entities are located in the PDCP sublayer.
+ * Several PDCP entities may be defined for a UE.
+ * Each PDCP entity is carrying the data of one radio bearer.
+ * A PDCP entity is associated either to the control plane or the user plane
+ * depending on which radio bearer it is carrying data for.
+ */
+struct rte_pdcp_entity {
+	/**
+	 * PDCP entities may hold packets for purposes of in-order delivery
+	 * (in case of receiving PDCP entity) and re-transmission
+	 * (in case of transmitting PDCP entity).
+	 *
+	 * The field 'max_pkt_cache' would be used to indicate the maximum
+	 * number of packets that may be cached in an entity at any point of time.
+	 * When application provides buffers to receive packets from PDCP entity,
+	 * the size of the buffer should be such that it can
+	 * hold additionally 'max_pkt_cache' number of packets.
+	 */
+	uint32_t max_pkt_cache;
+} __rte_cache_aligned;
+
+/**
+ * PDCP entity configuration to be used for establishing an entity.
+ */
+/* Structure rte_pdcp_entity_conf 8< */
+struct rte_pdcp_entity_conf {
+	/** PDCP transform for the entity. */
+	struct rte_security_pdcp_xform pdcp_xfrm;
+	/** Crypto transform applicable for the entity. */
+	struct rte_crypto_sym_xform *crypto_xfrm;
+	/** Mempool for crypto symmetric session. */
+	struct rte_mempool *sess_mpool;
+	/** Crypto op pool. */
+	struct rte_mempool *cop_pool;
+	/**
+	 * Sequence number value to be used.
+	 * 32 bit count value to be used for the first packet
+	 * would be derived based on HFN (`rte_security_pdcp_xform.hfn`) and SN.
+	 */
+	uint32_t sn;
+	/** Indicate whether the PDCP entity belongs to Side Link Radio Bearer. */
+	bool is_slrb;
+	/** Enable security offload on the device specified. */
+	bool en_sec_offload;
+	/** Device on which security/crypto session need to be created. */
+	uint8_t dev_id;
+	/**
+	 * Reverse direction during IV generation.
+	 * Can be used to simulate UE crypto processing.
+	 */
+	bool reverse_iv_direction;
+};
+/* >8 End of structure rte_pdcp_entity_conf. */
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * 5.1.1 PDCP entity establishment
+ *
+ * Establish PDCP entity based on provided input configuration.
+ *
+ * @param conf
+ *   Parameters to be used for initializing PDCP entity object.
+ * @return
+ *   - Valid handle if success
+ *   - NULL in case of failure. rte_errno will be set to error code.
+ */
+__rte_experimental
+struct rte_pdcp_entity *
+rte_pdcp_entity_establish(const struct rte_pdcp_entity_conf *conf);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * 5.1.3 PDCP entity release
+ *
+ * Release PDCP entity.
+ *
+ * For UL/transmitting PDCP entity, all stored PDCP SDUs would be dropped.
+ * For DL/receiving PDCP entity, the stored PDCP SDUs would be returned in
+ * *out_mb* buffer. The buffer should be large enough to hold all cached
+ * packets in the entity.
+ *
+ * Entity release would result in freeing all memory associated with the PDCP
+ * entity as well as any crypto/security sessions created.
+ *
+ * @param pdcp_entity
+ *   Pointer to the PDCP entity to be released.
+ * @param[out] out_mb
+ *   The address of an array that can hold up to *rte_pdcp_entity.max_pkt_cache*
+ *   pointers to *rte_mbuf* structures.
+ * @return
+ *   -  0: Success and no cached packets to return
+ *   - >0: Success and the number of packets returned in out_mb
+ *   - <0: Error code in case of failures
+ */
+__rte_experimental
+int
+rte_pdcp_entity_release(struct rte_pdcp_entity *pdcp_entity,
+			struct rte_mbuf *out_mb[]);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * 5.1.4 PDCP entity suspend
+ *
+ * Suspend PDCP entity.
+ *
+ * For DL/receiving PDCP entity, the stored PDCP SDUs would be returned in
+ * *out_mb* buffer. The buffer should be large enough to hold all cached
+ * packets in the entity.
+ *
+ * For UL/transmitting PDCP entity, *out_mb* buffer would be unused.
+ *
+ * @param pdcp_entity
+ *   Pointer to the PDCP entity to be suspended.
+ * @param[out] out_mb
+ *   The address of an array that can hold up to *rte_pdcp_entity.max_pkt_cache*
+ *   pointers to *rte_mbuf* structures.
+ * @return
+ *   -  0: Success and no cached packets to return.
+ *   - >0: Success and the number of packets returned in out_mb.
+ *   - <0: Error code in case of failures.
+ */
+__rte_experimental
+int
+rte_pdcp_entity_suspend(struct rte_pdcp_entity *pdcp_entity,
+			struct rte_mbuf *out_mb[]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RTE_PDCP_H */
diff --git a/lib/pdcp/version.map b/lib/pdcp/version.map
new file mode 100644
index 0000000000..923e165f3f
--- /dev/null
+++ b/lib/pdcp/version.map
@@ -0,0 +1,10 @@
+EXPERIMENTAL {
+	global:
+
+	# added in 23.07
+	rte_pdcp_entity_establish;
+	rte_pdcp_entity_release;
+	rte_pdcp_entity_suspend;
+
+	local: *;
+};
