[PATCH RESEND 3/5] hw/ufs: Add IDLE operation

Jaemyung Lee posted 5 patches 13 hours ago
[PATCH RESEND 3/5] hw/ufs: Add IDLE operation
Posted by Jaemyung Lee 13 hours ago
Add timer-based idle behaviour to UFS Device.

When no I/O occurs, the UFS Device performs various internal operations.
To emulate this, adds a timer that periodically checks the current I/O
status of the device and call the ufs_process_idle() function when idle.

Signed-off-by: Jaemyung Lee <jaemyung.lee@samsung.com>
---
 hw/ufs/ufs.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 hw/ufs/ufs.h |  3 +++
 2 files changed, 49 insertions(+)

diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c
index 69f82ab462..19438c89e1 100644
--- a/hw/ufs/ufs.c
+++ b/hw/ufs/ufs.c
@@ -1801,6 +1801,46 @@ static void ufs_sendback_req(void *opaque)
     ufs_irq_check(u);
 }

+/* IDLE */
+static void ufs_process_idle(UfsHc *u)
+{
+    /* Currently do nothing */
+    return;
+}
+
+static inline bool ufs_check_idle(UfsHc *u)
+{
+    return !u->reg.utrldbr;
+}
+
+static inline bool ufs_mcq_check_idle(UfsHc *u)
+{
+    for (int i = 0; i < ARRAY_SIZE(u->sq); i++) {
+        if (!ufs_mcq_sq_empty(u, i)) {
+            return false;
+        }
+    }
+
+    for (int i = 0; i < ARRAY_SIZE(u->cq); i++) {
+        if (!ufs_mcq_cq_empty(u, i)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+#define UFS_IDLE_TIMER_TICK 100 /* 0.1s */
+static void ufs_idle_timer_cb(void *opaque)
+{
+    UfsHc *u = opaque;
+    int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
+
+    if (ufs_check_idle(u) && ufs_mcq_check_idle(u)) {
+        ufs_process_idle(u);
+    }
+
+    timer_mod(&u->idle_timer, now + UFS_IDLE_TIMER_TICK);
+}
+
 static bool ufs_check_constraints(UfsHc *u, Error **errp)
 {
     if (u->params.nutrs > UFS_MAX_NUTRS) {
@@ -1863,6 +1903,7 @@ static void ufs_init_hc(UfsHc *u)
     uint32_t cap = 0;
     uint32_t mcqconfig = 0;
     uint32_t mcqcap = 0;
+    int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);

     u->reg_size = pow2ceil(ufs_reg_size(u));

@@ -1959,6 +2000,9 @@ static void ufs_init_hc(UfsHc *u)
      * dynamically
      */
     u->temperature = UFS_TEMPERATURE;
+
+    timer_init_ms(&u->idle_timer, QEMU_CLOCK_VIRTUAL, ufs_idle_timer_cb, u);
+    timer_mod(&u->idle_timer, now + UFS_IDLE_TIMER_TICK);
 }

 static void ufs_realize(PCIDevice *pci_dev, Error **errp)
@@ -1986,6 +2030,8 @@ static void ufs_exit(PCIDevice *pci_dev)
 {
     UfsHc *u = UFS(pci_dev);

+    timer_del(&u->idle_timer);
+
     qemu_free_irq(u->irq);

     qemu_bh_delete(u->doorbell_bh);
diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h
index 13d964c5ae..f0b1f8b0e0 100644
--- a/hw/ufs/ufs.h
+++ b/hw/ufs/ufs.h
@@ -148,6 +148,9 @@ typedef struct UfsHc {
     UfsCq *cq[UFS_MAX_MCQ_QNUM];

     uint8_t temperature;
+
+    /* IDLE timer */
+    QEMUTimer idle_timer;
 } UfsHc;

 static inline uint32_t ufs_mcq_sq_tail(UfsHc *u, uint32_t qid)
--
2.34.1