hw/ufs: Add idle operation
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> Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com>
Jaemyung Lee committed
May 14, 2026 at 17:10 UTC
9b43508b10469f6cd5dad3223d1cdcf2b448d5c9
2 files changed
+73
hw/ufs/ufs.c
+71
@@ -1870,6 +1870,71 @@ static void ufs_sendback_req(void *opaque)
1870
ufs_irq_check(u);
1871
}
1872
1873
+static void ufs_process_idle(UfsHc *u)
1874
+{
1875
+ /* Currently do nothing */
1876
+ return;
1877
+}
1878
+
1879
+static inline bool ufs_check_idle(UfsHc *u)
1880
+{
1881
+ return !u->reg.utrldbr;
1882
+}
1883
+
1884
+static inline bool ufs_mcq_check_idle(UfsHc *u)
1885
+{
1886
+ if (!u->params.mcq) {
1887
+ return true;
1888
+ }
1889
+
1890
+ for (int qid = 0; qid < ARRAY_SIZE(u->sq); qid++) {
1891
+ if (!u->sq[qid]) {
1892
+ continue;
1893
+ }
1894
+
1895
+ if (!ufs_mcq_sq_empty(u, qid)) {
1896
+ return false;
1897
+ }
1898
+
1899
+ /* internal ongoing MCQ request check */
1900
+ UfsSq *sq = u->sq[qid];
1901
+ for (int i = 0; i < sq->size; i++) {
1902
+ if (sq->req[i].state != UFS_REQUEST_IDLE) {
1903
+ return false;
1904
+ }
1905
+ }
1906
+ }
1907
+
1908
+ for (int qid = 0; qid < ARRAY_SIZE(u->cq); qid++) {
1909
+ if (!u->cq[qid]) {
1910
+ continue;
1911
+ }
1912
+
1913
+ if (!ufs_mcq_cq_empty(u, qid)) {
1914
+ return false;
1915
+ }
1916
+
1917
+ if (!QTAILQ_EMPTY(&u->cq[qid]->req_list)) {
1918
+ return false;
1919
+ }
1920
+ }
1921
+
1922
+ return true;
1923
+}
1924
+
1925
+#define UFS_IDLE_TIMER_TICK 100 /* 0.1s */
1926
+static void ufs_idle_timer_cb(void *opaque)
1927
+{
1928
+ UfsHc *u = opaque;
1929
+ int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT);
1930
+
1931
+ if (ufs_check_idle(u) && ufs_mcq_check_idle(u)) {
1932
+ ufs_process_idle(u);
1933
+ }
1934
+
1935
+ timer_mod(&u->idle_timer, now + UFS_IDLE_TIMER_TICK);
1936
+}
1937
+
1938
static bool ufs_check_constraints(UfsHc *u, Error **errp)
1939
{
1940
if (u->params.nutrs > UFS_MAX_NUTRS) {
@@ -1932,6 +1997,7 @@ static void ufs_init_hc(UfsHc *u)
1997
uint32_t cap = 0;
1998
uint32_t mcqconfig = 0;
1999
uint32_t mcqcap = 0;
2000
+ int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT);
2001
2002
u->reg_size = pow2ceil(ufs_reg_size(u));
2003
@@ -2028,6 +2094,9 @@ static void ufs_init_hc(UfsHc *u)
2094
* dynamically
2095
*/
2096
u->temperature = UFS_TEMPERATURE;
2097
+
2098
+ timer_init_ms(&u->idle_timer, QEMU_CLOCK_VIRTUAL_RT, ufs_idle_timer_cb, u);
2099
+ timer_mod(&u->idle_timer, now + UFS_IDLE_TIMER_TICK);
2100
}
2101
2102
static void ufs_realize(PCIDevice *pci_dev, Error **errp)
@@ -2055,6 +2124,8 @@ static void ufs_exit(PCIDevice *pci_dev)
2124
{
2125
UfsHc *u = UFS(pci_dev);
2126
2127
+ timer_del(&u->idle_timer);
2128
+
2129
qemu_free_irq(u->irq);
2130
2131
qemu_bh_delete(u->doorbell_bh);
hw/ufs/ufs.h
+2
@@ -148,6 +148,8 @@ typedef struct UfsHc {
148
UfsCq *cq[UFS_MAX_MCQ_QNUM];
149
150
uint8_t temperature;
151
+
152
+ QEMUTimer idle_timer;
153
} UfsHc;
154
155
static inline uint32_t ufs_mcq_sq_tail(UfsHc *u, uint32_t qid)