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 <[email protected]> --- hw/ufs/ufs.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/ufs/ufs.h | 2 ++ 2 files changed, 73 insertions(+) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 69f82ab462d135fe6cda479891f9d8f26d19be9a..c504a0d7e3fb89eb5558cfd9c410cda9396bc3b4 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -1801,6 +1801,71 @@ static void ufs_sendback_req(void *opaque) ufs_irq_check(u); } +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) +{ + if (!u->params.mcq) { + return true; + } + + for (int qid = 0; qid < ARRAY_SIZE(u->sq); qid++) { + if (!u->sq[qid]) { + continue; + } + + if (!ufs_mcq_sq_empty(u, qid)) { + return false; + } + + /* internal ongoing MCQ request check */ + UfsSq *sq = u->sq[qid]; + for (int i = 0; i < sq->size; i++) { + if (sq->req[i].state != UFS_REQUEST_IDLE) { + return false; + } + } + } + + for (int qid = 0; qid < ARRAY_SIZE(u->cq); qid++) { + if (!u->cq[qid]) { + continue; + } + + if (!ufs_mcq_cq_empty(u, qid)) { + return false; + } + + if (!QTAILQ_EMPTY(&u->cq[qid]->req_list)) { + 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_RT); + + 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 +1928,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_RT); u->reg_size = pow2ceil(ufs_reg_size(u)); @@ -1959,6 +2025,9 @@ static void ufs_init_hc(UfsHc *u) * dynamically */ u->temperature = UFS_TEMPERATURE; + + timer_init_ms(&u->idle_timer, QEMU_CLOCK_VIRTUAL_RT, 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 +2055,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 13d964c5ae5ec430a98b2ef71987cb9279e9a317..b5f040302129f4d02732ddd20ef82eb33c41922a 100644 --- a/hw/ufs/ufs.h +++ b/hw/ufs/ufs.h @@ -148,6 +148,8 @@ typedef struct UfsHc { UfsCq *cq[UFS_MAX_MCQ_QNUM]; uint8_t temperature; + + QEMUTimer idle_timer; } UfsHc; static inline uint32_t ufs_mcq_sq_tail(UfsHc *u, uint32_t qid) -- 2.48.1
