Up until now, I've been recommending that the flash be somehow disabled on ESP-12 modules. The flash and the SDIO bus share the same signals, and I was certain that the SPI flash chip on the module normally used for code storage would interfere with the SDIO bus.
It turns out that's not as likely to happen as I thought.
SPI flash chips keep all their pins as inputs unless they have been asked to respond with data. In single bit mode the MISO signal carries data from the flash to the host, and is the only one that can become an active output. For this to happen, one of a handful of commands with read responses have to be sent to the flash first. Meanwhile, the chip select signal (/CS) must be held active-low or the transaction is aborted. Data is sent to and from the flash in groups of 8 bits, with each bit being valid on the edge of the clock (CLK) signal. So for the SPI flash to do anything that could stomp on the SDIO bus, /CS must go low and stay low for 8 CLK edges while a valid command with a read response must appear on the MOSI pin.
The SDIO bus operates in a fundamentally different way, with the only commonality being the clock shared on the same pin (SD_CLK / SCK). Commands and response codes on SDIO are communicated through a dedicated bidirectional line called SD_CMD. The data payload sent to and from the SDIO device come over the bidirectional data bus on the remaining pins (SD_D0 to SD_D3). A typical SDIO register read starts with the host sending a command over SD_CMD requesting some number of bytes and a starting address. The device responds over SD_CMD acknowledging the request at roughly the same time that the data for the read is sent out over SD_Dn. The device keeps sending data with each clock pulse until the request is fulfilled, then sends a CRC for the packet and a stop bit to complete the transaction.
Allowing command / response packets and data transfers to occur simultaneously improves the bus throughput for SDIO. But it presents a problem because SD_CMD and the SPI /CS are shared. In the idle state, SD_CMD is held high which keeps the SPI flash idle. But when SD_CMD is active, the SPI flash will read in whatever is on SD_D1. SD commands and responses are fairly short: 48 bits for all but the device ID command. For some packets it is possible for part of the payload to have 8 to 16 zeros, which would be enough to cause the SPI flash to become active if the right data were on the MOSI signal (SD_D1).
Fortunately the SPI flash commands are all non-zero and non-0xFF. So if the SDIO data bus was in the idle state while a string of 8 or more zeros went across SD_CMD, the SPI flash would clock in all 0xFFs since SD_D1 idles high. The concern is when this happens while the data bus is not idle. In that case, the data present at each clock cycle on SD_D1 is latched by the SPI flash. And if the data happened to contain one of the read response commands to the flash, then the flash might try to drive data out on MISO and corrupt SD_D0.
In 1-bit SDIO, this should never be a problem. SD_D1 in 1-bit mode is repurposed as an active-low interrupt signal and is not likely to transition fast enough to form any of the read commands. But this failure could potentially happen in 4-bit mode because SD_D1 may be involved in a data transfer while a packet is going across SD_CMD.
I know others have been sending the SPI flash powerdown command based on work done by @haddyhad. That got me thinking about the actual likelihood of problems from SPI flash interference.
I have run throughput tests on an ESP-12F (unmodified) in 4-bit mode for days and have not seen a failure. I'm not clear why I haven't seen bus corruption, but I suspect the ESP holds off on data from a read command until after the response is sent. When I get a chance I'll look with a logic analyzer.
Suspicion confirmed. I captured a bunch of transactions under heavy load and it looks like read data doesn't start until well after the response completes. I guess this isn't as efficient but with 72Mbps max there's plenty of room on a 50MHz 4-bit SDIO bus.
ESP-12F should work fine in SDIO 1-bit and 4-bit modes without modification or putting the flash in HOLD mode.