MSM8227 Mainlining

The Qualcomm MSM8227 is a mid-range SoC from 2012. It's similar to the MSM8930 and MSM8627, so depending on the context, MSM8227 can also mean MSM8930.
== Status ==
The following features provided by the MSM8227 SoC are supported and should work on most MSM8227-based devices after the device tree has been set up:
* UART
* USB
* Internal/External Storage (eMMC/SD card) (SD card may or may not work depending on device)
* Power, volume up/down buttons
Certain components (e.g. touchscreen, sensors, ...) are device-specific. With a bit of luck, some will be already supported by mainline, others won't.
== Overview ==
=== linux-msm8227 ===
The close-to-mainline Linux kernel fork (currently more like bringup playground) which consists of APQ8064, flto's and Konrad's work.
=== msm8916-mainline/lk2nd ===
The reference bootloader provided by Qualcomm for MSM8960 is open-source and based on Little Kernel (LK). Most devices will use this bootloader in a more or less modified form. On Samsung devices for example, the standard Fastboot interface was replaced by their proprietary download mode.
lk2nd is a fork of the reference bootloader with the goal to provide a unified boot interface on all devices. It provides a standard Fastboot interface (even on Samsung devices!). It also used for a number of mainline quirks (e.g. to set a WiFi/BT MAC address in the device tree; without lk2nd, WiFi/BT does not work out of the box). Eventually, more quirks may be added in the future. It is therefore recommended for all mainline MSM8960 devices. Porting it is generally much easier than porting mainline.
== Getting Started ==
=== Requirements ===
* MSM8227-based device
* UART
* (Downstream) Linux kernel source for your device (explaining ways to mainline without kernel source is out of scope for this article)
* Basic knowledge about Linux, Git, C, Device Trees, ...
* Willingness to learn about "mainlining" and to figure out things on your own
=== Before you start ===
Mainlining is not easy. MSM8227 is a platform where a lot components can be easily enabled by only setting up a device tree, which can be largely copied from other devices with minor changes. However, at some point you will reach the point where you would like to enable a particular component that is device-specific (e.g. the touchscreen, sensors, ...). In this case you will be largely on your own, and need to figure out how to enable it yourself. (Do you just need to add something to the device tree or even write a new kernel driver?) That requires some familiarity with the way the Linux kernel is working for MSM8227.
If you have any questions, ask @wctrl:matrix.org in matrix.
=== Preparations ===
Some code in downstream only applies to MSM8930 or MSM8227. The MSM8227 probably won't be as a separate SoC, it will be MSM8627 instead. To be sure, run dmesg | grep Machine.
Your device most likely either the CDP or MTP or FLUID board. Use the note above to determine exact board. When adding device tree nodes, don't forget to look in the _devices array, prefixed with board type
=== lk2nd ===
msm8916-mainline/lk2nd does not require any device-specific code. Normally, it should just run out of the box on your device.
Try building it. Make sure you are on the experimental-tmp branch.
=== Build Kernel ===
Go back to your linux clone. Try to build it with envkernel.sh:
 $ cd path/to/kernel
 $ source path/to/pmbootstrap/helpers/envkernel.sh
 
 # Initialize kernel configuration
 # Replace <cores> with the number of cores you'd like to use for compilation
 $ make qcom_msm8227_defconfig -j<cores>
 
 # Compile kernel
 $ make -j<cores>
 
Otherwise, you can build with LLVM (e.g. using toolchain with PGO, which should be faster than gcc):
 $ make ARCH=arm LLVM=1 qcom_msm8227_defconfig -j<cores>
 $ make ARCH=arm LLVM=1 -j<cores>
