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) {
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));
* 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)
{
UfsHc *u = UFS(pci_dev);
+ timer_del(&u->idle_timer);
+
qemu_free_irq(u->irq);
qemu_bh_delete(u->doorbell_bh);