#define CTRL_MBOX_MAX_PF       128
 #define CTRL_MBOX_SZ           ((size_t)(0x400000 / CTRL_MBOX_MAX_PF))
 
+#define FW_HB_INTERVAL_IN_SECS         1
+#define FW_HB_MISS_COUNT               10
+
 /* Names of Hardware non-queue generic interrupts */
 static char *cn93_non_ioq_msix_names[] = {
        "epf_ire_rint",
        conf->ctrl_mbox_cfg.barmem_addr = (void __iomem *)oct->mmio[2].hw_addr +
                                           (0x400000ull * 7) +
                                           (link * CTRL_MBOX_SZ);
+
+       conf->hb_interval = FW_HB_INTERVAL_IN_SECS;
+       conf->max_hb_miss_cnt = FW_HB_MISS_COUNT;
+
 }
 
 /* Setup registers for a hardware Tx Queue  */
                octep_write_csr64(oct, CN93_SDP_EPF_OEI_RINT, reg0);
                if (reg0 & CN93_SDP_EPF_OEI_RINT_DATA_BIT_MBOX)
                        queue_work(octep_wq, &oct->ctrl_mbox_task);
+               else if (reg0 & CN93_SDP_EPF_OEI_RINT_DATA_BIT_HBEAT)
+                       atomic_set(&oct->hb_miss_cnt, 0);
 
                handled = true;
        }
 
                           msecs_to_jiffies(OCTEP_INTR_POLL_TIME_MSECS));
 }
 
+/**
+ * octep_hb_timeout_task - work queue task to check firmware heartbeat.
+ *
+ * @work: pointer to hb work_struct
+ *
+ * Check for heartbeat miss count. Uninitialize oct device if miss count
+ * exceeds configured max heartbeat miss count.
+ *
+ **/
+static void octep_hb_timeout_task(struct work_struct *work)
+{
+       struct octep_device *oct = container_of(work, struct octep_device,
+                                               hb_task.work);
+
+       int miss_cnt;
+
+       miss_cnt = atomic_inc_return(&oct->hb_miss_cnt);
+       if (miss_cnt < oct->conf->max_hb_miss_cnt) {
+               queue_delayed_work(octep_wq, &oct->hb_task,
+                                  msecs_to_jiffies(oct->conf->hb_interval * 1000));
+               return;
+       }
+
+       dev_err(&oct->pdev->dev, "Missed %u heartbeats. Uninitializing\n",
+               miss_cnt);
+       rtnl_lock();
+       if (netif_running(oct->netdev))
+               octep_stop(oct->netdev);
+       rtnl_unlock();
+}
+
 /**
  * octep_ctrl_mbox_task - work queue task to handle ctrl mbox messages.
  *
 int octep_device_setup(struct octep_device *oct)
 {
        struct pci_dev *pdev = oct->pdev;
-       int i;
+       int i, ret;
 
        /* allocate memory for oct->conf */
        oct->conf = kzalloc(sizeof(*oct->conf), GFP_KERNEL);
 
        oct->pkind = CFG_GET_IQ_PKIND(oct->conf);
 
-       return octep_ctrl_net_init(oct);
+       ret = octep_ctrl_net_init(oct);
+       if (ret)
+               return ret;
+
+       atomic_set(&oct->hb_miss_cnt, 0);
+       INIT_DELAYED_WORK(&oct->hb_task, octep_hb_timeout_task);
+       queue_delayed_work(octep_wq, &oct->hb_task,
+                          msecs_to_jiffies(oct->conf->hb_interval * 1000));
+       return 0;
 
 unsupported_dev:
        for (i = 0; i < OCTEP_MMIO_REGIONS; i++)
        }
 
        octep_ctrl_net_uninit(oct);
+       cancel_delayed_work_sync(&oct->hb_task);
 
        oct->hw_ops.soft_reset(oct);
        for (i = 0; i < OCTEP_MMIO_REGIONS; i++) {
 
        bool poll_non_ioq_intr;
        /* Work entry to poll non-ioq interrupts */
        struct delayed_work intr_poll_task;
+
+       /* Firmware heartbeat timer */
+       struct timer_list hb_timer;
+       /* Firmware heartbeat miss count tracked by timer */
+       atomic_t hb_miss_cnt;
+       /* Task to reset device on heartbeat miss */
+       struct delayed_work hb_task;
 };
 
 static inline u16 OCTEP_MAJOR_REV(struct octep_device *oct)