diff options
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/sdmmc/ld_sdmmc.c | 62 |
1 files changed, 57 insertions, 5 deletions
diff --git a/sys/dev/sdmmc/ld_sdmmc.c b/sys/dev/sdmmc/ld_sdmmc.c index e6367d479a1..fb56783048e 100644 --- a/sys/dev/sdmmc/ld_sdmmc.c +++ b/sys/dev/sdmmc/ld_sdmmc.c @@ -1,4 +1,4 @@ -/* $NetBSD: ld_sdmmc.c,v 1.34 2017/08/20 15:58:43 mlelstv Exp $ */ +/* $NetBSD: ld_sdmmc.c,v 1.35 2018/11/09 14:39:19 jmcneill Exp $ */ /* * Copyright (c) 2008 KIYOHARA Takashi @@ -28,7 +28,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ld_sdmmc.c,v 1.34 2017/08/20 15:58:43 mlelstv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ld_sdmmc.c,v 1.35 2018/11/09 14:39:19 jmcneill Exp $"); #ifdef _KERNEL_OPT #include "opt_sdmmc.h" @@ -78,6 +78,11 @@ struct ld_sdmmc_task { struct buf *task_bp; int task_retries; /* number of xfer retry */ struct callout task_restart_ch; + + kmutex_t task_lock; + kcondvar_t task_cv; + + uintptr_t task_data; }; struct ld_sdmmc_softc { @@ -91,6 +96,7 @@ struct ld_sdmmc_softc { struct evcnt sc_ev_discard; /* discard counter */ struct evcnt sc_ev_discarderr; /* discard error counter */ struct evcnt sc_ev_discardbusy; /* discard busy counter */ + struct evcnt sc_ev_cachesyncbusy; /* cache sync busy counter */ }; static int ld_sdmmc_match(device_t, cfdata_t, void *); @@ -153,6 +159,8 @@ ld_sdmmc_attach(device_t parent, device_t self, void *aux) task = &sc->sc_task[i]; task->task_sc = sc; callout_init(&task->task_restart_ch, CALLOUT_MPSAFE); + mutex_init(&task->task_lock, MUTEX_DEFAULT, IPL_NONE); + cv_init(&task->task_cv, "ldsdmmctask"); pcq_put(sc->sc_freeq, task); } @@ -224,8 +232,11 @@ ld_sdmmc_detach(device_t dev, int flags) return rv; ldenddetach(ld); - for (i = 0; i < __arraycount(sc->sc_task); i++) + for (i = 0; i < __arraycount(sc->sc_task); i++) { callout_destroy(&sc->sc_task[i].task_restart_ch); + mutex_destroy(&sc->sc_task[i].task_lock); + cv_destroy(&sc->sc_task[i].task_cv); + } pcq_destroy(sc->sc_freeq); evcnt_detach(&sc->sc_ev_discard); @@ -379,15 +390,56 @@ ld_sdmmc_discard(struct ld_softc *ld, struct buf *bp) return 0; } +static void +ld_sdmmc_docachesync(void *arg) +{ + struct ld_sdmmc_task *task = arg; + struct ld_sdmmc_softc *sc = task->task_sc; + const bool poll = (bool)task->task_data; + + task->task_data = sdmmc_mem_flush_cache(sc->sc_sf, poll); + + mutex_enter(&task->task_lock); + cv_signal(&task->task_cv); + mutex_exit(&task->task_lock); +} + +static int +ld_sdmmc_cachesync(struct ld_softc *ld, bool poll) +{ + struct ld_sdmmc_softc *sc = device_private(ld->sc_dv); + struct ld_sdmmc_task *task = pcq_get(sc->sc_freeq); + int error = 0; + + if (task == NULL) { + sc->sc_ev_cachesyncbusy.ev_count++; + return EBUSY; + } + + sdmmc_init_task(&task->task, ld_sdmmc_docachesync, task); + task->task_data = poll; + + mutex_enter(&task->task_lock); + sdmmc_add_task(sc->sc_sf->sc, &task->task); + error = cv_wait_sig(&task->task_cv, &task->task_lock); + mutex_exit(&task->task_lock); + + if (error == 0) + error = (int)task->task_data; + + pcq_put(sc->sc_freeq, task); + + return error; +} + static int ld_sdmmc_ioctl(struct ld_softc *ld, u_long cmd, void *addr, int32_t flag, bool poll) { - struct ld_sdmmc_softc *sc = device_private(ld->sc_dv); switch (cmd) { case DIOCCACHESYNC: - return sdmmc_mem_flush_cache(sc->sc_sf, poll); + return ld_sdmmc_cachesync(ld, poll); default: return EPASSTHROUGH; } |
