#include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/dmi.h>
+#include <linux/delay.h>
 
 #include "nhi.h"
 #include "nhi_regs.h"
 #define MSIX_MIN_VECS          6
 #define MSIX_MAX_VECS          16
 
+#define NHI_MAILBOX_TIMEOUT    500 /* ms */
+
 static int ring_interrupt_index(struct tb_ring *ring)
 {
        int bit = ring->hop;
        kfree(ring);
 }
 
+/**
+ * nhi_mailbox_cmd() - Send a command through NHI mailbox
+ * @nhi: Pointer to the NHI structure
+ * @cmd: Command to send
+ * @data: Data to be send with the command
+ *
+ * Sends mailbox command to the firmware running on NHI. Returns %0 in
+ * case of success and negative errno in case of failure.
+ */
+int nhi_mailbox_cmd(struct tb_nhi *nhi, enum nhi_mailbox_cmd cmd, u32 data)
+{
+       ktime_t timeout;
+       u32 val;
+
+       iowrite32(data, nhi->iobase + REG_INMAIL_DATA);
+
+       val = ioread32(nhi->iobase + REG_INMAIL_CMD);
+       val &= ~(REG_INMAIL_CMD_MASK | REG_INMAIL_ERROR);
+       val |= REG_INMAIL_OP_REQUEST | cmd;
+       iowrite32(val, nhi->iobase + REG_INMAIL_CMD);
+
+       timeout = ktime_add_ms(ktime_get(), NHI_MAILBOX_TIMEOUT);
+       do {
+               val = ioread32(nhi->iobase + REG_INMAIL_CMD);
+               if (!(val & REG_INMAIL_OP_REQUEST))
+                       break;
+               usleep_range(10, 20);
+       } while (ktime_before(ktime_get(), timeout));
+
+       if (val & REG_INMAIL_OP_REQUEST)
+               return -ETIMEDOUT;
+       if (val & REG_INMAIL_ERROR)
+               return -EIO;
+
+       return 0;
+}
+
+/**
+ * nhi_mailbox_mode() - Return current firmware operation mode
+ * @nhi: Pointer to the NHI structure
+ *
+ * The function reads current firmware operation mode using NHI mailbox
+ * registers and returns it to the caller.
+ */
+enum nhi_fw_mode nhi_mailbox_mode(struct tb_nhi *nhi)
+{
+       u32 val;
+
+       val = ioread32(nhi->iobase + REG_OUTMAIL_CMD);
+       val &= REG_OUTMAIL_CMD_OPMODE_MASK;
+       val >>= REG_OUTMAIL_CMD_OPMODE_SHIFT;
+
+       return (enum nhi_fw_mode)val;
+}
+
 static void nhi_interrupt_work(struct work_struct *work)
 {
        struct tb_nhi *nhi = container_of(work, typeof(*nhi), interrupt_work);
 
        return __ring_enqueue(ring, frame);
 }
 
+enum nhi_fw_mode {
+       NHI_FW_SAFE_MODE,
+       NHI_FW_AUTH_MODE,
+       NHI_FW_EP_MODE,
+       NHI_FW_CM_MODE,
+};
+
+enum nhi_mailbox_cmd {
+       NHI_MAILBOX_SAVE_DEVS = 0x05,
+       NHI_MAILBOX_DRV_UNLOADS = 0x07,
+       NHI_MAILBOX_ALLOW_ALL_DEVS = 0x23,
+};
+
+int nhi_mailbox_cmd(struct tb_nhi *nhi, enum nhi_mailbox_cmd cmd, u32 data);
+enum nhi_fw_mode nhi_mailbox_mode(struct tb_nhi *nhi);
+
 /*
  * PCI IDs used in this driver from Win Ridge forward. There is no
  * need for the PCI quirk anymore as we will use ICM also on Apple
 
 #define REG_DMA_MISC                   0x39864
 #define REG_DMA_MISC_INT_AUTO_CLEAR     BIT(2)
 
+#define REG_INMAIL_DATA                        0x39900
+
+#define REG_INMAIL_CMD                 0x39904
+#define REG_INMAIL_CMD_MASK            GENMASK(7, 0)
+#define REG_INMAIL_ERROR               BIT(30)
+#define REG_INMAIL_OP_REQUEST          BIT(31)
+
+#define REG_OUTMAIL_CMD                        0x3990c
+#define REG_OUTMAIL_CMD_OPMODE_SHIFT   8
+#define REG_OUTMAIL_CMD_OPMODE_MASK    GENMASK(11, 8)
+
 #endif