After the device tree has been set up, you may want fast incremental builds, install ccache and append CC="ccache clang"
=== Initrd ===
The initramfs generated by pmbootstrap is probably not suitable for debugging. You can of course just use busybox, but to include more utilities like irqtop it would be better to use a builder like buildroot. You can use my config (use first revision!, glibc doesn't play well with downstream) or make your own, it should work with both mainline and downstream kernels.
=== Booting kernel ===
Chances are you'd like to test the changes faster, so you need to build the boot.img yourself rather updating package. Since the kernel needs the device tree to work properly, you need to append it to the kernel, and package it into android boot image with your device parameters (you can get them from the device package). So your loop will boil down to:
 $ make ...
 $ cat arch/arm/boot/zImage arch/arm/boot/dts/qcom/YOUR-DT-HERE.dtb > kernel.bin
 # This may be different for your device
 $ mkbootimg -o boot.img --pagesize 4096 --base 0x80200000 --kernel_offset 0x00008000 --ramdisk_offset 0x02000000 --second_offset 0x00f00000 --tags_offset 0x00000100 --kernel kernel.bin --ramdisk /path/to/initrd --cmdline "console=ttyMSM0,115200,n8 earlycon"
 # On the device after you test stuff
 $ reboot
When you want to boot the downstream, take cmdline from device package or use panic=3 console=ttyHSL0,115200,n8 androidboot.hardware=qcom user_debug=23 msm_rtb.filter=0x3F ehci-hcd.park=3 androidboot.bootdevice=msm_sdcc.1.
=== Initial device tree ===
Fortunately, the following skeleton is enough for UART:
// SPDX-License-Identifier: GPL-2.0-only
#include "qcom-msm8930.dtsi"
#include "pm8038.dtsi"
/ {
	model = "Model"; // FIXME
	compatible = "vendor,codename", "qcom,msm8930", "qcom,msm8227"; // FIXME
	aliases {
		serial0 = &gsbi5_serial;
	};
	chosen {
		stdout-path = "serial0:115200n8";
	};
};
&gsbi5 {
	qcom,mode = <GSBI_PROT_I2C_UART>;
	status = "okay";
};
&gsbi5_serial {
	status = "okay";
};
=== Regulators ===
Before you add something else, you need the regulators. In the downstream kernel, look for arch/arm/mach-msm/board-8930-regulator-pm8038.c, here are the values of the regulators themselves and what they supply.
Find something similar to RPM_SMPS(S1, 0, 1, 1, 500000, 1150000, NULL, 100000, 4p80, AUTO, LPM) and copy values like in this example:
&rpm {
	regulators {
		compatible = "qcom,rpm-pm8038-regulators";
		
		/* SMPS */
		pm8038_s1: s1 {
			regulator-always-on;
			regulator-min-microvolt = <500000>;
			regulator-max-microvolt = <1150000>;
			qcom,switch-mode-frequency = <4800000>;
		};
		// s2, s3, ..., l1, l2, ..., lvs1, lvs2, ...
	}
};
=== USB ===
Usually you won't find a regulator with an exact voltage of 3.3V, instead you should use 3.075V. In most cases, all you need to do is set the supply regulators and select USB OTG mode:
&usb_hs1_phy {
	v3p3-supply = <&pm8038_l3>;
	v1p8-supply = <&pm8038_l4>;
};
&usb1 {
	dr_mode = "otg";
	status = "okay";
};
=== eMMC ===
Adding regulators may be enough:
&sdcc1 {
	vmmc-supply = <&pm8038_l5>; // FIXME: change for your device
	vqmmc-supply = <&pm8038_l11>; // FIXME: change for your device
	status = "okay";
};
However, pinctrl is recommended. Open board-8930-storage.c and find the pad data structures. They're quite simple, so the device tree equivalent should look like this:
&msmgpio {
	sdcc1_pins_active: sdcc1-pins-active {
		clk {
			pins = "sdc1_clk";
			drive-strengh = <16>;
			bias-disable;
		};
		cmd {
			pins = "sdc1_cmd";
			drive-strengh = <10>;
			bias-pull-up;
		};
		data {
			pins = "sdc1_data";
			drive-strengh = <10>;
			bias-pull-up;
		};
	};
	sdcc1_pins_sleep: sdcc1-pins-sleep {
		clk {
			pins = "sdc1_clk";
			drive-strengh = <2>;
			bias-disable;
		};
		cmd {
			pins = "sdc1_cmd";
			drive-strengh = <2>;
			bias-pull-up;
		};
		data {
			pins = "sdc1_data";
			drive-strengh = <2>;
			bias-pull-up;
		};
	};
};
&sdcc1 {
	vmmc-supply = <&pm8038_l5>; // FIXME
	vqmmc-supply = <&pm8038_l11>; // FIXME
	pinctrl-0 = <&sdcc1_pins_active>;
	pinctrl-1 = <&sdcc1_pins_sleep>
	pinctrl-names = "default", "sleep";
	status = "okay";
};
You also may need set the values for eMMC and I/O regulators. Copy values from that file:
&sdcc1 {
	vmmc-supply = <&pm8038_l5>; // FIXME
	vqmmc-supply = <&pm8038_l11>; // FIXME
	qcom,vdd-voltage-level = <2950000 2950000>;
	qcom,vdd-current_level = <9000 200000>;
	qcom,vdd-always-on;
	qcom,vdd-lpm_sup;
	qcom,vdd-io-voltage-level = <1800000 1800000>;
	qcom,vdd-io-current_level = <0 200000>; // no lpm for vdd-io
	qcom,vdd-io-always-on;
	pinctrl-0 = <&sdcc1_pins_active>;
	pinctrl-1 = <&sdcc1_pins_sleep>
	pinctrl-names = "default", "sleep";
	status = "okay";
};
=== GPIO keys ===
Look for keys_8930_pm8038 in board-8930.c. Add following skeleton to your root node and change values:
gpio-keys {
 compatible = "gpio-keys";
 pinctrl-names = "default";
 pinctrl-0 = <&gpio_keys_pin_a>; // defined in pm8038.dtsi
 key-camera-focus {
 label = "camera_focus";
 gpios = <&pm8038_gpio 11 GPIO_ACTIVE_LOW>; // FIXME
 linux,input-type = <1>;
 linux,code = <KEY_CAMERA_FOCUS>;
 };
 key-camera-snapshot {
 label = "camera_snapshot";
 gpios = <&pm8038_gpio 10 GPIO_ACTIVE_LOW>; // FIXME
 linux,input-type = <1>;
 linux,code = <KEY_CAMERA>;
 };
 key-volume-down {
 label = "volume_down";
 gpios = <&pm8038_gpio 8 GPIO_ACTIVE_LOW>; // FIXME
 linux,input-type = <1>;
 linux,code = <KEY_VOLUMEDOWN>;
 };
 key-volume-up {
 label = "volume_up";
 gpios = <&pm8038_gpio 3 GPIO_ACTIVE_LOW>; // FIXME
 linux,input-type = <1>;
 linux,code = <KEY_VOLUMEUP>;
		};
 };
The power button should work without any configuration.
== Troubleshooting ==
=== Device doesn't boot after small change in device tree or complains about bad initrd ===
This is probably due to incorrect memory ranges. Currently, we don't rely on lk2nd that much, so you can try booting from original bootloader (if possible), kernel should apply atags information and fix it. Otherwise parse atags to determine memory ranges and add memory node to the root node:
memory@80000000 {
	device_type = "memory";
	reg = <0x80200000 0x8c00000 0x90000000 0x10000000>; // FIXME
};
The above example is equivalent to:
 Mem: 0x80200000 - 0x88e00000 (size 146800640)
 Mem: 0x90000000 - 0xa0000000 (size 268435456)
Remember to apply lk2nd hack or the bootloader will override the values.
Category:Guide
Category:Technical Reference