All of lore.kernel.org
 help / color / mirror / Atom feed
From: Stanley Jhu <stanleyjhu@google.com>
To: Jeuk Kim <jeuk20.kim@samsung.com>, qemu-devel@nongnu.org
Cc: Brian Kao <powenkao@google.com>, Stanley Jhu <stanleyjhu@google.com>
Subject: [PATCH v2 2/3] hw/ufs: Support MCQ runtime interrupt and queue status registers
Date: Wed,  9 Sep 2026 11:11:45 +0800	[thread overview]
Message-ID: <20260909031146.1646684-3-stanleyjhu@google.com> (raw)
In-Reply-To: <20260909031146.1646684-1-stanleyjhu@google.com>

According to JEDEC UFSHCI 5.2.1 and 5.6, Multi-Circular Queue (MCQ)
architecture provides per-queue runtime control and interrupt registers.
When Linux initializes MCQ via ufshcd_mcq_make_queues_operational(),
it configures operational registers (SQnRTC, SQnCTI, SQnIS/IE, CQnIS/IE,
CQnIACR). Currently, QEMU treats these offsets as unhandled, generating
"invalid register offset" warnings and failing queue lifecycle transitions.

Implement MCQ runtime operational register handling:
- Support SQ run-time control (SQnRTC) to start, stop, and clean up submission
  queues, updating run-time status (SQnRTS) and synchronizing with the SQ
  processing bottom half.
- Support per-queue interrupt status and enable registers (SQnIS/IE, CQnIS/IE).
  Implement write-1-to-clear semantics and dynamically synchronize the global
  CQES (CQ Event Status) bit in IS to prevent interrupt storms.
- Store queue configuration for interrupt aggregation (CQnIACR) and completion
  timeout intervals (SQnCTI).
- Enforce controller reset state machine rules: guard MMIO reads when HCE=0,
  and reinitialize operational registers across HCE resets while preserving
  MCQ capability configurations.

Signed-off-by: Stanley Jhu <stanleyjhu@google.com>
---
 hw/ufs/trace-events |  1 +
 hw/ufs/ufs.c        | 73 +++++++++++++++++++++++++++++++++++++++++++--
 include/block/ufs.h |  9 ++++++
 3 files changed, 81 insertions(+), 2 deletions(-)

diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events
index 922293355b..d8173b12b6 100644
--- a/hw/ufs/trace-events
+++ b/hw/ufs/trace-events
@@ -14,6 +14,7 @@ ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32
 ufs_mcq_complete_req(uint8_t qid) "sqid %"PRIu8""
 ufs_mcq_create_sq(uint8_t sqid, uint8_t cqid, uint64_t addr, uint16_t size) "mcq create sq sqid %"PRIu8", cqid %"PRIu8", addr 0x%"PRIx64", size %"PRIu16""
 ufs_mcq_create_cq(uint8_t cqid, uint64_t addr, uint16_t size) "mcq create cq cqid %"PRIu8", addr 0x%"PRIx64", size %"PRIu16""
+ufs_write_mcq_op_reg(uint8_t qid, uint32_t offset, uint32_t data) "qid %"PRIu8", offset 0x%"PRIx32", data 0x%"PRIx32""
 ufs_hce_reset(void) "HCE 1 -> 0 reset: cancelling BHs, resetting MCQ and request lists"
 
 # error condition
diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c
index adae6639e1..4e22c31f89 100644
--- a/hw/ufs/ufs.c
+++ b/hw/ufs/ufs.c
@@ -446,13 +446,14 @@ static void ufs_mcq_process_sq(void *opaque)
 {
     UfsSq *sq = opaque;
     UfsHc *u = sq->u;
+    UfsMcqOpReg *opr = &u->mcq_op_reg[sq->sqid];
     UfsSqEntry sqe;
     UfsRequest *req;
     hwaddr addr;
     uint16_t head = ufs_mcq_sq_head(u, sq->sqid);
     int err;
 
-    if (u->resetting) {
+    if (u->resetting || FIELD_EX32(opr->sq.rts, SQRTS, STS)) {
         return;
     }
 
@@ -770,7 +771,17 @@ static void ufs_hce_reset(UfsHc *u)
     u->reg.utmrldbr = 0;
     u->reg.utrlcnr = 0;
     u->reg.utrlrsr = 0;
+    u->reg.utriacr = 0;
+    u->reg.utrlclr = 0;
+    if (u->params.mcq) {
+        u->reg.mcqconfig = FIELD_DP32(0, MCQCONFIG, MAC, 0x1f);
+    } else {
+        u->reg.mcqconfig = 0;
+    }
+    u->reg.ie = 0;
     u->reg.is = 0;
+    u->reg.utrlba = 0;
+    u->reg.utrlbau = 0;
 
     /* 4. Free MCQ Queues and reset MCQ dynamic registers */
     if (u->params.mcq) {
@@ -983,6 +994,9 @@ static void ufs_write_mcq_op_reg(UfsHc *u, hwaddr offset, uint32_t data,
 
     opr = &u->mcq_op_reg[qid];
 
+    trace_ufs_write_mcq_op_reg(qid, (uint32_t)(offset % sizeof(UfsMcqOpReg)),
+                               data);
+
     switch (offset % sizeof(UfsMcqOpReg)) {
     case offsetof(UfsMcqOpReg, sq.tp):
         if (opr->sq.tp != data) {
@@ -990,6 +1004,38 @@ static void ufs_write_mcq_op_reg(UfsHc *u, hwaddr offset, uint32_t data,
         }
         opr->sq.tp = data;
         break;
+    case offsetof(UfsMcqOpReg, sq.rtc):
+        opr->sq.rtc = data;
+        if (FIELD_EX32(data, SQRTC, ICU)) {
+            /* SQ_ICU: Initiate Cleanup (SQ_CUS = 1, RTC = 0) */
+            opr->sq.rts = FIELD_DP32(opr->sq.rts, SQRTS, CUS, 1);
+            opr->sq.rts = FIELD_DP32(opr->sq.rts, SQRTS, RTC, 0);
+        }
+        if (FIELD_EX32(data, SQRTC, STOP)) {
+            /* SQ_STOP: Stop queue */
+            opr->sq.rts = FIELD_DP32(opr->sq.rts, SQRTS, STS, 1);
+            if (u->sq[qid] && u->sq[qid]->bh) {
+                qemu_bh_cancel(u->sq[qid]->bh);
+            }
+        } else {
+            /* SQ_START: Start queue */
+            opr->sq.rts = FIELD_DP32(opr->sq.rts, SQRTS, STS, 0);
+            opr->sq.rts = FIELD_DP32(opr->sq.rts, SQRTS, CUS, 0);
+            if (u->sq[qid] && u->sq[qid]->bh) {
+                qemu_bh_schedule(u->sq[qid]->bh);
+            }
+        }
+        break;
+    case offsetof(UfsMcqOpReg, sq.cti):
+        opr->sq.cti = data;
+        break;
+    case offsetof(UfsMcqOpReg, sq_int.is):
+        opr->sq_int.is &= ~data;
+        ufs_irq_check(u);
+        break;
+    case offsetof(UfsMcqOpReg, sq_int.ie):
+        opr->sq_int.ie = data;
+        break;
     case offsetof(UfsMcqOpReg, cq.hp): {
         UfsCq *cq = u->cq[qid];
 
@@ -1006,8 +1052,27 @@ static void ufs_write_mcq_op_reg(UfsHc *u, hwaddr offset, uint32_t data,
         ufs_mcq_update_cq_head(u, qid, data);
         break;
     }
-    case offsetof(UfsMcqOpReg, cq_int.is):
+    case offsetof(UfsMcqOpReg, cq_int.is): {
+        bool pending = false;
+
         opr->cq_int.is &= ~data;
+        for (int i = 0; i < ARRAY_SIZE(u->mcq_op_reg); i++) {
+            if (u->mcq_op_reg[i].cq_int.is) {
+                pending = true;
+                break;
+            }
+        }
+        if (!pending) {
+            u->reg.is = FIELD_DP32(u->reg.is, IS, CQES, 0);
+        }
+        ufs_irq_check(u);
+        break;
+    }
+    case offsetof(UfsMcqOpReg, cq_int.ie):
+        opr->cq_int.ie = data;
+        break;
+    case offsetof(UfsMcqOpReg, cq_int.iacr):
+        opr->cq_int.iacr = data;
         break;
     default:
         trace_ufs_err_invalid_register_offset(offset);
@@ -1029,6 +1094,10 @@ static uint64_t ufs_mmio_read(void *opaque, hwaddr addr, unsigned size)
         offset = addr - ufs_mcq_reg_addr(u, 0);
         ptr = (uint32_t *)&u->mcq_reg;
     } else if (ufs_is_mcq_op_reg(u, addr, size)) {
+        if (!FIELD_EX32(u->reg.hce, HCE, HCE)) {
+            trace_ufs_err_invalid_register_offset(addr);
+            return 0xffffffff;
+        }
         offset = addr - ufs_mcq_op_reg_addr(u, 0);
         ptr = (uint32_t *)&u->mcq_op_reg;
     } else {
diff --git a/include/block/ufs.h b/include/block/ufs.h
index d19b3c65ef..00591aa755 100644
--- a/include/block/ufs.h
+++ b/include/block/ufs.h
@@ -224,6 +224,15 @@ typedef struct QEMU_PACKED UfsMcqSqReg {
     uint32_t rts;
 } UfsMcqSqReg;
 
+REG32(SQRTC, offsetof(UfsMcqSqReg, rtc))
+    FIELD(SQRTC, STOP, 0, 1)
+    FIELD(SQRTC, ICU, 1, 1)
+
+REG32(SQRTS, offsetof(UfsMcqSqReg, rts))
+    FIELD(SQRTS, STS, 0, 1)
+    FIELD(SQRTS, CUS, 1, 1)
+    FIELD(SQRTS, RTC, 4, 4)
+
 typedef struct QEMU_PACKED UfsMcqCqReg {
     uint32_t hp;
     uint32_t tp;
-- 
2.55.0.1007.g17ff1f9808-goog



  parent reply	other threads:[~2026-09-09  3:12 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-09  3:11 [PATCH v2 0/3] hw/ufs: Support Task Management Request (TMR) and MCQ status registers Stanley Jhu
2026-09-09  3:11 ` [PATCH v2 1/3] hw/ufs: Track SCSIRequest and cancel pending requests in ufs_clear_req Stanley Jhu
2026-09-23  2:03   ` Jeuk Kim
2026-09-23 14:45     ` Stanley Jhu
2026-09-09  3:11 ` Stanley Jhu [this message]
2026-09-23  2:03   ` [PATCH v2 2/3] hw/ufs: Support MCQ runtime interrupt and queue status registers Jeuk Kim
2026-09-23 14:45     ` Stanley Jhu
2026-09-09  3:11 ` [PATCH v2 3/3] hw/ufs: Implement Task Management Request (TMR) handling Stanley Jhu
2026-09-23  2:03   ` Jeuk Kim
2026-09-23 14:45     ` Stanley Jhu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260909031146.1646684-3-stanleyjhu@google.com \
    --to=stanleyjhu@google.com \
    --cc=jeuk20.kim@samsung.com \
    --cc=powenkao@google.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.