@samitouri / QOSamiQemu / commits / 9b43508b10

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)