Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 8 Jun 2019 16:05:43 +0000 (UTC)
From:      "Bjoern A. Zeeb" <bz@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r348803 - head/sys/arm/broadcom/bcm2835
Message-ID:  <201906081605.x58G5hKf094010@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: bz
Date: Sat Jun  8 16:05:43 2019
New Revision: 348803
URL: https://svnweb.freebsd.org/changeset/base/348803

Log:
  bcm2835_sdhci.c: save block registers to avoid controller bug
  
  Extending what the initial revision, r273264, r276985, r277346 have
  started for the transfer mode and command registers, another pair of
  16bit registers written in sequence are block size and block count,
  which fall together onto the same 32bit line and hence the same
  register(s) would be written twice in sequence for those as well.
  
  Use a similar approach to transfer mode and command and save the writes
  to either of the block regiters and then only execute a write once.
  We can do this as with transfer mode their values are meaningless until
  a command is issued so we can use that write to command as a trigger
  to also write out the block registers.
  Compared to transfer mode and command the value of block count can
  change, so we need to keep state and actually read the block registers
  back the first time after a write.
  
  MFC after:	2 weeks
  Differential Revision:	https://reviews.freebsd.org/D20197

Modified:
  head/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c

Modified: head/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c
==============================================================================
--- head/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c	Sat Jun  8 16:03:34 2019	(r348802)
+++ head/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c	Sat Jun  8 16:05:43 2019	(r348803)
@@ -107,12 +107,14 @@ struct bcm_sdhci_softc {
 	bus_dma_tag_t		sc_dma_tag;
 	bus_dmamap_t		sc_dma_map;
 	vm_paddr_t		sc_sdhci_buffer_phys;
-	uint32_t		cmd_and_mode;
 	bus_addr_t		dmamap_seg_addrs[NUM_DMA_SEGS];
 	bus_size_t		dmamap_seg_sizes[NUM_DMA_SEGS];
 	int			dmamap_seg_count;
 	int			dmamap_seg_index;
 	int			dmamap_status;
+	uint32_t		blksz_and_count;
+	uint32_t		cmd_and_mode;
+	bool			need_update_blk;
 };
 
 static int bcm_sdhci_probe(device_t);
@@ -269,6 +271,10 @@ bcm_sdhci_attach(device_t dev)
 
 	sdhci_start_slot(&sc->sc_slot);
 
+	/* Seed our copies. */
+	sc->blksz_and_count = SDHCI_READ_4(dev, &sc->sc_slot, SDHCI_BLOCK_SIZE);
+	sc->cmd_and_mode = SDHCI_READ_4(dev, &sc->sc_slot, SDHCI_TRANSFER_MODE);
+
 	return (0);
 
 fail:
@@ -338,17 +344,21 @@ static uint16_t
 bcm_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
 {
 	struct bcm_sdhci_softc *sc = device_get_softc(dev);
-	uint32_t val = RD4(sc, off & ~3);
+	uint32_t val32;
 
 	/*
-	 * Standard 32-bit handling of command and transfer mode.
+	 * Standard 32-bit handling of command and transfer mode, as
+	 * well as block size and count.
 	 */
-	if (off == SDHCI_TRANSFER_MODE) {
-		return (sc->cmd_and_mode >> 16);
-	} else if (off == SDHCI_COMMAND_FLAGS) {
-		return (sc->cmd_and_mode & 0x0000ffff);
-	}
-	return ((val >> (off & 3)*8) & 0xffff);
+	if ((off == SDHCI_BLOCK_SIZE || off == SDHCI_BLOCK_COUNT) &&
+	    sc->need_update_blk)
+		val32 = sc->blksz_and_count;
+	else if (off == SDHCI_TRANSFER_MODE || off == SDHCI_COMMAND_FLAGS)
+		val32 = sc->cmd_and_mode;
+	else
+		val32 = RD4(sc, off & ~3);
+
+	return ((val32 >> (off & 3)*8) & 0xffff);
 }
 
 static uint32_t
@@ -383,18 +393,43 @@ bcm_sdhci_write_2(device_t dev, struct sdhci_slot *slo
 {
 	struct bcm_sdhci_softc *sc = device_get_softc(dev);
 	uint32_t val32;
-	if (off == SDHCI_COMMAND_FLAGS)
+
+	/*
+	 * If we have a queued up 16bit value for blk size or count, use and
+	 * update the saved value rather than doing any real register access.
+	 * If we did not touch either since the last write, then read from
+	 * register as at least block count can change.
+	 * Similarly, if we are about to issue a command, always use the saved
+	 * value for transfer mode as we can never write that without issuing
+	 * a command.
+	 */
+	if ((off == SDHCI_BLOCK_SIZE || off == SDHCI_BLOCK_COUNT) &&
+	    sc->need_update_blk)
+		val32 = sc->blksz_and_count;
+	else if (off == SDHCI_COMMAND_FLAGS)
 		val32 = sc->cmd_and_mode;
 	else
 		val32 = RD4(sc, off & ~3);
+
 	val32 &= ~(0xffff << (off & 3)*8);
 	val32 |= (val << (off & 3)*8);
+
 	if (off == SDHCI_TRANSFER_MODE)
 		sc->cmd_and_mode = val32;
-	else {
-		WR4(sc, off & ~3, val32);
-		if (off == SDHCI_COMMAND_FLAGS)
+	else if (off == SDHCI_BLOCK_SIZE || off == SDHCI_BLOCK_COUNT) {
+		sc->blksz_and_count = val32;
+		sc->need_update_blk = true;
+	} else {
+		if (off == SDHCI_COMMAND_FLAGS) {
+			/* If we saved blk writes, do them now before cmd. */
+			if (sc->need_update_blk) {
+				WR4(sc, SDHCI_BLOCK_SIZE, sc->blksz_and_count);
+				sc->need_update_blk = false;
+			}
+			/* Always save cmd and mode registers. */
 			sc->cmd_and_mode = val32;
+		}
+		WR4(sc, off & ~3, val32);
 	}
 }
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201906081605.x58G5hKf094010>