diff --git a/doc/guides/rawdevs/cnxk_bphy.rst b/doc/guides/rawdevs/cnxk_bphy.rst
index e031f4148..899557df0 100644
--- a/doc/guides/rawdevs/cnxk_bphy.rst
+++ b/doc/guides/rawdevs/cnxk_bphy.rst
@@ -18,6 +18,7 @@ The BPHY CGX/RPM implements following features in the rawdev API:
 
 - Access to BPHY CGX/RPM via a set of predefined messages
 - Access to BPHY memory
+- Custom interrupt handlers
 
 Device Setup
 ------------
@@ -117,6 +118,18 @@ are also two convenience functions namely ``rte_pmd_bphy_intr_init()`` and
 ``rte_pmd_bphy_intr_fini()`` that take care of all details.
 
 
+Register or remove interrupt handler
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Message is used setup custom interrupt handler.
+
+Message must have type set to ``CNXK_BPHY_IRQ_MSG_TYPE_REGISTER`` or
+``CNXK_BPHY_IRQ_MSG_TYPE_UNREGISTER``. The former will register an interrupt handler while the
+latter will remove it. Prior sending actual message payload i.e ``struct cnxk_bphy_irq_info`` needs
+to be filled with relevant information. There are also two convenience functions namely
+``rte_pmd_bphy_intr_register()`` and ``rte_pmd_bphy_intr_unregister()`` that take care of all
+details.
+
 Get device memory
 ~~~~~~~~~~~~~~~~~
 
