Improve your PinePhone eMMC speed

8 minute read Published: 2021-11-13

While reading megi's PinePhone Pro review I noticed that the PinePhone Pro eMMC is fast (~150MB/s), and began wondering why can't the original PinePhone eMMC (50-80MB/s) go that fast as well.

eMMC cards can use these timing modes:

Timing modeeMMC versionClock speedMax data rateVccq voltage allowed
Legacy>= 4.10-26MHz26MB/s3.3V, 1.8V, 1.2V
DDR52>= 4.40-52MHz104MB/s3.3V, 1.8V, 1.2V
HS200>= 4.50-200MHz200MB/s1.8V, 1.2V
HS400>= 5.00-200MHz400MB/s1.8V, 1.2V

A bit of investigation shows that the PinePhone eMMC is using the DDR52 timing mode. This explains why we get read speeds below 100MB/s.

# cat /sys/kernel/debug/mmc2/ios 
clock:		52000000 Hz
actual clock:	50000000 Hz
vdd:		21 (3.3 ~ 3.4 V)
bus mode:	2 (push-pull)
chip select:	0 (don't care)
power mode:	2 (on)
bus width:	3 (8 bits)
timing spec:	8 (mmc DDR52)
signal voltage:	0 (3.30 V)
driver type:	0 (driver type B)

After searching in the datasheets from the Pine64 PinePhone wiki I figured out that both the Allwinner A64 and the Kimtigo eMMC support eMMC 5.0 standard, so they should be able to use the High Speed HS200/HS400 timings.

The eMMC timing mode is defined by the PinePhone Device Tree which is used by the Linux kernel to know the hardware configuration of a certain device.

The PinePhone base Device Tree, which is common to all board revisions can be found in the kernel source in the arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi file.

This is the portion describing the eMMC:

&mmc2 {
	pinctrl-names = "default";
	pinctrl-0 = <&mmc2_pins>;
	vmmc-supply = <&reg_dcdc1>;
	vqmmc-supply = <&reg_dcdc1>;
	bus-width = <8>;
	non-removable;
	cap-mmc-hw-reset;
	post-power-on-delay-ms = <1>; /* power is already turned on by the bootloader */
	status = "okay";
};

I have tried enabling HS200 mode by adding mmc-hs200-1_8v; to the mmc2 block and the eMMC fails the HS200 initialization and falls back to legacy mode.

$ dmesg | grep mmc
[    0.661840] mmc2: mmc_select_hs200 failed, error -74
[    0.717040] mmc2: new MMC card at address 0001
[    0.718093] mmcblk2: mmc2:0001 DA4032 29.1 GiB
[    0.718663] mmcblk2boot0: mmc2:0001 DA4032 4.00 MiB
[    0.719119] mmcblk2boot1: mmc2:0001 DA4032 4.00 MiB
[    0.721260]  mmcblk2: p1 p2
[    2.048130]     pmos_boot=/dev/mmcblk0p1
[    2.048136]     pmos_root=/dev/mmcblk0p2
# cat /sys/kernel/debug/mmc2/ios
clock:		25000000 Hz
actual clock:	25000000 Hz
vdd:		21 (3.3 ~ 3.4 V)
bus mode:	2 (push-pull)
chip select:	0 (don't care)
power mode:	2 (on)
bus width:	3 (8 bits)
timing spec:	0 (legacy)
signal voltage:	1 (1.80 V)
driver type:	0 (driver type B)

eMMC cards are managed NAND devices, they contain one or mode NAND chips and a micro-controller which takes care of wear leveling and other operations required by NAND storage and exposes a block device to the host.

eMMC cards have two power supply lines:

In the eMMC timing table above you can see that HS200/HS400 modes are not allowed when Vccq is powered at 3.3V, and looking at the PinePhone schematic, we see that the PinePhone has Vccq set to 3.3V.

This is the reason why HS200 doesn't work on the PinePhone.

Hardware modification

This modification involves desoldering and soldering components on the PinePhone mainboard, do this only if you feel confident with the procedure.
I take no resposibility for eventual damage.

This modification requires support from the Linux distribution you are using, as the Device Tree needs to be patched to reflect the hardware change. PostmarketOS supports this with the package device-pine64-pinephone-vccq-mod, if you use other Linux distros, get in touch with them to know if the mod is supported.

After performing this modification you won't be able to boot from eMMC any Linux image that is missing the proper Device Tree Overlay, this limitation does not apply to the microSD slot.

If we look at the schematics and the component placement files from the Pine64 PinePhone wiki, we can see that there is a 0 ohm resistor R615 that can be moved to a Not Connect footprint R614, to switch Vccq from a 3.3V regulator (DCDC1) to a 1.8V regulator (ELDO1).

The steps to perform the hardware mod are the following:

See the postmarketOS PinePhone wiki page for details.

Software support

After performing the mod, the PinePhone Device Tree will not correspond to our PinePhone hardware, because we have moved Vccq from a 3.3V regulator to a 1.8V one, and we need to enable HS200 mode to get advantage of the additional speed.

To do this and maintain compatibility with non modified PinePhones I created the device-pine64-pinephone-vccq-mod packet (postmarketOS Merge Request)

The package has to temporarily change the Device Tree, it does this by using a Device Tree Overlay, that gets loaded by the U-boot bootloader before starting the Linux kernel.

This is the Device Tree Overlay we need:

// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
// Copyright (C) 2021 Federico Amedeo Izzo <federico@izzo.pro>

// Switch eMMC Vccq from 3.3v regulator DCDC1 to 1.8v regulator ELDO1
// And enable HS200 mode.
// This DT overlay should be applied only after modifying the Pinephone
// Board by moving R615 to R614.

/dts-v1/;
/plugin/;
/ {
    compatible = "pine64,pinephone";
};

&mmc2 {
	pinctrl-0 = <&mmc2_pins>, <&mmc2_ds_pin>;
	vqmmc-supply = <&reg_eldo1>;
	mmc-hs200-1_8v;
};

&pio {
	vcc-pc-supply = <&reg_eldo1>;
};

To apply this device tree only after installing the device-pine64-pinephone-vccq-mod I had to change the U-boot configuration.

The PinePhone is using U-boot in a read-only mode, as U-boot environment variables are not persistent, they are restored to default values at each boot, and the boot procedure itself is managed by a boot.scr script, that is executed at each boot.

I think that this is a good decision as having read-only variables means that U-boot is stateless and cannot get corrupted by changing the variables.

To change the U-boot behaviour, by suggestion of Martijn Braam I used the source command in boot.scr to load a user script that loads the Device Tree Overlay. If the user script is not found, the source command is skipped.

This section was added to the main boot.scr

echo Loading user script
setenv user_scriptaddr 0x50100000
load mmc ${mmc_bootdev}:1 ${user_scriptaddr} user.scr
if test $? -eq 0; then source ${user_scriptaddr}; else echo No user script found; fi

And this is the secondary user.scr script

echo Entering user script:

echo Loading DTB overlay
load mmc ${mmc_bootdev}:1 ${fdtoverlay_addr_r} pinephone-vccq-mod.dtbo

echo Applying DTB overlay
fdt apply ${fdtoverlay_addr_r}

echo Exiting user script:

By applying the change on boot.scr by default and installing the user.scr and Device Tree Overlay with the device-pine64-pinephone-vccq-mod, the software support is complete.

Speed measurements

The read speed of your eMMC or other storage device can be measured with the hdparm command. Replace /dev/mmcblk2 with the device you want to mesure, and only consider the buffered speed.

# hdparm -tT /dev/mmcblk2
/dev/mmcblk2:
 Timing cached reads:   1038 MB in  2.00 seconds = 518.44 MB/sec
 Timing buffered disk reads: 376 MB in  3.00 seconds = 125.13 MB/sec

Here are the results on the two PinePhones I modified:

Future improvements:

HS400 mode is supported by hardware but it's not currently supported by the sunxi-mmc drivers in the Linux kernel.

Adding support for HS400 mode could bring speed improvements, but there is a caveat:

From the Allwinner A64 SoC datasheet we can see that the eMMC clock is limited to 150MHz in HS200 mode and 100MHz in HS400 mode, so the speed improvements depend on whether it's higher the speed gain from HS400 or the speed penalty from the clock reduction.