When I tried to mainline MT6577, I've read tons of forum posts, chat rooms and a lot of guides on the internet. This repository contains my notes, tips and other thoughts which could be useful for bringing support for old Mediatek devices into mainline Linux kernel and relevant development in general. The infomation should be appliable for mt65xx 32-bit CPUs running linux kernel v3.4. I have never worked worked on 3.10 and 3.18 kernels. If you have something to add please open a pull request or leave a comment.
- The state of MT65xx in mainline Linux kernel
- mt83xx/mt65xx comparison
- Extracting information from the running device
- Searching in the source code
- Working with BootROM / Preloader / Download agents
- Debugging over UART
Only basic hardware works: CPU cores, generic interrupt controller, timer, UART, (sometimes) watchdog.
There's no clock driver for these chipsets which is the most limiting factor as of now. With working clocks, it would be theoretically possible to bring this hardware to life:
- MMC (internal memory and microSD card support)
- I2C (implementing this will make a lot of devices work)
- MIPI subsystem (display stuff)
There's also no GPIO and pinctrl stuff for mt65xx. Unless you can write the drivers yourself, do not expect your mainlined device to be of any worth.
Chipset | Technology node | Cores | Max frequency | Core architecture | GPU | Max screen resolution | Identical to |
---|---|---|---|---|---|---|---|
mt8317 | 40 nm | 2 | 1.2 GHz | Cortex-A9 | PowerVR SGX531 | 1280x720 | mt6517 |
mt8377 | 40 nm | 2 | 1.2 GHz | Cortex-A9 | PowerVR SGX531 | 1280x720 | mt6577 |
mt8389 | 28 nm | 4 | 1.2 GHz | Cortex-A7 | PowerVR SGX544 | 1920x1080 | mt6589 |
mt8312 | 28 nm | 2 | 1.2 GHz | Cortex-A7 | Mali-400 MP1 | 1280x720 | mt6572 |
mt8382 | 28 nm | 4 | 1.3 GHz | Cortex-A7 | Mali-400 MP2 | 1280x720 | mt6582 |
mt8392 | 28 nm | 8 | 4x2.0 GHz, 4x1.7 GHz | Cortex-A7 | Mali-450 MP4 | 1920x1080 | mt6592 |
It's implied your device has root and busybox, and is connected to your PC via ADB, and the shell is running
There has to be a way to find voltages required for mainline kernel. As of now, the command below just prints all available CPU frequencies. All CPU cores must be running for correct output, start some stress test program. It doesn't matter if it's some Pi value calculator or cryptocurrency miner. Other cores might symlink their frequency list to the first core.
busybox find /sys/devices/system/cpu -type f -name 'time_in_state' -print -exec cat '{}' \; | busybox cut -d ' ' -f 1
Example output:
/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state
250250
500500
667333
750750
834166
1001000
1209000
The output seems to be always stripped
cat /sys/devices/virtual/misc/mtgpio/pin
Example output:
206: 0 0 0 0 1 1 0
207: 1 0 0 0 1 0 0
208: 1 0 0 0 1 0 0
209: 1 0 0 0 1 0 0
210: 1 0 0 0 1 0 0
211: 1 0 1 0 1 0 0
212: 1 0 1 0 1 0 0
213: 1 0 1 0 1 0 0
214: 1 0 1 0 1 0 0
215: 1 0 11|shell@android:/ $
Output description (source):
1 [MODE 0 - GPIO]
2 [PULL_SEL (Pullup)]
3 [DIN]
4 [DOUT (output voltage) / 1 - high voltage (1.8V/2.8V..) ,0 - low voltage]
5 [PULL EN (Pull-up enabled)]
6 [DIR (Input on the next direction)(3,4 decision is valid]
7 [INV]
8 [IES]
The command should list all attached I2C devices on all busses of your device:
find /sys/devices/platform/mt*i2c.* -mindepth 2 -name 'driver' -print -exec realpath '{}' \; -exec echo \;
Example output:
/sys/devices/platform/mt-i2c.0/i2c-0/0-0036/driver
/sys/bus/i2c/drivers/ncp1851
/sys/devices/platform/mt-i2c.0/i2c-0/0-004c/driver
/sys/bus/i2c/drivers/MC32X0
Output of this command contains 2 lines for each attached device:
- /sys/devices/platform/mt-i2c.0/i2c-0/0-0036/driver ← I2C address (hex)
- /sys/bus/i2c/drivers/ncp1851 ← Driver name which could hint the actual hardware
cat /proc/cmdline
Example outputs:
console=ttyMT3,921600n1 vmalloc=320M lcm=1-lg4573b fps=5965 pl_t=582 lk_t=5249
console=ttyMT3,921600n1 vmalloc=506M slub_max_order=0 lcm=1-hx8379a_dsi_vdo_bidirectional fps=5300 pl_t=3466 lk_t=3184
See the lcm=
parameter, remove leading digit and dash. LCM names from example outputs are lg4573b
and hx8379a_dsi_vdo_bidirectional
respectively.
This command prints known voltage values in millivolts (mV):
busybox find /sys/devices/platform/mt-pmic/ -iname '*volt*' -print -exec cat '{}' \; -exec echo \;
Example output:
/sys/devices/platform/mt-pmic/LDO_VCAM_AF_VOLTAGE
2800
/sys/devices/platform/mt-pmic/BUCK_VCORE_VOLTAGE
800
It's great if there is a public kernel source code for your SoC. If you have a kernel source code for your exact device model, you can do a bit more. Usually old mediatek kernels have directory structure like this, or this. Newer kernels have this directory structure. Anyway, the mediatek/platforrm/mt65xx
directory is what we need.
Note: some platforms only list frequencies without voltages. If you know how to find voltage values please let me know.
Note: DVFS stands for "Dynamic Voltage Frequency Scaling"
mediatek/platform/mt6572/kernel/core/mt_cpufreq.c
has the following macro:
#define OP(khz, volt) \
{ \
.cpufreq_khz = khz, \
.cpufreq_volt = volt, \
}
Find usages of this macro. Example:
static struct mt_cpu_freq_info mt6572_freqs_e1_1[] = {
OP(DVFS_D3, DVFS_V0),
OP(DVFS_F1, DVFS_V1),
OP(DVFS_F2, DVFS_V1),
OP(DVFS_F3, DVFS_V1),
};
Here we can see only constants prefixed with DVFS_
are used. Search for their definitions:
fgrep -I 'define DVFS_' -r mediatek/
mediatek/platform/mt6572/kernel/core/include/mach/mt_cpufreq.h:#define DVFS_D0 (1599000) // KHz, OD
mediatek/platform/mt6572/kernel/core/include/mach/mt_cpufreq.h:#define DVFS_D1 (1404000) // KHz, OD
mediatek/platform/mt6572/kernel/core/include/mach/mt_cpufreq.h:#define DVFS_D2 (1300000) // KHz, OD
mediatek/platform/mt6572/kernel/core/include/mach/mt_cpufreq.h:#define DVFS_D3 (1209000) // KHz, OD
mediatek/platform/mt6572/kernel/core/include/mach/mt_cpufreq.h:#define DVFS_F1 (1001000) // KHz
mediatek/platform/mt6572/kernel/core/include/mach/mt_cpufreq.h:#define DVFS_F2 (806000) // KHz
mediatek/platform/mt6572/kernel/core/include/mach/mt_cpufreq.h:#define DVFS_F3 (598000) // KHz
mediatek/platform/mt6572/kernel/core/include/mach/mt_cpufreq.h:#define DVFS_V0 (1250) // mV, OD
mediatek/platform/mt6572/kernel/core/include/mach/mt_cpufreq.h:#define DVFS_V1 (1150) // mV
mediatek/platform/mt6572/kernel/core/include/mach/mt_cpufreq.h:#define DVFS_MIN_VCORE (1150)
mediatek/platform/mt6577/kernel/core/mt_cpufreq.c
has the following macro:
#define OP(cpufreq) \
{ \
.cpufreq_mhz = cpufreq, \
}
The voltages are not specified here! However, by analyzing this file it's possible to find how Linux kernel interacts with PMIC by writing values which depend on chosen frequency:
if (freq_new == DVFS_F1) ... DRV_WriteReg32(SC_AP_DVFS_CON, ((DRV_Reg32(SC_AP_DVFS_CON) & 0xFFFFFFFC) | 0x00));
else if (freq_new == DVFS_F2) ... DRV_WriteReg32(SC_AP_DVFS_CON, ((DRV_Reg32(SC_AP_DVFS_CON) & 0xFFFFFFFC) | 0x03));
else if (freq_new == DVFS_F3) ... DRV_WriteReg32(SC_AP_DVFS_CON, ((DRV_Reg32(SC_AP_DVFS_CON) & 0xFFFFFFFC) | 0x03));
else if (freq_new == DVFS_F4) ... DRV_WriteReg32(SC_AP_DVFS_CON, ((DRV_Reg32(SC_AP_DVFS_CON) & 0xFFFFFFFC) | 0x03));
It's quite similar to how MT6572 (see above) uses one voltage for the highest frequency, and the other voltage for all other frequencies. Most likely 0x00
and 0x03
values mean some voltage setting. Some PDF from the internet says:
Before the CA9 clock rate speeds up, the software needs to program the external PMIC. VPROC from companion PMIC MT6329 can be programmed by I2C interface, or VPROC can be controlled by pin PMUCTRL1/0 (DVS fast control) from MT6577. Please refer to MT6329 datasheet for detailed programming guides. There is the 4-step setup by values 0x0, 0x1, 0x2 and 0x3, standing for voltages from low to high.
But I couldn't figure out what voltage does each value represent.
Mainlining a device involves writing a Device Tree Source file which requires you to know exact register addresses. Mediatek source code uses virtual register addresses, but DTS needs physical addresses. To solve this, you need to look in mediatek/platform/mt65xx/kernel/core/include/mach/memory.h
and search for IO_VIRT_TO_PHYS
macro there.
Example (source):
#define IO_VIRT_TO_PHYS(v) (0xC0000000 | ((v) & 0x0fffffff))
What this function does is simply replacing the first hexadecimal digit with 'C'. So, if downstream kernel source code lists some register address as 0xF0001234
, then its physical address is just 0xC0001234
. Though there might be more complicated functions.
After virtual to physical address conversion is sorted out, it's safe to continue working on registers. Below are major sources of register addresses:
mediatek/platform/mt65xx/kernel/core/include/mach/mt_reg_base.h
- should list registers for big SoC subsystemsmediatek/platform/mt65xx/kernel/core/include/mach/mt_clock_manager.h
- should contain most of the clock-related registersmediatek/platform/mt65xx/kernel/core/include/mach/mt_device_apc.h
- DEVAPC (DEVice Automatic Power Control)mediatek/platform/mt65xx/kernel/core/include/mach/mt_dcm.h
mediatek/platform/mt65xx/kernel/core/include/mach/mt_cpe.h
mediatek/platform/mt65xx/kernel/core/include/mach/mt_emi_bm.h
mediatek/platform/mt65xx/kernel/core/include/mach/mt_emi_bwl.h
mediatek/platform/mt65xx/kernel/core/include/mach/mt_emi_mpu.h
Data gathered from the first 2 files is usually enough to boot basic mainline kernel.
They are required for various hardware in dts(i), see file mediatek/platform/mt65xx/kernel/core/include/mach/mt_irq.h
. The addendum of GIC_*_SIGNALS
is the IRQ number, example:
#define MT_TS_IRQ_ID (GIC_PRIVATE_SIGNALS + 21)
^^--- IRQ ID
⚠️ Warning! If you are using Virtual Box above v6 with USB passthrough to interact with BootROM / Preloader / DAs you might encounter a performance issue that slows down reconnecting your device from host to guest. It must happen very fast but due to the bug it doesn't and the bootloader reaches timeout faster than the handshake is completed. Older versions of VirtualBox such as v5 do not have this issue.- Device connects as USB 2.0 so if you're using Virtual Box, corresponding Extension Pack is required.
- (Linux only) If VirtualBox doesn't detect any USB devices at all, make sure the current user is in
vboxusers
group.
Below is the table of the common VID/PIDs used by Mediatek devices. If you are using a VM with USB passthrough to interact with BootROM / Preloader / DAs you must configure Host->Guest autoreconnect. In VirtualBox you can do this by creating a filter for each device.
VID | PID | Purpose |
---|---|---|
0e8d | 0003 | BootROM |
0e8d | 2000 | Preloader |
0e8d | 2001 | Download agent |
Note: There are other VID/PIDs used by modern devices and their values depend on specific manufacturer. These 3, however, should be enough for 99% of mt65xx devices.
For some reasons you might want to use BootROM mode instead Preloader mode.
- ☢️☢️☢️ WARNING: MAKE A BACKUP FIRST ☢️☢️☢️ The one-size-fits-all way to get into the BootROM mode is to corrupt the preloader stored in the internal memory (PRELOADER partition). You can do this in variety of ways:
- Write some random data to the start of the PRELOADER partition. Though even 1 kB is enough I tend to overwrite the whole partition. If your device is rooted you can do it right there with
dd
. Otherwise you can always rely on proprietary SP Flash Tool (enable Advanced Mode and look for "Write Memory" tab) or FLOSS software such as mtkclient. - Format the whole internal storage. Try to avoid this option as you're risking losing NVRAM and other important stuff.
- Write some random data to the start of the PRELOADER partition. Though even 1 kB is enough I tend to overwrite the whole partition. If your device is rooted you can do it right there with
- Some devices boot into BootROM when some key is held. Usually it's one of the volume keys.
- Some devices enter BootROM mode when connected to PC without a battery.
Read brom-dump/README.md.
UART is one of the best tools for gathering information and even communicating with your device. Usually a single SoC has multiple UARTs for various purposes. For example, one of UARTs could be used to control the wireless hardware (Wi-Fi, Bluetooth, GPS, Radio...). Despite its advantages, there are several drawbacks. First, there's need to tear down the device to access UART. Second, you will need a soldering iron with thin tip and some good flux, and skills to use them. Third, most Mediatek devices have UART pins exposed on the motherboard, however identifying them might not be the easiest task. I will go through some ways to find UART pads, Fly IQ430 (MT6577) will be used as an example.
There's a chance the motherboard of your device has labels for important pads. Look for "TX", "RX" labels. I've seen some boards having the most straightforward and obvious labeling: "UART_0_TX", "UART_0_RX". Fly IQ430 motherboard does have labels on it:
Where you can download a schematics for your device for free is whole different topic and won't be discussed here. However if you are out of luck, try asking in the "#offtopic" chat of postmarketOS, additionally you can ping/mention me (look for arzamas-16 nickname), there's a chance of me being able to fetch the desired schematic for you.
Usually they are less informative and more generic. This is how they look like. Just search for 'UART' or 'TX' and check the results. For example, Fly IQ430 general schematic doesn't specify the exact location of UART, and doesn't even give any clear pointers to where could it be. Some boards have alphanumeric labels on them, and general schematics could point to required pads.
Those are the best schematics one could have. Not only they show the exact placement of components and pads, they can also list these components, so if you screw something up, you can search for a replacement part. Boardview files require special software to open them (OpenBoardView is Free/Libre Open Source Software, and works quite good), and it can also show how internal components are connected with each other. Very nice!
Just like with general schematics, search for UART-related stuff such as "UART" or "TX". Fly IQ430 board schematic tells us the exact location and names of UART pads, but notice how the board is flipped horizontally:
Some devices might have interesting approaches to exposing UARTs such as using 3.5mm audio jack output or microUSB connector pads. Search it up on the internet.
Please do not use PL2303HX USB dongles from AliExpress/Banggood/Wish. They are super cheap, but also they are fake because Prolific doesn't produce these chips anymore, official sources:
Not only you will get counterfeit and unstable hardware, but you can also do great damage to your device. I recommend using USB dongles based on FT232RL, they don't cost much, they support high baudrates and 1.8 V logic levels, and they just don't die out of sudden like fake PL2303-based converters.
At least 2 connections are mandatory: the TX pad and the GND pad. Sometimes there's a UART voltage supply pad on the board, but I've never used it myself, so I'd say it's safe to not use it at all.
In 95% of cases the "TX" pad of your device has to be connected to the "RX" pin of your USB-UART dongle. "RX" pad of your device goes to "TX" on the dongle.
For dumping early boot logs on downstream kernel connecting just a TX pad is enough. Use RX pad for communicating your device which runs mainline kernel to access shell.
On Linux, you are free to use literally anything you'd like to. I prefer picocom
. On Windows PuTTY
is usually recommended by the folks on the internet.
As for baudrate, it's either 115200 or 921600 depending on the purpose of UART you've connected to.
UART labels and identifiers on Mediatek devices are not standardized, but there are some common patterns:
- UART1 (TX1/RX1) is used by the Boot ROM and Preloader. Runs at 115200 baud.
- UART2 (TX2/RX2) is used directly by the SoC for controlling the hardware on the board. Baud rate differs. Might not be exposed on the board.
- UART3 (TX3/RX3) is used directly by the SoC for controlling the hardware on the board, usually it's the wireless connectivity chip. Often it's not exposed on the board.
- UART4 (TX4/RX4) is used by the Preloader, and U-Boot, and Linux kernel for logging output. Runs at 921600 baud. Gets set up by U-Boot upon booting the device.
A few lines right after powering on the device. Output can be very verbose while the device is being flashed via SP Flash Tool.
RP: 0000 0000 0000
F2: 3000 00A0
F3: 0000 0000
RP: 0000 0000
V0: 0000 0000 [0001]
V1: 0000 0000 [0003]
V2: 0000 0000 [0009]
00: 0000 0000
READY
Following lines are printed by the Preloader after the device is powered on:
[i2c_init] Start...................
[i2c_set_speed] Set sclk to 99 khz (orig: 100 khz)
[i2c_set_speed] I2C Timing parameter sample_cnt_div(0), step_cnt_div(61)
[i2c_init] Done
[pmic6329_init] Start...................
After these lines you could also see the DRAM calibration table, and the EMMC partition layout table.
U-Boot log might begin with:
[LCM Auto Detect], we have 1 lcm drivers built in
[LCM Auto Detect], try to find driver for [unknown]
[LCM Specified] [ej070na]
[mtkfb] LCM TYPE: DPI
[mtkfb] LCM INTERFACE: SERIAL
[mtkfb] LCM resolution: 1024 x 600
[PROFILE] ------- i2c init takes 1 ms --------
UB wdt init
[LEDS]LK: leds_init: mt65xx_backlight_off
[LEDS]LK: mt65xx_backlight_off
[LEDS]LK: lcd-backlight level is 0
[LEDS]LK: backlight_set_pwm:duty is 0
[PROFILE] ------- led init takes 15 ms --------
U-Boot logs contain the list of partitions on EMMC, clock setup, display initialization output, and other messages related to showing the boot logo, charging (not always! some devices handle charging in Linux kernel), checking the signatures and headers. The last lines of U-Boot log look like these:
[PROFILE] ------- boot_time takes 1804 ms --------
booting linux @ 0xa08000, ramdisk @ 0x4a00000 (594282)
[LEDS]LK: leds_deinit: LEDS off
lk boot time = 1804 ms
lk boot mode = 0
lk finished --> jump to linux kernel
Linux kernel log starts with this line:
[ 0.000000] (0)[0:swapper]Linux version 3.4.0 (marsl_lin@BM-1) (gcc version 4.6.x-google 20120106 (prerelease) (GCC) ) #1 SMP PREEMPT Tue Apr 2 19:19:54 CST 2013