dev-arm: drain implementation for SMMUv3

SMMUv3 is drained when (1) no SMMU translations are pending
on any of its slave interfaces and (2) no commands are stored
in the Command Queue waiting to be processed.

Change-Id: I81cef5fd821fa5e509e130af02aece5239493df5
Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/19309
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
diff --git a/src/dev/arm/smmu_v3.cc b/src/dev/arm/smmu_v3.cc
index 2517649..d913d55 100644
--- a/src/dev/arm/smmu_v3.cc
+++ b/src/dev/arm/smmu_v3.cc
@@ -741,7 +741,11 @@
 DrainState
 SMMUv3::drain()
 {
-    panic("SMMUv3 doesn't support draining\n");
+    // Wait until the Command Executor is not busy
+    if (commandExecutor.isBusy()) {
+        return DrainState::Draining;
+    }
+    return DrainState::Drained;
 }
 
 void
diff --git a/src/dev/arm/smmu_v3_cmdexec.cc b/src/dev/arm/smmu_v3_cmdexec.cc
index 8660846..494c867 100644
--- a/src/dev/arm/smmu_v3_cmdexec.cc
+++ b/src/dev/arm/smmu_v3_cmdexec.cc
@@ -75,6 +75,8 @@
         }
 
         busy = false;
+        // No more commands to process, signal the SMMU as drained
+        smmu.signalDrainDone();
 
         doSleep(yield);
     }
diff --git a/src/dev/arm/smmu_v3_slaveifc.cc b/src/dev/arm/smmu_v3_slaveifc.cc
index 48b1141..0a53ed0 100644
--- a/src/dev/arm/smmu_v3_slaveifc.cc
+++ b/src/dev/arm/smmu_v3_slaveifc.cc
@@ -253,6 +253,16 @@
     }
 }
 
+DrainState
+SMMUv3SlaveInterface::drain()
+{
+    // Wait until all SMMU translations are completed
+    if (xlateSlotsRemaining < params()->xlate_slots) {
+        return DrainState::Draining;
+    }
+    return DrainState::Drained;
+}
+
 SMMUv3SlaveInterface*
 SMMUv3SlaveInterfaceParams::create()
 {
diff --git a/src/dev/arm/smmu_v3_slaveifc.hh b/src/dev/arm/smmu_v3_slaveifc.hh
index a782ff9..3302d82 100644
--- a/src/dev/arm/smmu_v3_slaveifc.hh
+++ b/src/dev/arm/smmu_v3_slaveifc.hh
@@ -56,6 +56,9 @@
 
 class SMMUv3SlaveInterface : public MemObject
 {
+  protected:
+    friend class SMMUTranslationProcess;
+
   public:
     SMMUv3 *smmu;
     SMMUTLB* microTLB;
@@ -124,6 +127,14 @@
         delete mainTLB;
     }
 
+    const SMMUv3SlaveInterfaceParams *
+    params() const
+    {
+        return static_cast<const SMMUv3SlaveInterfaceParams *>(_params);
+    }
+
+    DrainState drain() override;
+
     void setSMMU(SMMUv3 *_smmu) { smmu = _smmu; }
     void sendRange();
 };
diff --git a/src/dev/arm/smmu_v3_transl.cc b/src/dev/arm/smmu_v3_transl.cc
index 6e0dacb..f1e1fb1 100644
--- a/src/dev/arm/smmu_v3_transl.cc
+++ b/src/dev/arm/smmu_v3_transl.cc
@@ -94,6 +94,11 @@
 {
     // Increase number of pending translation slots on the slave interface
     ifc.xlateSlotsRemaining++;
+    // If no more SMMU translations are pending (all slots available),
+    // signal SMMU Slave Interface as drained
+    if (ifc.xlateSlotsRemaining == ifc.params()->xlate_slots) {
+        ifc.signalDrainDone();
+    }
 }
 
 void