diff --git a/drivers/mailbox/arm_mhu_v2.c b/drivers/mailbox/arm_mhu_v2.c
index a0af683b83a2..0e3fa5917925 100644
--- a/drivers/mailbox/arm_mhu_v2.c
+++ b/drivers/mailbox/arm_mhu_v2.c
@@ -430,6 +430,111 @@ static const struct mhuv2_ops mhuv2_single_word_ops = {
 };
 /* ========================================================================== */
 
+/* =================== Doorbell transport protocol operations =============== */
+
+static inline int mhuv2_read_data_doorbell(struct arm_mhuv2 *mhuv2,
+					   struct mbox_chan *chan,
+					   struct arm_mbox_msg *msg)
+{
+	return 0;
+}
+
+static inline int mhuv2_clear_data_doorbell(struct arm_mhuv2 *mhuv2,
+					    struct mbox_chan *chan,
+					    struct arm_mbox_msg *msg)
+{
+	const u32 ch_mbox_idx = mhuv2_chan_idx(chan);
+	const u32 ch_window_idx = ch_mbox_idx / MHUV2_STAT_BITS;
+	const u32 ch_window_reg_idx = ch_mbox_idx % MHUV2_STAT_BITS;
+
+	writel_relaxed(BIT(ch_window_reg_idx),
+		       &mhuv2->reg.recv->channel[ch_window_idx].STAT_CLEAR);
+	return 0;
+}
+
+static inline int mhuv2_send_data_doorbell(struct arm_mhuv2 *mhuv2,
+					   struct mbox_chan *chan,
+					   const struct arm_mbox_msg *msg)
+{
+	const u32 ch_mbox_idx = mhuv2_chan_idx(chan);
+	const u32 ch_window_idx = ch_mbox_idx / MHUV2_STAT_BITS;
+	const u32 ch_window_reg_idx = ch_mbox_idx % MHUV2_STAT_BITS;
+
+	writel_relaxed(
+		readl_relaxed(&mhuv2->reg.send->channel[ch_window_idx].STAT) |
+			BIT(ch_window_reg_idx),
+		&mhuv2->reg.send->channel[ch_window_idx].STAT_SET);
+	return 0;
+}
+
+static inline int mhuv2_last_tx_done_doorbell(struct arm_mhuv2 *mhuv2,
+					      struct mbox_chan *chan)
+{
+	const u32 ch_mbox_idx = mhuv2_chan_idx(chan);
+	const u32 ch_window_idx = ch_mbox_idx / MHUV2_STAT_BITS;
+	const u32 ch_window_reg_idx = ch_mbox_idx % MHUV2_STAT_BITS;
+
+	return (readl_relaxed(&mhuv2->reg.send->channel[ch_window_idx].STAT) &
+		BIT(ch_window_reg_idx)) == 0;
+}
+
+static inline int mhuv2_setup_doorbell(struct arm_mhuv2 *mhuv2)
+{
+	int i;
+	const u32 channel_windows =
+		readl_relaxed_bitfield(mhuv2->frame == RECEIVER_FRAME ?
+					       &mhuv2->reg.recv->MHU_CFG :
+					       &mhuv2->reg.send->MHU_CFG,
+				       NUM_CH);
+
+	mhuv2->mbox.num_chans = MHUV2_STAT_BITS * channel_windows;
+	mhuv2->mbox.chans =
+		devm_kzalloc(mhuv2->dev,
+			     mhuv2->mbox.num_chans * sizeof(struct mbox_chan),
+			     GFP_KERNEL);
+
+	for (i = 0; i < mhuv2->mbox.num_chans; i++) {
+		mhuv2->mbox.chans[i].con_priv =
+			devm_kzalloc(mhuv2->dev,
+				     sizeof(struct arm_mhuv2_mbox_chan_priv),
+				     GFP_KERNEL);
+		mhuv2_chan_idx(&mhuv2->mbox.chans[i]) = i;
+	}
+
+	if (mhuv2->frame == RECEIVER_FRAME) {
+		/* Ensure all status registers are unmasked */
+		for (i = 0; i < channel_windows; i++) {
+			writel_relaxed(0x0,
+				       &mhuv2->reg.recv->channel[i].MASK_SET);
+		}
+	}
+
+	return 0;
+}
+
+static inline struct mbox_chan *
+	mhuv2_get_active_mbox_chan_doorbell(struct arm_mhuv2 *mhuv2)
+{
+	const u32 ch_window_idx = mhuv2_combint_idx(mhuv2);
+	const u32 ch_window_reg_idx = __find_set_bit(
+		readl_relaxed(&mhuv2->reg.recv->channel[ch_window_idx].STAT));
+	if (ch_window_reg_idx == -1)
+		return ERR_PTR(-EIO);
+
+	return &mhuv2->mbox.chans[ch_window_reg_idx +
+				  ch_window_idx * MHUV2_STAT_BITS];
+}
+
+static const struct mhuv2_ops mhuv2_doorbell_ops = {
+	.read_data = mhuv2_read_data_doorbell,
+	.clear_data = mhuv2_clear_data_doorbell,
+	.send_data = mhuv2_send_data_doorbell,
+	.setup = mhuv2_setup_doorbell,
+	.last_tx_done = mhuv2_last_tx_done_doorbell,
+	.get_active_mbox_chan = mhuv2_get_active_mbox_chan_doorbell,
+};
+/* ========================================================================== */
+
 /*
  * MHUv2 receiver interrupt service routine
  *
@@ -638,6 +743,9 @@ static int mhuv2_probe(struct amba_device *adev, const struct amba_id *id)
 	case SINGLE_WORD:
 		mhuv2->ops = &mhuv2_single_word_ops;
 		break;
+	case DOORBELL:
+		mhuv2->ops = &mhuv2_doorbell_ops;
+		break;
 	default:
 		return -ENODEV;
 	}
