From owner-svn-src-stable@freebsd.org Thu Sep 13 10:18:51 2018 Return-Path: Delivered-To: svn-src-stable@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 916D91083FD4; Thu, 13 Sep 2018 10:18:51 +0000 (UTC) (envelope-from marius@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 43F6A8B420; Thu, 13 Sep 2018 10:18:51 +0000 (UTC) (envelope-from marius@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 21E5B14272; Thu, 13 Sep 2018 10:18:51 +0000 (UTC) (envelope-from marius@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id w8DAIpZP020630; Thu, 13 Sep 2018 10:18:51 GMT (envelope-from marius@FreeBSD.org) Received: (from marius@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id w8DAIoRJ020627; Thu, 13 Sep 2018 10:18:50 GMT (envelope-from marius@FreeBSD.org) Message-Id: <201809131018.w8DAIoRJ020627@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: marius set sender to marius@FreeBSD.org using -f From: Marius Strobl Date: Thu, 13 Sep 2018 10:18:50 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org Subject: svn commit: r338638 - stable/10/sys/dev/mmc X-SVN-Group: stable-10 X-SVN-Commit-Author: marius X-SVN-Commit-Paths: stable/10/sys/dev/mmc X-SVN-Commit-Revision: 338638 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable@freebsd.org X-Mailman-Version: 2.1.27 Precedence: list List-Id: SVN commit messages for all the -stable branches of the src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 13 Sep 2018 10:18:52 -0000 Author: marius Date: Thu Sep 13 10:18:50 2018 New Revision: 338638 URL: https://svnweb.freebsd.org/changeset/base/338638 Log: MFC: r333614, r333647, r338275, r338280, r338513 - Let mmcsd_ioctl() ensure appropriate privileges via priv_check(9). - If present, take advantage of the R/W cache of eMMC revision 1.5 and later devices. These caches work akin to the ones found in HDDs/SSDs that ada(4)/da(4) also enable if existent, but likewise increase the likelihood of data loss in case of a sudden power outage etc. On the other hand, write performance is up to twice as high for e. g. 1 GiB files depending on the actual chip and transfer mode employed. For maximum data integrity, the usage of eMMC caches can be disabled via the hw.mmcsd.cache tunable. - Get rid of the NOP mmcsd_open(). - Obtain the bus mode (MMC or SD) from the directly superordinated bus rather than reaching up to the bridge and use the cached mode in mmcsd_delete(), too. - Use le32dec(9) for decoding EXT_CSD values where it makes sense. [1] - Locally cache some instance variable values in mmc_discover_cards() in order to improve the code readability a bit. Obtained from: NetBSD [1] Modified: stable/10/sys/dev/mmc/mmc.c stable/10/sys/dev/mmc/mmcreg.h stable/10/sys/dev/mmc/mmcsd.c Directory Properties: stable/10/ (props changed) Modified: stable/10/sys/dev/mmc/mmc.c ============================================================================== --- stable/10/sys/dev/mmc/mmc.c Thu Sep 13 10:18:47 2018 (r338637) +++ stable/10/sys/dev/mmc/mmc.c Thu Sep 13 10:18:50 2018 (r338638) @@ -1588,10 +1588,13 @@ mmc_discover_cards(struct mmc_softc *sc) uint32_t raw_cid[4]; struct mmc_ivars *ivar = NULL; const struct mmc_quirk *quirk; + const uint8_t *ext_csd; device_t child; int err, host_caps, i, newcard; uint32_t resp, sec_count, status; uint16_t rca = 2; + int16_t rev; + uint8_t card_type; host_caps = mmcbr_get_caps(sc->dev); if (bootverbose || mmc_debug) @@ -1779,6 +1782,7 @@ mmc_discover_cards(struct mmc_softc *sc) goto free_ivar; } + rev = -1; /* Only MMC >= 4.x devices support EXT_CSD. */ if (ivar->csd.spec_vers >= 4) { err = mmc_send_ext_csd(sc->dev, sc->dev, @@ -1788,11 +1792,10 @@ mmc_discover_cards(struct mmc_softc *sc) "Error reading EXT_CSD %d\n", err); goto free_ivar; } + ext_csd = ivar->raw_ext_csd; + rev = ext_csd[EXT_CSD_REV]; /* Handle extended capacity from EXT_CSD */ - sec_count = ivar->raw_ext_csd[EXT_CSD_SEC_CNT] + - (ivar->raw_ext_csd[EXT_CSD_SEC_CNT + 1] << 8) + - (ivar->raw_ext_csd[EXT_CSD_SEC_CNT + 2] << 16) + - (ivar->raw_ext_csd[EXT_CSD_SEC_CNT + 3] << 24); + sec_count = le32dec(&ext_csd[EXT_CSD_SEC_CNT]); if (sec_count != 0) { ivar->sec_count = sec_count; ivar->high_cap = 1; @@ -1800,65 +1803,56 @@ mmc_discover_cards(struct mmc_softc *sc) /* Find maximum supported bus width. */ ivar->bus_width = mmc_test_bus_width(sc); /* Get device speeds beyond normal mode. */ - if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & - EXT_CSD_CARD_TYPE_HS_52) != 0) { + card_type = ext_csd[EXT_CSD_CARD_TYPE]; + if ((card_type & EXT_CSD_CARD_TYPE_HS_52) != 0) { setbit(&ivar->timings, bus_timing_hs); ivar->hs_tran_speed = MMC_TYPE_HS_52_MAX; - } else if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & - EXT_CSD_CARD_TYPE_HS_26) != 0) { + } else if ((card_type & EXT_CSD_CARD_TYPE_HS_26) != 0) { setbit(&ivar->timings, bus_timing_hs); ivar->hs_tran_speed = MMC_TYPE_HS_26_MAX; } - if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & - EXT_CSD_CARD_TYPE_DDR_52_1_2V) != 0 && + if ((card_type & EXT_CSD_CARD_TYPE_DDR_52_1_2V) != 0 && (host_caps & MMC_CAP_SIGNALING_120) != 0) { setbit(&ivar->timings, bus_timing_mmc_ddr52); setbit(&ivar->vccq_120, bus_timing_mmc_ddr52); } - if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & - EXT_CSD_CARD_TYPE_DDR_52_1_8V) != 0 && + if ((card_type & EXT_CSD_CARD_TYPE_DDR_52_1_8V) != 0 && (host_caps & MMC_CAP_SIGNALING_180) != 0) { setbit(&ivar->timings, bus_timing_mmc_ddr52); setbit(&ivar->vccq_180, bus_timing_mmc_ddr52); } - if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & - EXT_CSD_CARD_TYPE_HS200_1_2V) != 0 && + if ((card_type & EXT_CSD_CARD_TYPE_HS200_1_2V) != 0 && (host_caps & MMC_CAP_SIGNALING_120) != 0) { setbit(&ivar->timings, bus_timing_mmc_hs200); setbit(&ivar->vccq_120, bus_timing_mmc_hs200); } - if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & - EXT_CSD_CARD_TYPE_HS200_1_8V) != 0 && + if ((card_type & EXT_CSD_CARD_TYPE_HS200_1_8V) != 0 && (host_caps & MMC_CAP_SIGNALING_180) != 0) { setbit(&ivar->timings, bus_timing_mmc_hs200); setbit(&ivar->vccq_180, bus_timing_mmc_hs200); } - if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & - EXT_CSD_CARD_TYPE_HS400_1_2V) != 0 && + if ((card_type & EXT_CSD_CARD_TYPE_HS400_1_2V) != 0 && (host_caps & MMC_CAP_SIGNALING_120) != 0 && ivar->bus_width == bus_width_8) { setbit(&ivar->timings, bus_timing_mmc_hs400); setbit(&ivar->vccq_120, bus_timing_mmc_hs400); } - if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & - EXT_CSD_CARD_TYPE_HS400_1_8V) != 0 && + if ((card_type & EXT_CSD_CARD_TYPE_HS400_1_8V) != 0 && (host_caps & MMC_CAP_SIGNALING_180) != 0 && ivar->bus_width == bus_width_8) { setbit(&ivar->timings, bus_timing_mmc_hs400); setbit(&ivar->vccq_180, bus_timing_mmc_hs400); } - if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & - EXT_CSD_CARD_TYPE_HS400_1_2V) != 0 && - (ivar->raw_ext_csd[EXT_CSD_STROBE_SUPPORT] & + if ((card_type & EXT_CSD_CARD_TYPE_HS400_1_2V) != 0 && + (ext_csd[EXT_CSD_STROBE_SUPPORT] & EXT_CSD_STROBE_SUPPORT_EN) != 0 && (host_caps & MMC_CAP_SIGNALING_120) != 0 && ivar->bus_width == bus_width_8) { setbit(&ivar->timings, bus_timing_mmc_hs400es); setbit(&ivar->vccq_120, bus_timing_mmc_hs400es); } - if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & - EXT_CSD_CARD_TYPE_HS400_1_8V) != 0 && - (ivar->raw_ext_csd[EXT_CSD_STROBE_SUPPORT] & + if ((card_type & EXT_CSD_CARD_TYPE_HS400_1_8V) != 0 && + (ext_csd[EXT_CSD_STROBE_SUPPORT] & EXT_CSD_STROBE_SUPPORT_EN) != 0 && (host_caps & MMC_CAP_SIGNALING_180) != 0 && ivar->bus_width == bus_width_8) { @@ -1870,13 +1864,13 @@ mmc_discover_cards(struct mmc_softc *sc) * units of 10 ms), defaulting to 500 ms. */ ivar->cmd6_time = 500 * 1000; - if (ivar->raw_ext_csd[EXT_CSD_REV] >= 6) + if (rev >= 6) ivar->cmd6_time = 10 * - ivar->raw_ext_csd[EXT_CSD_GEN_CMD6_TIME]; + ext_csd[EXT_CSD_GEN_CMD6_TIME]; /* Handle HC erase sector size. */ - if (ivar->raw_ext_csd[EXT_CSD_ERASE_GRP_SIZE] != 0) { + if (ext_csd[EXT_CSD_ERASE_GRP_SIZE] != 0) { ivar->erase_sector = 1024 * - ivar->raw_ext_csd[EXT_CSD_ERASE_GRP_SIZE]; + ext_csd[EXT_CSD_ERASE_GRP_SIZE]; err = mmc_switch(sc->dev, sc->dev, ivar->rca, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_ERASE_GRP_DEF, @@ -1891,8 +1885,7 @@ mmc_discover_cards(struct mmc_softc *sc) } } - mmc_decode_cid_mmc(ivar->raw_cid, &ivar->cid, - ivar->raw_ext_csd[EXT_CSD_REV] >= 5); + mmc_decode_cid_mmc(ivar->raw_cid, &ivar->cid, rev >= 5); child_common: for (quirk = &mmc_quirks[0]; quirk->mid != 0x0; quirk++) { Modified: stable/10/sys/dev/mmc/mmcreg.h ============================================================================== --- stable/10/sys/dev/mmc/mmcreg.h Thu Sep 13 10:18:47 2018 (r338637) +++ stable/10/sys/dev/mmc/mmcreg.h Thu Sep 13 10:18:50 2018 (r338638) @@ -300,6 +300,8 @@ struct mmc_request { /* * EXT_CSD fields */ +#define EXT_CSD_FLUSH_CACHE 32 /* W/E */ +#define EXT_CSD_CACHE_CTRL 33 /* R/W/E */ #define EXT_CSD_EXT_PART_ATTR 52 /* R/W, 2 bytes */ #define EXT_CSD_ENH_START_ADDR 136 /* R/W, 4 bytes */ #define EXT_CSD_ENH_SIZE_MULT 140 /* R/W, 3 bytes */ @@ -333,12 +335,19 @@ struct mmc_request { #define EXT_CSD_PWR_CL_200_360 237 /* RO */ #define EXT_CSD_PWR_CL_52_195_DDR 238 /* RO */ #define EXT_CSD_PWR_CL_52_360_DDR 239 /* RO */ +#define EXT_CSD_CACHE_FLUSH_POLICY 249 /* RO */ #define EXT_CSD_GEN_CMD6_TIME 248 /* RO */ +#define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */ #define EXT_CSD_PWR_CL_200_360_DDR 253 /* RO */ /* * EXT_CSD field definitions */ +#define EXT_CSD_FLUSH_CACHE_FLUSH 0x01 +#define EXT_CSD_FLUSH_CACHE_BARRIER 0x02 + +#define EXT_CSD_CACHE_CTRL_CACHE_EN 0x01 + #define EXT_CSD_EXT_PART_ATTR_DEFAULT 0x0 #define EXT_CSD_EXT_PART_ATTR_SYSTEMCODE 0x1 #define EXT_CSD_EXT_PART_ATTR_NPERSISTENT 0x2 @@ -417,6 +426,8 @@ struct mmc_request { #define EXT_CSD_SEC_FEATURE_SUPPORT_BD_BLK_EN 0x04 #define EXT_CSD_SEC_FEATURE_SUPPORT_GB_CL_EN 0x10 #define EXT_CSD_SEC_FEATURE_SUPPORT_SANITIZE 0x40 + +#define EXT_CSD_CACHE_FLUSH_POLICY_FIFO 0x01 /* * Vendor specific EXT_CSD fields Modified: stable/10/sys/dev/mmc/mmcsd.c ============================================================================== --- stable/10/sys/dev/mmc/mmcsd.c Thu Sep 13 10:18:47 2018 (r338637) +++ stable/10/sys/dev/mmc/mmcsd.c Thu Sep 13 10:18:50 2018 (r338638) @@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -67,7 +68,9 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include +#include #include #include @@ -129,6 +132,8 @@ struct mmcsd_softc { uint32_t flags; #define MMCSD_INAND_CMD38 0x0001 #define MMCSD_USE_TRIM 0x0002 +#define MMCSD_FLUSH_CACHE 0x0004 +#define MMCSD_DIRTY 0x0008 uint32_t cmd6_time; /* Generic switch timeout [us] */ uint32_t part_time; /* Partition switch timeout [us] */ off_t enh_base; /* Enhanced user data area slice base ... */ @@ -149,12 +154,20 @@ static const char *errmsg[] = "NO MEMORY" }; +static SYSCTL_NODE(_hw, OID_AUTO, mmcsd, CTLFLAG_RD, NULL, "mmcsd driver"); + +static int mmcsd_cache = 1; +TUNABLE_INT("hw.mmcsd.cache", &mmcsd_cache); +SYSCTL_INT(_hw_mmcsd, OID_AUTO, cache, CTLFLAG_RDTUN, &mmcsd_cache, 0, + "Device R/W cache enabled if present"); + #define LOG_PPS 5 /* Log no more than 5 errors per second. */ /* bus entry points */ static int mmcsd_attach(device_t dev); static int mmcsd_detach(device_t dev); static int mmcsd_probe(device_t dev); +static int mmcsd_shutdown(device_t dev); /* disk routines */ static int mmcsd_close(struct disk *dp); @@ -163,7 +176,6 @@ static int mmcsd_dump(void *arg, void *virtual, vm_off static int mmcsd_getattr(struct bio *); static int mmcsd_ioctl_disk(struct disk *disk, u_long cmd, void *data, int fflag, struct thread *td); -static int mmcsd_open(struct disk *dp); static void mmcsd_strategy(struct bio *bp); static void mmcsd_task(void *arg); @@ -176,8 +188,9 @@ static void mmcsd_add_part(struct mmcsd_softc *sc, u_i static int mmcsd_bus_bit_width(device_t dev); static daddr_t mmcsd_delete(struct mmcsd_part *part, struct bio *bp); static const char *mmcsd_errmsg(int e); +static int mmcsd_flush_cache(struct mmcsd_softc *sc); static int mmcsd_ioctl(struct mmcsd_part *part, u_long cmd, void *data, - int fflag); + int fflag, struct thread *td); static int mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_ioc_cmd *mic, int fflag); static uintmax_t mmcsd_pretty_size(off_t size, char *unit); @@ -234,7 +247,7 @@ mmcsd_attach(device_t dev) sc = device_get_softc(dev); sc->dev = dev; sc->mmcbus = mmcbus = device_get_parent(dev); - sc->mode = mmcbr_get_mode(mmcbus); + sc->mode = mmc_get_card_type(dev); /* * Note that in principle with an SDHCI-like re-tuning implementation, * the maximum data size can change at runtime due to a device removal/ @@ -294,6 +307,28 @@ mmcsd_attach(device_t dev) rev = ext_csd[EXT_CSD_REV]; /* + * With revision 1.5 (MMC v4.5, EXT_CSD_REV == 6) and later, take + * advantage of the device R/W cache if present and useage is not + * disabled. + */ + if (rev >= 6 && mmcsd_cache != 0) { + size = le32dec(&ext_csd[EXT_CSD_CACHE_SIZE]); + if (bootverbose) + device_printf(dev, "cache size %juKB\n", size); + if (size > 0) { + MMCBUS_ACQUIRE_BUS(mmcbus, dev); + err = mmc_switch(mmcbus, dev, sc->rca, + EXT_CSD_CMD_SET_NORMAL, EXT_CSD_CACHE_CTRL, + EXT_CSD_CACHE_CTRL_CACHE_EN, sc->cmd6_time, true); + MMCBUS_RELEASE_BUS(mmcbus, dev); + if (err != MMC_ERR_NONE) + device_printf(dev, "failed to enable cache\n"); + else + sc->flags |= MMCSD_FLUSH_CACHE; + } + } + + /* * Ignore user-creatable enhanced user data area and general purpose * partitions partitions as long as partitioning hasn't been finished. */ @@ -323,10 +358,8 @@ mmcsd_attach(device_t dev) size *= erase_size * wp_size; if (size != mmc_get_media_size(dev) * sector_size) { sc->enh_size = size; - sc->enh_base = (ext_csd[EXT_CSD_ENH_START_ADDR] + - (ext_csd[EXT_CSD_ENH_START_ADDR + 1] << 8) + - (ext_csd[EXT_CSD_ENH_START_ADDR + 2] << 16) + - (ext_csd[EXT_CSD_ENH_START_ADDR + 3] << 24)) * + sc->enh_base = + le32dec(&ext_csd[EXT_CSD_ENH_START_ADDR]) * (sc->high_cap != 0 ? MMC_SECTOR_SIZE : 1); } else if (bootverbose) device_printf(dev, @@ -502,7 +535,6 @@ mmcsd_add_part(struct mmcsd_softc *sc, u_int type, con MMCSD_DISK_LOCK_INIT(part); d = part->disk = disk_alloc(); - d->d_open = mmcsd_open; d->d_close = mmcsd_close; d->d_strategy = mmcsd_strategy; d->d_ioctl = mmcsd_ioctl_disk; @@ -516,6 +548,8 @@ mmcsd_add_part(struct mmcsd_softc *sc, u_int type, con d->d_stripesize = sc->erase_sector * d->d_sectorsize; d->d_unit = cnt; d->d_flags = DISKFLAG_CANDELETE; + if ((sc->flags & MMCSD_FLUSH_CACHE) != 0) + d->d_flags |= DISKFLAG_CANFLUSHCACHE; d->d_delmaxsize = mmc_get_erase_sector(dev) * d->d_sectorsize; strlcpy(d->d_ident, mmc_get_card_sn_string(dev), sizeof(d->d_ident)); @@ -668,10 +702,22 @@ mmcsd_detach(device_t dev) free(part, M_DEVBUF); } } + if (mmcsd_flush_cache(sc) != MMC_ERR_NONE) + device_printf(dev, "failed to flush cache\n"); return (0); } static int +mmcsd_shutdown(device_t dev) +{ + struct mmcsd_softc *sc = device_get_softc(dev); + + if (mmcsd_flush_cache(sc) != MMC_ERR_NONE) + device_printf(dev, "failed to flush cache\n"); + return (0); +} + +static int mmcsd_suspend(device_t dev) { struct mmcsd_softc *sc = device_get_softc(dev); @@ -703,6 +749,8 @@ mmcsd_suspend(device_t dev) MMCSD_IOCTL_UNLOCK(part); } } + if (mmcsd_flush_cache(sc) != MMC_ERR_NONE) + device_printf(dev, "failed to flush cache\n"); return (0); } @@ -737,19 +785,18 @@ mmcsd_resume(device_t dev) } static int -mmcsd_open(struct disk *dp __unused) +mmcsd_close(struct disk *dp) { + struct mmcsd_softc *sc; + if ((dp->d_flags & DISKFLAG_OPEN) != 0) { + sc = ((struct mmcsd_part *)dp->d_drv1)->sc; + if (mmcsd_flush_cache(sc) != MMC_ERR_NONE) + device_printf(sc->dev, "failed to flush cache\n"); + } return (0); } -static int -mmcsd_close(struct disk *dp __unused) -{ - - return (0); -} - static void mmcsd_strategy(struct bio *bp) { @@ -771,22 +818,23 @@ mmcsd_strategy(struct bio *bp) static int mmcsd_ioctl_rpmb(struct cdev *dev, u_long cmd, caddr_t data, - int fflag, struct thread *td __unused) + int fflag, struct thread *td) { - return (mmcsd_ioctl(dev->si_drv1, cmd, data, fflag)); + return (mmcsd_ioctl(dev->si_drv1, cmd, data, fflag, td)); } static int mmcsd_ioctl_disk(struct disk *disk, u_long cmd, void *data, int fflag, - struct thread *td __unused) + struct thread *td) { - return (mmcsd_ioctl(disk->d_drv1, cmd, data, fflag)); + return (mmcsd_ioctl(disk->d_drv1, cmd, data, fflag, td)); } static int -mmcsd_ioctl(struct mmcsd_part *part, u_long cmd, void *data, int fflag) +mmcsd_ioctl(struct mmcsd_part *part, u_long cmd, void *data, int fflag, + struct thread *td) { struct mmc_ioc_cmd *mic; struct mmc_ioc_multi_cmd *mimc; @@ -796,6 +844,10 @@ mmcsd_ioctl(struct mmcsd_part *part, u_long cmd, void if ((fflag & FREAD) == 0) return (EBADF); + err = priv_check(td, PRIV_DRIVER); + if (err != 0) + return (err); + err = 0; switch (cmd) { case MMC_IOC_CMD: @@ -937,6 +989,8 @@ mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_io if (err != MMC_ERR_NONE) goto switch_back; } + if (mic->write_flag != 0) + sc->flags |= MMCSD_DIRTY; if (mic->is_acmd != 0) (void)mmc_wait_for_app_cmd(mmcbus, dev, rca, &cmd, 0); else @@ -1149,6 +1203,7 @@ mmcsd_rw(struct mmcsd_part *part, struct bio *bp) else cmd.opcode = MMC_READ_SINGLE_BLOCK; } else { + sc->flags |= MMCSD_DIRTY; if (numblocks > 1) cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK; else @@ -1257,7 +1312,7 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp) memset(&cmd, 0, sizeof(cmd)); cmd.mrq = &req; req.cmd = &cmd; - if (mmc_get_card_type(dev) == mode_sd) + if (sc->mode == mode_sd) cmd.opcode = SD_ERASE_WR_BLK_START; else cmd.opcode = MMC_ERASE_GROUP_START; @@ -1276,7 +1331,7 @@ mmcsd_delete(struct mmcsd_part *part, struct bio *bp) memset(&req, 0, sizeof(req)); memset(&cmd, 0, sizeof(cmd)); req.cmd = &cmd; - if (mmc_get_card_type(dev) == mode_sd) + if (sc->mode == mode_sd) cmd.opcode = SD_ERASE_WR_BLK_END; else cmd.opcode = MMC_ERASE_GROUP_END; @@ -1334,13 +1389,18 @@ mmcsd_dump(void *arg, void *virtual, vm_offset_t physi device_t dev, mmcbus; int err; - /* length zero is special and really means flush buffers to media */ - if (!length) - return (0); - disk = arg; part = disk->d_drv1; sc = part->sc; + + /* length zero is special and really means flush buffers to media */ + if (length == 0) { + err = mmcsd_flush_cache(sc); + if (err != MMC_ERR_NONE) + return (EIO); + return (0); + } + dev = sc->dev; mmcbus = sc->mmcbus; @@ -1390,6 +1450,14 @@ mmcsd_task(void *arg) "mmcsd disk jobqueue", 0); } while (bp == NULL); MMCSD_DISK_UNLOCK(part); + if (__predict_false(bp->bio_cmd == BIO_FLUSH)) { + if (mmcsd_flush_cache(sc) != MMC_ERR_NONE) { + bp->bio_error = EIO; + bp->bio_flags |= BIO_ERROR; + } + biodone(bp); + continue; + } if (bp->bio_cmd != BIO_READ && part->ro) { bp->bio_error = EROFS; bp->bio_resid = bp->bio_bcount; @@ -1447,10 +1515,35 @@ mmcsd_bus_bit_width(device_t dev) return (8); } +static int +mmcsd_flush_cache(struct mmcsd_softc *sc) +{ + device_t dev, mmcbus; + int err; + + if ((sc->flags & MMCSD_FLUSH_CACHE) == 0) + return (MMC_ERR_NONE); + + dev = sc->dev; + mmcbus = sc->mmcbus; + MMCBUS_ACQUIRE_BUS(mmcbus, dev); + if ((sc->flags & MMCSD_DIRTY) == 0) { + MMCBUS_RELEASE_BUS(mmcbus, dev); + return (MMC_ERR_NONE); + } + err = mmc_switch(mmcbus, dev, sc->rca, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_FLUSH_CACHE, EXT_CSD_FLUSH_CACHE_FLUSH, 60 * 1000, true); + if (err == MMC_ERR_NONE) + sc->flags &= ~MMCSD_DIRTY; + MMCBUS_RELEASE_BUS(mmcbus, dev); + return (err); +} + static device_method_t mmcsd_methods[] = { DEVMETHOD(device_probe, mmcsd_probe), DEVMETHOD(device_attach, mmcsd_attach), DEVMETHOD(device_detach, mmcsd_detach), + DEVMETHOD(device_shutdown, mmcsd_shutdown), DEVMETHOD(device_suspend, mmcsd_suspend), DEVMETHOD(device_resume, mmcsd_resume), DEVMETHOD_END