When the Raspberry Pi Zero was announced, a significant chunk of the hacker world dismissed it for having the same fatal flaw as the other Pis: only one USB host port for high speed I/O.
Network connectivity is a major reason that people base projects around a Pi or other embedded Linux board. The 2B and B+ both have onboard USB hubs providing 4 USB ports and an Ethernet interface, making it easy to connect to a wired network or add on a USB WiFi adapter. But the A+ and Zero do not have this luxury. You need an external hub to get the same functionality, which compromises the size advantage and makes for an unwieldy cable mess.
That's unfortunate, because other embedded Linux boards in similar form factors are at least $50 (Intel Edison). These often are designed for commercial products, and so have hacker-unfriendly interconnects that need a breakout board that drives the cost up even further. What's worse is that alternatives don't have the same online support community that Raspberry Pi has fostered.
What the Zero desperately needs is WiFi that doesn't involve the USB port.
SDIO and HATs
The BCM283x SoCs at the heart of every Pi has two MMC controllers. One of these connects to the SD card slot that the Pi boots and runs its OS from. The other has largely been ignored, but comes out on the HAT connector as alternate functions on GPIOs 22-27. Recent kernel and boot blob changes have enabled these pins to be mapped to the MMC controller that also supports SDIO devices.
SDIO started out as an extension of the SD spec to allow for cards that have functionality beyond flash storage. Some PDAs could be expanded to have WiFi or cameras through SDIO. Although smartphones killed the market for SDIO cards by integrating these common features, the bus still lives on as a bridge between SoCs and WiFi modules.
At the time of this writing, the rpi-4.2.y branch of the Raspberry Pi supports the SDIO interface on the HAT connector well. Simply build and install the kernel and edit /boot/config.txt to include the following line:
This enables SDIO on the HAT connector, and asks it to poll for SDIO cards past the initial driver probe at boot time (more on this later).
Enter the Eagle
In all the excitement over the Zero, I noticed a bunch of questions about using cheap ESP8266 modules as a WiFi bridge. Usually these threads were killed by someone by saying that the SPI interface would constrain the speed too much. Fortunately those questions jogged my memory a bit.
I remembered this blog post showing a decapped ESP8266, noting that in the corner is printed "ESP8089". The post mentioned that ESP8089 is another Espressif part, a commodity SPI and SDIO to WiFi chip. That got me thinking that, if the ESP8089 and ESP8266 are the same die, then there is a good chance that an ESP8266 module could work as an SDIO WiFi adapter on the Pi.
Espressif isn't helpful with ESP8089 documentation, but some Googling landed me at the center of the cheap Chinese tablet universe. That pointed me to a driver in a kernel tree for a specific Rockchip-based tablet that happens to have an ESP8089 adapter, as well as an attempt to break out an older version of the driver as a standalone module to use with ESP8089 in SPI mode.
So there's a driver, and possibly a physical interface between the Pi and ESP8266, but what about firmware?
The ESP8266 we're all familiar with normally boots from an off-chip SPI flash device, and can optionally boot code over the UART. The determination of how to boot (UART vs SPI) is done by GPIO strapping at power-on. Most ESP modules are set so that one pin alternates between booting from SPI flash or the UART. This lets the UART be used to download new firmware to the SPI flash when hooked to a PC and to run from flash when not. But the sparse ESP8266 documentation does mention a third SDIO boot mode, but there is no explanation of how to use it.
It turns out that SDIO boot is intended for the ESP8089. The driver contains a few firmware binaries encoded in header files, eagle_fwN.h. Encouragingly, "Eagle" seems to show up as a code name in some ESP8266 documentation. This firmware is downloaded to the ESP8089 by the driver after it's detected, after which the ESP8089 restarts and reattaches to SDIO. This reattachment is why the SDIO driver needs the "poll_once=false", otherwise the ESP8089 would not be detected after firmware download.
Back to hardware
Since there are a variety of ESP8266 modules, and I'm not an RF guru, I decided to hack an off-the-shelf module to bring out SDIO. All available ESP8266 modules use all six pins of the SDIO interface for the SPI flash. That means that the SPI flash will need to be removed from the module to gain access to the SDIO bus. The ESP-12E would be ideal for this since it breaks out the 6 SDIO signals to castellations, but it has a soldered-down shield making it difficult to desolder the flash. The popular ESP-01 has no shield, but from the PCB art posted online also has GPIO15 strapped low via a trace under the QFN, making it impossible to boot into SDIO mode. I ended up with the ESP-03, which has no lid and brings all the GPIOs needed for boot mode strapping out to pads.
A tangent on the SDIO interface: At the bottom of the ESP8266 Hardware User's Guide is a sample schematic showing how to interface an ESP8266 with 1-bit SDIO while also using a SPI flash. In this schematic, the other SPI interface is used for clock, data, and CS to the flash, while SD_D2 and SD_D3 are remapped to flash's /HOLD and /WP. Meanwhile the remaining four SDIO pins operate in a lower bandwidth 1 bit mode (SD_D1 becomes an interrupt request). It looks like in the SDIO boot mode, that this configuration is selected by the pullups and pulldowns on the SD_D2 and SD_D3 signals, but there's not any solid documentation on this. I played a bit with pulling SD_D2 and SD_D3 high or low during boot, while watch the serial port, and these straps affect the "y" in the "boot mode: (x,y)" string. However this is irrelevant for this project since we don't need a SPI flash and these should only have the pullups from the host end for 4-bit SDIO.
The Hardware User's Guide has a note on the SDIO signals, suggesting a 200 ohm series termination resistor on all six. I built these into my prototype so they could be swapped out if needed. The signals look good, so I would use these on any future boards.
Another Espressif document, the Pin List spreadsheet, has a cryptic page covering all the boot modes. With MTDO / GPIO15 pulled low, GPIO2 and GPIO0 select between the usual "FLASH BOOT" (SPI) and UART, as well as the undocumented "Remapping" and "Jump Boot". With MTDO / GPIO15 pulled high, the ESP8266 boots from SDIO. In SDIO boot, GPIO0 and GPIO2 select one of four options for the SDIO bus mode. I don't know what "V1" and "V2" I/O mean, but experimentation shows that "LowSpeed" is 25MHz clock while "HighSpeed" is 50MHz. The ideal strapping, and the one I use, has GPIO15, 0, and 2 pulled high by the internal pullups. In other words, not connected.
CHIP_PD needs to be pulled high in order for the chip to operate. I suspect that this is usually wired to a GPIO on the host SoC to disable the interface, possibly run through the kernel "rfkill" driver. The original ESP8089 driver code had some functions that toggled some GPIO, presumably driving CHIP_PD, to cause the chip to clear state on a driver reload. I will eventually investigate wiring CHIP_PD to a HAT GPIO pin and modify the driver to do this, since the module fails to load once the ESP8089 is running the downloaded firmware.