MSM8960 Mainlining

The Qualcomm MSM8960/APQ8064 (Snapdragon S4 series) is a high-end smartphone SoC in 2012.

Status
The following features provided by the MSM8960 SoC are supported in mainline and should work on most MSM8960-based devices after the device tree has been set up:
 * UART
 * USB
 * Internal/External Storage (eMMC/SD card)

Certain components (e.g. touchscreen, sensors, ...) are device-specific. With a bit of luck, some will be already supported by mainline, others won't.

msm8960-mainline/linux
The close-to-mainline Linux kernel fork with patches that have not been accepted/submitted upstream yet. Patches in the  branch are generally in good shape unless marked otherwise. Submit a PR to add new patches there.

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.

Requirements

 * MSM8960-based device
 * UART if possible (with a bit of luck you may get USB working without 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. MSM8960 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 MSM8960.

The best way to make yourself familiar with the process is to attempt to figure out some simple things on your own. Therefore, this guide will only describe everything until (eventually) USB network is working. For everything else, this article provides only some information that might be helpful to figure it out yourself.

If you have any questions, ask in the mainline channel on Matrix or IRC. Make sure to mention "msm8960" if your question is specific to this article.

Please also join the MSM8960 specific channel on Matrix.

Preparations
Unfortunately, as the MSM8960 platform is outdated, many of the downstream kernels use board files instead of device trees. That means you will likely have to manually traverse these board files to make a proper device tree.

Now, let's check how it looks on mainline. Clone linux and take a look at. This is the directory where you will add your device tree later.

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.

In order to boot mainline, lk2nd now requires a reserved memory to be specified in a dts in order to know what ram Linux can use. Leaving it out can be fine, however it will likely lead to oddities later on.

To figure out what your reserved memories are, first install lk2nd onto your device and then run. You should now have a file called atags.bin

Next, make a file called  in   and put the following code inside
 * 1) include 
 * 2) include 
 * 3) include 
 * 4) include "setup.h"

void parse_tag(const struct tag *t) { switch (t->hdr.tag) { case ATAG_MEM: printf("Mem: 0x%x - 0x%x (size %d)\n", t->u.mem.start, t->u.mem.start + t->u.mem.size, t->u.mem.size); break; case ATAG_INITRD2: printf("Initrd: 0x%x - 0x%x (size: %d)\n", t->u.initrd.start, t->u.initrd.start + t->u.initrd.size, t->u.initrd.size); break; case ATAG_SERIAL: printf("Serial: %08x%08x\n", t->u.serialnr.high, t->u.serialnr.low); break; case ATAG_REVISION: printf("Revision: 0x%x\n", t->u.revision.rev); break; case ATAG_CMDLINE: printf("Cmdline: %s\n", t->u.cmdline.cmdline); break; case ATAG_CORE: case ATAG_NONE: break; // Ignore default: printf("Unknown: 0x%x\n", t->hdr.tag); } }

int main(int argc, char* argv[]) { FILE *f = fopen(argv[1], "rb"); fseek(f, 0, SEEK_END); long fsize = ftell(f); fseek(f, 0, SEEK_SET);

struct tag *t = malloc(fsize); fread(t, fsize, 1, f); fclose(f);

if (t->hdr.tag != ATAG_CORE) { printf("Fail: 0x%x\n", t->hdr.tag); return 1; }

for (t->hdr.size; t = tag_next(t)) { parse_tag(t); }   return 0; }

Compile that c file with `cc atags.c -o atags` and run `./atags atags.bin`. It should give you something like the following:

Revision: 0x8 Serial: 0000b1c10000f58e Mem: 0x80200000 - 0x88d00000 (size 145752064) Mem: 0x90000000 - 0xc0000000 (size 805306368) Cmdline: lk2nd sec_log=0x80000@0x88d00008 sec_dbg=0x40000@0x88d90004 sec_debug.reset_reason=0x1a2b3c00 etb_buf=0x4000@0x8fffb9c0 androidboot.debug_level=0x4f4c sec_debug.enable=0 sec_debug.enable_user=0 androidboot.cp_debug_level=0x55FF sec_debug.enable_cp_debug=0 cordon=9aca3e010986c9435237198590d9cb10 sysscope=0xee000000 loglevel=4 samsung.hardware=SGH-I437 androidboot.emmc_checksum=3 androidboot.bootloader=I437UCDMG4 androidboot.nvdata_backup=0 androidboot.boot_recovery=0 androidboot.batt_check_recovery=0 level=0x574f4c44 sec_pvs=0 androidboot.emmc=true androidboot.serialno=b1c1f58e androidboot.baseband=msm

The memories that are listed are the ones that Linux can occupy. We need to tell Linux not to occupy the other spots. For the example above, it would look like the following: https://gitlab.com/LogicalErzor/expressatt-linux/-/commit/884e2afac2618e7f65b1c972877c17065cb6447c

Device Package
TODO ( Don't really know the pmos official way. Currently make it manually here https://gitlab.com/LogicalErzor/mainlinemaker/-/blob/main/mainlinemaker.py )

Build Kernel
Go back to your msm8960-mainline/linux clone. Try to build it with envkernel.sh:

$ cd path/to/msm8960-mainline/linux $ source path/to/pmbootstrap/helpers/envkernel.sh $ make qcom_apq8064_defconfig $ make -j $ pmbootstrap build --envkernel linux-postmarketos-qcom-msm890
 * 1) Initialize kernel configuration
 * 1) Compile kernel; replace with the number of cores you'd like to use for compilation
 * 1) Create postmarketOS package with your built kernel

This is how you can build and test local changes for the kernel from now on. Now we will continue setting up an initial device tree for your device.

Later on, if you want to change the config, for example to build a new display driver as a module, you can run.