svn commit: r338637 - stable/11/sys/dev/mmc

Marius Strobl marius at FreeBSD.org
Thu Sep 13 10:18:48 UTC 2018


Author: marius
Date: Thu Sep 13 10:18:47 2018
New Revision: 338637
URL: https://svnweb.freebsd.org/changeset/base/338637

Log:
  MFC: r333647, r338275, r338280, r338513
  
  - 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/11/sys/dev/mmc/mmc.c
  stable/11/sys/dev/mmc/mmcreg.h
  stable/11/sys/dev/mmc/mmcsd.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/dev/mmc/mmc.c
==============================================================================
--- stable/11/sys/dev/mmc/mmc.c	Thu Sep 13 09:26:16 2018	(r338636)
+++ stable/11/sys/dev/mmc/mmc.c	Thu Sep 13 10:18:47 2018	(r338637)
@@ -1587,10 +1587,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)
@@ -1778,6 +1781,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,
@@ -1787,11 +1791,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;
@@ -1799,65 +1802,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) {
@@ -1869,13 +1863,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,
@@ -1890,8 +1884,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/11/sys/dev/mmc/mmcreg.h
==============================================================================
--- stable/11/sys/dev/mmc/mmcreg.h	Thu Sep 13 09:26:16 2018	(r338636)
+++ stable/11/sys/dev/mmc/mmcreg.h	Thu Sep 13 10:18:47 2018	(r338637)
@@ -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/11/sys/dev/mmc/mmcsd.c
==============================================================================
--- stable/11/sys/dev/mmc/mmcsd.c	Thu Sep 13 09:26:16 2018	(r338636)
+++ stable/11/sys/dev/mmc/mmcsd.c	Thu Sep 13 10:18:47 2018	(r338637)
@@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/bio.h>
 #include <sys/bus.h>
 #include <sys/conf.h>
+#include <sys/endian.h>
 #include <sys/fcntl.h>
 #include <sys/ioccom.h>
 #include <sys/kernel.h>
@@ -69,6 +70,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/mutex.h>
 #include <sys/priv.h>
 #include <sys/slicer.h>
+#include <sys/sysctl.h>
 #include <sys/time.h>
 
 #include <geom/geom.h>
@@ -130,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 ... */
@@ -150,12 +154,19 @@ static const char *errmsg[] =
 	"NO MEMORY"
 };
 
+static SYSCTL_NODE(_hw, OID_AUTO, mmcsd, CTLFLAG_RD, NULL, "mmcsd driver");
+
+static int mmcsd_cache = 1;
+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);
@@ -164,7 +175,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);
 
@@ -177,6 +187,7 @@ 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, struct thread *td);
 static int mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_ioc_cmd *mic,
@@ -235,7 +246,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/
@@ -295,6 +306,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.
 	 */
@@ -324,10 +357,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,
@@ -503,7 +534,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;
@@ -517,6 +547,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));
@@ -669,10 +701,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);
@@ -704,6 +748,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);
 }
 
@@ -738,19 +784,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)
 {
@@ -943,6 +988,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
@@ -1155,6 +1202,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
@@ -1263,7 +1311,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;
@@ -1282,7 +1330,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;
@@ -1340,13 +1388,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;
 
@@ -1396,6 +1449,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;
@@ -1453,10 +1514,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


More information about the svn-src-all mailing list