diff --git a/drivers/raw/cnxk_bphy/cnxk_bphy.c b/drivers/raw/cnxk_bphy/cnxk_bphy.c
index 278e26af0..2a516ae73 100644
--- a/drivers/raw/cnxk_bphy/cnxk_bphy.c
+++ b/drivers/raw/cnxk_bphy/cnxk_bphy.c
@@ -38,6 +38,7 @@ cnxk_bphy_irq_enqueue_bufs(struct rte_rawdev *dev,
 	struct bphy_device *bphy_dev = (struct bphy_device *)dev->dev_private;
 	struct cnxk_bphy_irq_msg *msg = buffers[0]->buf_addr;
 	unsigned int queue = (size_t)context;
+	struct cnxk_bphy_irq_info *info;
 	int ret = 0;
 
 	if (queue >= RTE_DIM(bphy_dev->queues))
@@ -53,6 +54,16 @@ cnxk_bphy_irq_enqueue_bufs(struct rte_rawdev *dev,
 	case CNXK_BPHY_IRQ_MSG_TYPE_FINI:
 		cnxk_bphy_intr_fini(dev->dev_id);
 		break;
+	case CNXK_BPHY_IRQ_MSG_TYPE_REGISTER:
+		info = (struct cnxk_bphy_irq_info *)msg->data;
+		ret = cnxk_bphy_intr_register(dev->dev_id, info->irq_num,
+					      info->handler, info->data,
+					      info->cpu);
+		break;
+	case CNXK_BPHY_IRQ_MSG_TYPE_UNREGISTER:
+		info = (struct cnxk_bphy_irq_info *)msg->data;
+		cnxk_bphy_intr_unregister(dev->dev_id, info->irq_num);
+		break;
 	case CNXK_BPHY_IRQ_MSG_TYPE_MEM_GET:
 		bphy_dev->queues[queue].rsp = &bphy_dev->mem;
 		break;
diff --git a/drivers/raw/cnxk_bphy/cnxk_bphy_irq.c b/drivers/raw/cnxk_bphy/cnxk_bphy_irq.c
index 13a0d8ad1..bbcc285a7 100644
--- a/drivers/raw/cnxk_bphy/cnxk_bphy_irq.c
+++ b/drivers/raw/cnxk_bphy/cnxk_bphy_irq.c
@@ -58,6 +58,39 @@ cnxk_bphy_intr_fini(uint16_t dev_id)
 	bphy_dev->irq_chip = NULL;
 }
 
+int
+cnxk_bphy_intr_register(uint16_t dev_id, int irq_num,
+			cnxk_bphy_intr_handler_t handler, void *data, int cpu)
+{
+	struct roc_bphy_intr intr = {
+		.irq_num = irq_num,
+		.intr_handler = handler,
+		.isr_data = data,
+		.cpu = cpu
+	};
+
+	struct bphy_device *bphy_dev = cnxk_bphy_get_bphy_dev_by_dev_id(dev_id);
+	struct roc_bphy_irq_chip *irq_chip = bphy_dev->irq_chip;
+
+	if (!irq_chip)
+		return -ENODEV;
+	if (!handler || !data)
+		return -EINVAL;
+
+	return roc_bphy_intr_register(irq_chip, &intr);
+}
+
+void
+cnxk_bphy_intr_unregister(uint16_t dev_id, int irq_num)
+{
+	struct bphy_device *bphy_dev = cnxk_bphy_get_bphy_dev_by_dev_id(dev_id);
+
+	if (bphy_dev->irq_chip)
+		roc_bphy_handler_clear(bphy_dev->irq_chip, irq_num);
+	else
+		plt_err("Missing irq chip");
+}
+
 struct bphy_mem *
 cnxk_bphy_mem_get(uint16_t dev_id)
 {
diff --git a/drivers/raw/cnxk_bphy/cnxk_bphy_irq.h b/drivers/raw/cnxk_bphy/cnxk_bphy_irq.h
index 5f87143a0..b55147b93 100644
--- a/drivers/raw/cnxk_bphy/cnxk_bphy_irq.h
+++ b/drivers/raw/cnxk_bphy/cnxk_bphy_irq.h
@@ -32,6 +32,10 @@ struct bphy_device {
 int cnxk_bphy_intr_init(uint16_t dev_id);
 void cnxk_bphy_intr_fini(uint16_t dev_id);
 struct bphy_mem *cnxk_bphy_mem_get(uint16_t dev_id);
+int cnxk_bphy_intr_register(uint16_t dev_id, int irq_num,
+			    cnxk_bphy_intr_handler_t handler,
+			    void *isr_data, int cpu);
+void cnxk_bphy_intr_unregister(uint16_t dev_id, int irq_num);
 uint64_t cnxk_bphy_irq_max_get(uint16_t dev_id);
 
 #endif /* _CNXK_BPHY_IRQ_ */
diff --git a/drivers/raw/cnxk_bphy/rte_pmd_bphy.h b/drivers/raw/cnxk_bphy/rte_pmd_bphy.h
index d08b14b57..f3387f38e 100644
--- a/drivers/raw/cnxk_bphy/rte_pmd_bphy.h
+++ b/drivers/raw/cnxk_bphy/rte_pmd_bphy.h
@@ -118,6 +118,8 @@ struct cnxk_bphy_irq_msg {
 	enum cnxk_bphy_irq_msg_type type;
 	/*
 	 * The data field, depending on message type, may point to
+	 * - (enq) full struct cnxk_bphy_irq_info for registration request
+	 * - (enq) struct cnxk_bphy_irq_info with irq_num set for unregistration
 	 * - (deq) struct cnxk_bphy_mem for memory range request response
 	 * - (xxx) NULL
 	 */
@@ -161,6 +163,49 @@ rte_pmd_bphy_intr_fini(uint16_t dev_id)
 	rte_rawdev_enqueue_buffers(dev_id, bufs, 1, CNXK_BPHY_DEF_QUEUE);
 }
 
+static __rte_always_inline int
+rte_pmd_bphy_intr_register(uint16_t dev_id, int irq_num,
+			   cnxk_bphy_intr_handler_t handler, void *data,
+			   int cpu)
+{
+	struct cnxk_bphy_irq_info info = {
+		.irq_num = irq_num,
+		.handler = handler,
+		.data = data,
+		.cpu = cpu,
+	};
+	struct cnxk_bphy_irq_msg msg = {
+		.type = CNXK_BPHY_IRQ_MSG_TYPE_REGISTER,
+		.data = &info
+	};
+	struct rte_rawdev_buf *bufs[1];
+	struct rte_rawdev_buf buf;
+
+	buf.buf_addr = &msg;
+	bufs[0] = &buf;
+
+	return rte_rawdev_enqueue_buffers(dev_id, bufs, 1, CNXK_BPHY_DEF_QUEUE);
+}
+
+static __rte_always_inline void
+rte_pmd_bphy_intr_unregister(uint16_t dev_id, int irq_num)
+{
+	struct cnxk_bphy_irq_info info = {
+		.irq_num = irq_num,
+	};
+	struct cnxk_bphy_irq_msg msg = {
+		.type = CNXK_BPHY_IRQ_MSG_TYPE_UNREGISTER,
+		.data = &info
+	};
+	struct rte_rawdev_buf *bufs[1];
+	struct rte_rawdev_buf buf;
+
+	buf.buf_addr = &msg;
+	bufs[0] = &buf;
+
+	rte_rawdev_enqueue_buffers(dev_id, bufs, 1, 0);
+}
+
 static __rte_always_inline struct cnxk_bphy_mem *
 rte_pmd_bphy_intr_mem_get(uint16_t dev_id)
 {
