git: 5603ddb8299d - main - ufshci: detect completions with the completion notification register
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 03 Nov 2026 04:33:41 UTC
The branch main has been updated by jaeyoon:
URL: https://cgit.FreeBSD.org/src/commit/?id=5603ddb8299d0fe3b8e81a5bee5cd1fa24a1806c
commit 5603ddb8299d0fe3b8e81a5bee5cd1fa24a1806c
Author: Jaeyoon Choi <jaeyoon@FreeBSD.org>
AuthorDate: 2026-11-03 04:11:14 +0000
Commit: Jaeyoon Choi <jaeyoon@FreeBSD.org>
CommitDate: 2026-11-03 04:29:41 +0000
ufshci: detect completions with the completion notification register
The single doorbell completion scan took a cleared doorbell bit as a
completion. A bit that was never set reads the same, so a slot whose
doorbell was not rung yet, or one that a reset wiped, looked complete
and was finished with a stale descriptor.
UFSHCI 3.0 added UTRLCNR, which the controller sets when it completes
a request and the driver already clears afterwards. Use it to find
completed slots on a 3.0 or later controller. Older controllers keep
the doorbell check, and the task management list has no such register.
Reviewed by: imp
Sponsored by: Samsung Electronics
Differential Revision: https://reviews.freebsd.org/D61111
---
sys/dev/ufshci/ufshci_private.h | 6 +++---
sys/dev/ufshci/ufshci_req_queue.c | 4 ++--
sys/dev/ufshci/ufshci_req_sdb.c | 22 +++++++++++++++-------
3 files changed, 20 insertions(+), 12 deletions(-)
diff --git a/sys/dev/ufshci/ufshci_private.h b/sys/dev/ufshci/ufshci_private.h
index 66cbe4a81bb5..d3a02f45f271 100644
--- a/sys/dev/ufshci/ufshci_private.h
+++ b/sys/dev/ufshci/ufshci_private.h
@@ -133,7 +133,7 @@ struct ufshci_qops {
struct ufshci_tracker **tr);
void (*ring_doorbell)(struct ufshci_controller *ctrlr,
struct ufshci_tracker *tr);
- bool (*is_doorbell_cleared)(struct ufshci_controller *ctrlr,
+ bool (*is_completed)(struct ufshci_controller *ctrlr,
uint8_t slot);
void (*clear_cpl_ntf)(struct ufshci_controller *ctrlr,
struct ufshci_tracker *tr);
@@ -532,9 +532,9 @@ void ufshci_req_sdb_utmr_ring_doorbell(struct ufshci_controller *ctrlr,
struct ufshci_tracker *tr);
void ufshci_req_sdb_utr_ring_doorbell(struct ufshci_controller *ctrlr,
struct ufshci_tracker *tr);
-bool ufshci_req_sdb_utmr_is_doorbell_cleared(struct ufshci_controller *ctrlr,
+bool ufshci_req_sdb_utmr_is_completed(struct ufshci_controller *ctrlr,
uint8_t slot);
-bool ufshci_req_sdb_utr_is_doorbell_cleared(struct ufshci_controller *ctrlr,
+bool ufshci_req_sdb_utr_is_completed(struct ufshci_controller *ctrlr,
uint8_t slot);
void ufshci_req_sdb_utmr_clear_cpl_ntf(struct ufshci_controller *ctrlr,
struct ufshci_tracker *tr);
diff --git a/sys/dev/ufshci/ufshci_req_queue.c b/sys/dev/ufshci/ufshci_req_queue.c
index f1b145f9791b..add399e98ed2 100644
--- a/sys/dev/ufshci/ufshci_req_queue.c
+++ b/sys/dev/ufshci/ufshci_req_queue.c
@@ -28,7 +28,7 @@ static const struct ufshci_qops sdb_utmr_qops = {
.reserve_slot = ufshci_req_sdb_reserve_slot,
.reserve_admin_slot = ufshci_req_sdb_reserve_slot,
.ring_doorbell = ufshci_req_sdb_utmr_ring_doorbell,
- .is_doorbell_cleared = ufshci_req_sdb_utmr_is_doorbell_cleared,
+ .is_completed = ufshci_req_sdb_utmr_is_completed,
.clear_cpl_ntf = ufshci_req_sdb_utmr_clear_cpl_ntf,
.process_cpl = ufshci_req_sdb_process_cpl,
.get_inflight_io = ufshci_req_sdb_get_inflight_io,
@@ -43,7 +43,7 @@ static const struct ufshci_qops sdb_utr_qops = {
.reserve_slot = ufshci_req_sdb_reserve_slot,
.reserve_admin_slot = ufshci_req_sdb_reserve_slot,
.ring_doorbell = ufshci_req_sdb_utr_ring_doorbell,
- .is_doorbell_cleared = ufshci_req_sdb_utr_is_doorbell_cleared,
+ .is_completed = ufshci_req_sdb_utr_is_completed,
.clear_cpl_ntf = ufshci_req_sdb_utr_clear_cpl_ntf,
.process_cpl = ufshci_req_sdb_process_cpl,
.get_inflight_io = ufshci_req_sdb_get_inflight_io,
diff --git a/sys/dev/ufshci/ufshci_req_sdb.c b/sys/dev/ufshci/ufshci_req_sdb.c
index b737a7081fd9..2404354bfade 100644
--- a/sys/dev/ufshci/ufshci_req_sdb.c
+++ b/sys/dev/ufshci/ufshci_req_sdb.c
@@ -551,21 +551,31 @@ ufshci_req_sdb_utr_ring_doorbell(struct ufshci_controller *ctrlr,
}
bool
-ufshci_req_sdb_utmr_is_doorbell_cleared(struct ufshci_controller *ctrlr,
+ufshci_req_sdb_utmr_is_completed(struct ufshci_controller *ctrlr,
uint8_t slot)
{
uint32_t utmrldbr;
+ /* The task management list has no completion notification. */
utmrldbr = ufshci_mmio_read_4(ctrlr, utmrldbr);
return (!(utmrldbr & (1 << slot)));
}
bool
-ufshci_req_sdb_utr_is_doorbell_cleared(struct ufshci_controller *ctrlr,
+ufshci_req_sdb_utr_is_completed(struct ufshci_controller *ctrlr,
uint8_t slot)
{
- uint32_t utrldbr;
+ uint32_t utrlcnr, utrldbr;
+ /*
+ * Since UFSHCI 3.0 the controller sets a notification bit when it
+ * completes a request. Before that, a cleared doorbell bit was the
+ * only sign, and a slot that was never rung looks the same.
+ */
+ if (ctrlr->major_version >= 3) {
+ utrlcnr = ufshci_mmio_read_4(ctrlr, utrlcnr);
+ return ((utrlcnr & (1 << slot)) != 0);
+ }
utrldbr = ufshci_mmio_read_4(ctrlr, utrldbr);
return (!(utrldbr & (1 << slot)));
}
@@ -592,15 +602,13 @@ ufshci_req_sdb_process_cpl(struct ufshci_req_queue *req_queue)
KASSERT(tr, ("there is no tracker assigned to the slot"));
/*
- * When the response is delivered from the device, the doorbell
- * is cleared. Check it under qlock so that a slot whose
+ * Check the completion under qlock so that a slot whose
* doorbell write is still in flight in the submit path is not
* mistaken for a completed one.
*/
mtx_lock(&hwq->qlock);
completed = tr->slot_state == UFSHCI_SLOT_STATE_SCHEDULED &&
- req_queue->qops.is_doorbell_cleared(req_queue->ctrlr,
- slot);
+ req_queue->qops.is_completed(req_queue->ctrlr, slot);
mtx_unlock(&hwq->qlock);
if (completed) {
ufshci_req_queue_complete_tracker(tr);