Jump to content

Mainline:Broadcom Kona/Adding a new device: Difference between revisions

From dissonant.dev wiki
start writing draft
 
 
(2 intermediate revisions by the same user not shown)
Line 20: Line 20:
'''The downstream Kona kernels used board files.''' (They did have some partial DT support, but it was never used for any production devices.)
'''The downstream Kona kernels used board files.''' (They did have some partial DT support, but it was never used for any production devices.)


== Getting the kernel source ==
== Kernel compilation toolchain ==


(WIP)
To compile the kernel for the device, we will need a compiler toolchain for the ARM architecture.
 
For convenience, we will use pmbootstrap (the postmarketOS build tool) with its envkernel.sh script, which handles toolchain setup automatically (everything is installed in a chroot managed by pmbootstrap, so no manual work needs to be done on the host system), and gives us the extra benefit of building a ready initramfs and rootfs image to boot with.
 
=== mainline-tools setup ===
 
[https://uart.sh serialuart] uses a convenience script that wraps around envkernel.sh and automates the setup process and most commonly used commands: [https://github.com/refractionware/linux/tree/meta/mt mainline-tools].
 
This script sets up a couple of aliases, including:
 
* <code>mb</code> (<code>mainline-build</code>) - shorthand for <code>make -j$(nproc)</code>, used to build the kernel.
* <code>mp</code> (<code>mainline-package</code>) - creates a boot.img image for the built kernel.
* <code>mf</code> (<code>mainline-flash</code>) - flashes the boot.img generated with <code>mp</code>.
* <code>mbp</code> (<code>mainline-build-package</code>) - updates the postmarketOS kernel package (<code>linux-postmarketos-brcm-kona</code>) based on the build done with <code>mb</code>.
* <code>msp</code> (<code>mainline-sideload-pkg</code>) - sideloads the latest kernel package (needed to update modules).
 
{{note|A note about kernel packaging:
 
* boot.img only contains the kernel and built-in drivers (Y in defconfig).
* The kernel package (linux-postmarketos-brcm-kona) contains modules (M in defconfig).
 
Flashing boot.img (<code>mp</code> and <code>mf</code>) will only update built-in modules; to update non-built-in modules on the device, use <code>mbp</code> and <code>msp</code> to sideload over USB (or regenerate the image with <code>pmbootstrap install</code>).}}
 
To set up that script, run the following commands:
 
<syntaxhighlight lang="shell-session">
$ mkdir ~/code
$ cd ~/code
$ git clone https://gitlab.postmarketos.org/postmarketOS/pmbootstrap  # git clone is needed for envkernel.sh, even if you already have pmbootstrap installed
$ wget https://raw.githubusercontent.com/refractionware/linux/refs/heads/meta/mt/mt.sh
$ source ~/code/mt.sh
</syntaxhighlight>
 
When you start a session, run <code>source ~/code/mt.sh</code> to enable mainline-tools.
 
== Getting the mainline kernel source ==
 
Broadcom Kona development happens in a close-to-mainline fork hosted on GitHub: [https://github.com/bcm-kona-mainline/linux bcm-kona-mainline/linux]
 
Clone the kernel from GitHub:
 
<syntaxhighlight lang="shell-session">
$ cd ~/code
$ git clone https://github.com/bcm-kona-mainline/linux
</syntaxhighlight>
 
== Getting the downstream kernel source ==
 
You will need to reference the downstream kernel for your device for the porting process; most likely someone has uploaded it to GitHub (look for android_kernel_(vendor)_(codename) repos).
 
Once you have the kernel downloaded, find the '''defconfig''' and '''board file''' for your device.
 
* The defconfig is in <code>arch/arm/configs</code>; if you got a CyanogenMod/LineageOS kernel, look for a <code>cyanogenmod_XXX_defconfig</code>; otherwise, there should be a config with a name like <code>bcmXXXXX_codename_revXX_defconfig</code> (pick the highest revision number).
* The board file is in <code>arch/arm/mach-PLATFORM/board-ss_CODENAME.c</code> (or just <code>board-CODENAME.c</code>), where PLATFORM is <code>capri</code> for BCM218xx, <code>rhea</code> for BCM21654, <code>hawaii</code> for BCM21664 and <code>java</code> for BCM23550.
 
== Adding a device package in postmarketOS ==
 
Run <code>pmbootstrap init</code>. When asked for the device, provide the vendor and codename.
 
* If you're asked to create a new device, follow the [https://wiki.postmarketos.org/wiki/Porting_to_a_new_device Porting Guide], using the <code>linux-postmarketos-brcm-kona</code> mainline kernel package.
* If this ends up selecting an existing downstream/archived device, you'll need to update the device package - see [https://gitlab.postmarketos.org/postmarketOS/pmaports/-/merge_requests/8981/diffs?commit_id=fc2eb16282fe1b9dea7fb010533568899fad1bcb the commit doing this for samsung-baffinlite] as an example.
 
== Adding a DTS for your device ==
 
The DTS describes the hardware of the device. DTS files for Broadcom-based devices are located in <code>arch/arm/boot/dts/broadcom</code>.
 
{{hint|<code>[https://github.com/bcm-kona-mainline/linux/blob/kona/7.1/arch/arm/boot/dts/broadcom/bcm23550-samsung-baffinlite.dts bcm23550-samsung-baffinlite.dts]</code> is a good reference point for DTSes for all Kona chips.}}
 
Create a new file in <code>arch/arm/boot/dts/broadcom</code> for your device with the filename <code>(soc)-(vendor)-(codename).dts</code> - for example, for a BCM21664 device with the codename samsung-kylepro, the filename will be <code>bcm21664-samsung-kylepro.dts</code>. Start from the following template (remove the comments when you're done):
 
<syntaxhighlight lang="devicetree">
// SPDX-License-Identifier: GPL-2.0-only
/*
* Common device tree for Samsung Galaxy Trend Plus/S Duos 2
* (kylepro/kyleprods).
*
* Copyright (c) 2026 Your Name <youremail@example.com>
*/
 
/dts-v1/;
 
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/leds/common.h>
 
// Depending on which SoC your device uses, change the following to:
// BCM2166x - "bcm21664.dtsi"
// BCM23550 - "bcm23550.dtsi"
// BCM281x5 - "bcm11351.dtsi"
#include "bcm21664.dtsi"
 
/ {
model = "Vendor Codename (MODEL-NUMBER)";
compatible = "vendor,codename", "brcm,bcm21664";  // remember to change the SoC compatible
chassis-type = "handset";  // usually "handset" (smartphone) or "tablet"
 
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x30000000>; /* 768 MB; CHANGEME */
};
 
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
 
coprocessor_mem: coprocessor-mem@80000000 {
reg = <0x80000000 0x1e00000>;
no-map;
};
 
secure_mem: secure-mem@9d800000 {
reg = <0x9d800000 0x100000>;
no-map;
};
 
mobicore: mobicore@9d900000 {
reg = <0x9d900000 0x300000>;
no-map;
};
};
};
</syntaxhighlight>
 
Then, open <code>arch/arm/boot/dts/broadcom/Makefile</code>, and locate the following section:
 
<syntaxhighlight lang="makefile">
dtb-$(CONFIG_ARCH_BCM_MOBILE) += \
bcm28155-ap.dtb \
bcm21664-garnet.dtb \
bcm21664-samsung-kylepro.dtb \
bcm23550-samsung-baffinlite.dtb \
bcm23550-sparrow.dtb
</syntaxhighlight>
 
Add your DTS to the list, changing <code>.dts</code> to <code>.dtb</code>.
 
=== Filling out the memory node ===
 
To get the memory size, as well as reserved regions, run the following command under a downstream kernel (e.g. under TWRP):
 
<syntaxhighlight lang="shell-session">
$ cat /proc/iomem
81e00000-afffffff : System RAM
  81e08000-823ec57b : Kernel code
  8251c000-826d72ff : Kernel data
</syntaxhighlight>
 
From the above, the RAM would begin at <code>81e00000</code> and have a size of <code>(0xafffffff+1) - 0x81e00000 = <b>0x2e200000</b></code>.
 
{{todo|A lot of Samsung device kernels seem to be configured with a mem base of 0x81e00000, but in ATAG_MEM the 0x80000000 - 0x81e00000 chunk is also reported as a separate memory block as well.
 
This memory region is likely used for storing coprocessor firmware blobs and communication with the modem and other coprocessors (see {{downstream|baffinlite|arch/arm/mach-java/include/mach/comms/platform_mconfig_hawaii.h}}).
 
While from initial testing it seems safe to use with the modem disabled, I have included a <code>coprocessor_mem</code> block in the DTS snippet above to block out this region.
 
TODO - figure out how to print ATAGs without serial. Typically they get printed over UART by the bootloader right before loading the kernel.}}
 
=== UART ===
 
UART/serial is most notably used for sending debug logs over USB, but is also used for talking to the Bluetooth and GPS chip.
 
Check the downstream board file; look for a struct of type <code>plat_serial8250_port</code>. It should look something like this:
 
<syntaxhighlight lang="c">
static struct plat_serial8250_port hawaii_uart_platform_data[] = {
HAWAII_8250PORT(UART0, UARTB_PERI_CLK_NAME_STR, 48000000,
"bluetooth", NULL),
HAWAII_8250PORT(UART1, UARTB2_PERI_CLK_NAME_STR, 26000000,
"gps", &power_save_enable),
HAWAII_8250PORT(UART2, UARTB3_PERI_CLK_NAME_STR, 26000000,
"console", NULL),
{
.flags = 0,
},
}
</syntaxhighlight>
 
Note the numbers in the XXX_8250PORT macros; these represent the clock frequency.
 
Also note the names given to the ports; there are a hint for what the ports are used for. As seen above, the layout is usually - uartb for Bluetooth, uartb2 for GPS and uartb3 for serial console.
 
At the bottom of your DTS, add (change clock frequencies to match board file):
 
<syntaxhighlight lang="devicetree">
&uartb {
status = "okay";
clock-frequency = <48000000>;
};
 
&uartb2 {
status = "okay";
clock-frequency = <26000000>;
};
 
&uartb3 {
status = "okay";
clock-frequency = <26000000>;
};
</syntaxhighlight>
 
=== PMIC ===
 
Most Kona devices use BCM590xx series PMICs: BCM59054 for BCM21664/BCM23550, BCM59056 for BCM281xx and BCM59039 for BCM21654. The only known exceptions are the GT-I9150 (BCM281xx device with a BCM59054 and SMB358 for charging) and the GT-S6810 (Dialog DA2083).
 
The PMIC is connected to a separate PMIC I2C bus (or BSC, as Broadcom calls their "I2C-compatible" bus):
 
<syntaxhighlight lang="devicetree">
 
&pmu_bsc {
clock-frequency = <3400000>; /* 3.2mhz? Downstream refers to this as a HS bus, but it doesn't give any speed information other than that. */
 
pinctrl-0 = <&pmbsc_pins>;
pinctrl-names = "default";
 
status = "okay";
 
pmu: pmu@8 {
compatible = "brcm,bcm59054";
reg = <0x08>;
interrupt-parent = <&gpio>;
interrupts = <29 IRQ_TYPE_EDGE_FALLING>;
 
regulators {
/* ... */
};
};
};
</syntaxhighlight>
 
PMIC configuration is stored in a separate file in downstream, beginning with <code>board-bcm59xxx-(...).c</code> (same directory as the board file). Every regulator is described in this file, in <code>regulator_init_data</code> structs:
 
<syntaxhighlight lang="c">
 
__weak struct regulator_consumer_supply rf_supply[] = {
{.supply = "rf"},
};
static struct regulator_init_data bcm59xxx_rfldo_data = {
.constraints = {
.name = "rfldo",
.min_uV = 1300000,
.max_uV = 3300000,
.valid_ops_mask = REGULATOR_CHANGE_STATUS |
REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_NORMAL |
REGULATOR_MODE_IDLE |
REGULATOR_MODE_STANDBY,
.always_on = 0,
.initial_mode = REGULATOR_MODE_STANDBY,
},
.num_consumer_supplies = ARRAY_SIZE(rf_supply),
.consumer_supplies = rf_supply,
};
</syntaxhighlight>
 
Equivalent DTS fragment:
 
<syntaxhighlight lang="devicetree">
rfldo_reg: rfldo {
regulator-min-microvolt = <1300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
/* regulator-allowed-modes = <0x2 0x4 0x8>; */ /* NORMAL, IDLE, STANDBY */
/* regulator-initial-mode = <0x8>; */ /* REGULATOR_MODE_STANDBY */
};
</syntaxhighlight>
 
Repeat for all regulator types; see DT bindings for reference ([https://github.com/bcm-kona-mainline/linux/blob/kona/7.1/Documentation/devicetree/bindings/regulator/brcm%2Cbcm59054.yaml BCM59054], [https://github.com/bcm-kona-mainline/linux/blob/kona/7.1/Documentation/devicetree/bindings/regulator/brcm%2Cbcm59056.yaml BCM59056]).
 
=== SDIO/Storage ===
 
SDIO is used for internal storage, the SD card and WiFi.
 
In the downstream board file, look for <code>sdio_platform_cfg</code> structs. Here's an example:
 
<syntaxhighlight lang="c">
 
static struct sdio_platform_cfg hawaii_sdio_param[] = {
{
.id = 0,
.data_pullup = 0,
.cd_gpio = SD_CARDDET_GPIO_PIN,
.devtype = SDIO_DEV_TYPE_SDMMC,
.flags = KONA_SDIO_FLAGS_DEVICE_REMOVABLE,
.peri_clk_name = "sdio1_clk",
.ahb_clk_name = "sdio1_ahb_clk",
.sleep_clk_name = "sdio1_sleep_clk",
.peri_clk_rate = 48000000,
/*The SD card regulator*/
.vddo_regulator_name = "vdd_sdio",
/*The SD controller regulator*/
.vddsdxc_regulator_name = "vdd_sdxc",
.configure_sdio_pullup = configure_sdio_pullup,
},
{
.id = 1,
.data_pullup = 0,
.is_8bit = 1,
.devtype = SDIO_DEV_TYPE_EMMC,
.flags = KONA_SDIO_FLAGS_DEVICE_NON_REMOVABLE ,
.peri_clk_name = "sdio2_clk",
.ahb_clk_name = "sdio2_ahb_clk",
.sleep_clk_name = "sdio2_sleep_clk",
.peri_clk_rate = 52000000,
},
{
.id = 2,
.data_pullup = 0,
.devtype = SDIO_DEV_TYPE_WIFI,
.flags = KONA_SDIO_FLAGS_DEVICE_REMOVABLE,
.peri_clk_name = "sdio3_clk",
.ahb_clk_name = "sdio3_ahb_clk",
.sleep_clk_name = "sdio3_sleep_clk",
.peri_clk_rate = 48000000,
#ifdef CONFIG_BRCM_UNIFIED_DHD_SUPPORT
.wifi_gpio = {
.reset          = 3,
.reg            = -1,
.host_wake      = 74,
.shutdown      = -1,
},
.register_status_notify = hawaii_wifi_status_register,
#endif
        },
};
</syntaxhighlight>
 
Things of note:
 
* <code>.peri_clk_rate</code> is equivalent to <code>max-frequency</code> in DT;
* <code>.is_8bit = 1</code> is equivalent to <code>bus-width = &lt;8&gt;</code>;
* <code>.cd_gpio</code> contains the card detect GPIO for the SD card slot. It is usually defined as a macro, the value is defined earlier in the board file. Here it's <code>91</code>.
* <code>.vddo_regulator_name</code> and <code>.vddsdxc_regulator_name</code> become <code>vmmc-supply</code> and <code>vqmmc-supply</code> respectively. To find which regulator they are attached to, look up the names in your board file; you might find a struct like such: <syntaxhighlight lang="c">struct regulator_consumer_supply sd_supply[] = {
{.supply = "sdldo_uc"},
REGULATOR_SUPPLY("vddmmc", "sdhci.3"), /* 0x3f1b0000.sdhci */
{.supply = "vdd_sdio"},
};
 
struct regulator_consumer_supply sdx_supply[] = {
{.supply = "sdxldo_uc"},
REGULATOR_SUPPLY("vddo", "sdhci.3"), /* 0x3f1b0000.sdhci */
{.supply = "vdd_sdxc"},
};</syntaxhighlight>
The first listed supply should show you the name of the BCM59xxx supply - in this case, <code>sdldo</code> and <code>sdxldo</code>.
 
Equivalent mainline DTS:
 
<syntaxhighlight lang="devicetree">
 
/* SD card */
&sdio1 {
max-frequency = <48000000>;
 
cd-gpios = <&gpio 91 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
vmmc-supply = <&sdldo_reg>;
vqmmc-supply = <&sdxldo_reg>;
 
    /* NOTE: skip pinctrl here and in the other nodes for BCM28155, we don't have ready-made pinctrl definitions for it yet */
pinctrl-0 = <&sd_width4_pins>;
pinctrl-names = "default";
 
status = "okay";
};
 
/* Internal storage */
&sdio2 {
max-frequency = <52000000>;
 
keep-power-in-suspend;
non-removable;
bus-width = <8>;
 
pinctrl-0 = <&mmc0_width8_pins>;
pinctrl-names = "default";
 
status = "okay";
};
 
/* WiFi */
&sdio3 {
max-frequency = <48000000>;
   
#address-cells = <1>;
#size-cells = <0>;
 
pinctrl-0 = <&mmc1_width4_pins>;
pinctrl-names = "default";
 
    // NOTE: Non-removable is hardcoded for the WiFi SDIO in the downstream driver, even if the board file claims it's removable.
keep-power-in-suspend;
non-removable;
 
status = "okay";
};
</syntaxhighlight>
 
=== I2C/BSC ===
 
Most sensors, as well as the touchscreen, are connected over I2C. Broadcom SoCs actually have an I2C-compatible bus called BSC.
 
Finding exactly which sensors are used is a bit difficult, because board files tend to have data for a lot of sensors that is unused. You will need to look for sections guarded with <code>#ifdef CONFIG_...</code>; for the config option values, reference the downstream defconfig file for your device.
 
In general, it's best to look through the defconfig first and find all the relevant options for sensors (look up keywords like SENSOR, PROXIMITY, ACCEL, MAG, LIGHT, TOUCH, INPUT...).
 
(Alternatively, if you have a Samsung device, reference [https://docs.google.com/spreadsheets/d/1NioX9a2vgbEcO_Zs9COUFO-n3a2Rqf1JWWU1cfsktLM/edit?gid=0#gid=0 the spreadsheet].)
 
In the board file, look for <code>i2c_board_info</code> structs:
 
<syntaxhighlight lang="c">
#if defined(CONFIG_SENSORS_BMM050)
#define BMA2X2_SLAVE_ADDR  0x10
#else
#define BMA2X2_SLAVE_ADDR  0x18
#endif
 
static struct i2c_board_info __initdata bsc3_i2c_boardinfo[] =
{
 
#if defined(CONFIG_SENSORS_BMC150)
#if defined(CONFIG_SENSORS_BMA2X2)
        {
I2C_BOARD_INFO("bma2x2", BMA2X2_SLAVE_ADDR),
.platform_data = &bss_bma2x2,
        },
#endif
#if defined(CONFIG_SENSORS_BMM050)
        {
I2C_BOARD_INFO("bmm050", 0x12),
.platform_data = &bss_bmm050,
        },
#endif       
#endif
 
#if defined  (CONFIG_SENSORS_HSCDTD006A) || defined(CONFIG_SENSORS_HSCDTD008A)
{
I2C_BOARD_INFO("hscd_i2c", 0x0c),
.platform_data = &hscd_i2c_platform_data,             
},
#endif
 
#if defined(CONFIG_SENSORS_GP2AP002)
        {
            I2C_BOARD_INFO("gp2ap002",0x44),
            .platform_data = &gp2ap002_platform_data,
        }
#endif
}
</syntaxhighlight>
 
Do a <code>grep -iR</code> with the codename in <code>drivers</code> in mainline to see if you can spot a driver for the sensor you want to add. Many sensors from that era are already supported in mainline. You can look at other device trees (<code>arch/arm/boot/dts</code> and <code>arch/arm64/boot/dts</code>) as well as DT bindings (<code>Documentation/devicetree/bindings</code>) to figure out how to add them to your DTS.
 
For the exact values to use in DT, you'll have to look at the <code>platform_data</code> struct, and sometimes at the driver for the peripheral (which you can usually find by grepping the downstream source code for the model number/driver name).
 
<syntaxhighlight lang="devicetree">
&bsc3 {
clock-frequency = <400000>;
 
    // NOTE: again, pinctrl only applies to BCM21664/BCM23550 at the moment
pinctrl-0 = <&bsc3_pins>;
pinctrl-names = "default";
 
status = "okay";
 
magnetometer@c {
compatible = "alps,hscdtd008a";
reg = <0x0c>;
};
 
accel@18 {
compatible = "bosch,bma254";
reg = <0x18>;
       
/* ... */
};
 
proximity@44 {
compatible = "sharp,gp2ap002s00f";
reg = <0x44>;
       
/* ... */
};
};
</syntaxhighlight>
 
=== USB ===
 
USB comprises of 3 parts:
 
* The USB OTG controller (usbotg node);
* The USB PHY (usbphy node);
* The USB mode switch (not all devices - BCM59039 devices don't seem to be paired with a switch).
 
Commonly used switches include the FSA9480/9485, TSUxxxx and RTxxxx. Look them up in your defconfig (keywords: FSA, TSU, RT).
 
On Samsung devices, the switches are often connected over I2C-GPIO (which is an I2C bus bitbanged over GPIO pins).  Example from baffinlite:
 
<syntaxhighlight lang="c">
#define GPIO_FSA9485_I2C_SDA 113
#define GPIO_FSA9485_I2C_SCL 114
#define GPIO_FSA9485_INT 56
 
/* ... */
 
static struct i2c_board_info  __initdata micro_usb_i2c_devices_info[]  = {
{
I2C_BOARD_INFO("fsa9485", 0x4A >> 1),
.platform_data = &fsa9485_pdata,
.irq = gpio_to_irq(GPIO_FSA9485_INT),
},
};
 
static struct i2c_gpio_platform_data fsa_i2c_gpio_data={
.sda_pin = GPIO_FSA9485_I2C_SDA,
.scl_pin = GPIO_FSA9485_I2C_SCL,
.udelay = 2,
};
</syntaxhighlight>
 
Note - <code>0x4A >> 1</code> means a bit shift, you can paste it into the Python interpreter and it will give you the value (<code>hex(0x4A >> 1)</code> gives <code>0x25</code>).
 
In mainline, this becomes:
 
<syntaxhighlight lang="devicetree">
/ {
/* (add this under the / node ) */
 
    /* ... */
 
/* USB switch */
i2c-musb {
#address-cells = <1>;
#size-cells = <0>;
compatible = "i2c-gpio";
sda-gpios = <&gpio 113 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio 114 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
 
musb@25 {
compatible = "fcs,fsa9480";
reg = <0x25>;
interrupt-parent = <&gpio>;
interrupts = <56 IRQ_TYPE_LEVEL_HIGH>;
 
connector {
compatible = "samsung,usb-connector-11pin",
    "usb-b-connector";
label = "micro-USB";
type = "micro";
 
ports {
#address-cells = <1>;
#size-cells = <0>;
 
port@0 {
reg = <0>;
 
muic_to_usb: endpoint {
remote-endpoint = <&usb_to_muic>;
};
};
};
};
 
};
};
    /* ... */
};
</syntaxhighlight>
 
Once you have the switch, you can add the remaining USB nodes:
 
<syntaxhighlight lang="devicetree">
&usbotg {
vusb_d-supply = <&usbldo_reg>;
vusb_a-supply = <&iosr1_reg>;
vbus-supply = <&vbus_reg>;
dr_mode = "otg";
role-switch-default-mode = "peripheral";
usb-role-switch;
status = "okay";
 
    // if your device has no switch, drop the port node below
port {
usb_to_muic: endpoint {
remote-endpoint = <&muic_to_usb>;
};
};
};
 
&usbphy {
status = "okay";
};
</syntaxhighlight>
 
=== WiFi and Bluetooth ===
 
Most Broadcom devices use Broadcom WiFi/Bluetooth chips from the BCM43xx series. (Look up BCM43 in your defconfig, or reference downstream kernel dmesg for the exact model.)
 
The below examples are for BCM4330. If you have another chip, you can grep for it in the mainline kernel (<code>arch/arm/boot/dts</code>) and see what compatibles are used by other devices.
 
The Bluetooth chip goes under the UART bus that was labeled as "bluetooth":
 
<syntaxhighlight lang="devicetree">
&uartb {
status = "okay";
clock-frequency = <48000000>;
 
bluetooth {
compatible = "brcm,bcm4330-bt";
 
shutdown-gpios = <&gpio 28 GPIO_ACTIVE_HIGH>;
device-wakeup-gpios = <&gpio 32 GPIO_ACTIVE_HIGH>;
host-wakeup-gpios = <&gpio 72 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio 71 GPIO_ACTIVE_LOW>;
interrupt-parent = <&gpio>;
interrupts = <72 IRQ_TYPE_EDGE_FALLING>;
};
};
</syntaxhighlight>
 
To get the GPIOs:
 
* Look for <code>bcmbt_platform_data</code>; <code>.bt_wake_gpio</code> becomes <code>device-wakeup-gpios</code>, and <code>.host_wake_gpio</code> becomes <code>host-wakeup-gpios</code>.
* Look for <code>bcmbt_rfkill_platform_data</code>; <code>.vreg_gpio</code> becomes <code>shutdown-gpios</code>, and <code>.n_reset_gpio</code> becomes <code>reset-gpios</code>.
 
For WiFi, add the following under the <code>/</code> node:
 
<syntaxhighlight lang="devicetree">
/ {
    /* ... */
   
/* GPIO regulator for WiFi */
wl_reg: regulator-gpio-wlan {
compatible = "regulator-fixed";
regulator-name = "WL_REG_ON";
regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <3000000>;
startup-delay-us = <100000>;
gpio = <&gpio 3 GPIO_ACTIVE_HIGH>;
enable-active-high;
};
   
    /* ... */
};
</syntaxhighlight>
 
(Get the GPIO from <code>board_wifi_info</code>, member <code>.wl_reset_gpio</code>).
 
Then, update the SDIO node for WiFi:
 
<syntaxhighlight lang="devicetree">
/* WiFi */
&sdio3 {
max-frequency = <48000000>;
 
/* Actually a regulator for the WiFi chip */
vmmc-supply = <&wl_reg>;
 
#address-cells = <1>;
#size-cells = <0>;
 
pinctrl-0 = <&mmc1_width4_pins>;
pinctrl-names = "default";
 
keep-power-in-suspend;
non-removable;
 
status = "okay";
 
wifi@1 {
compatible = "brcm,bcm4330-fmac", "brcm,bcm4329-fmac";
reg = <1>;
 
interrupt-parent = <&gpio>;
interrupts = <74 IRQ_TYPE_EDGE_FALLING>;
interrupt-names = "host-wake";
};
};
</syntaxhighlight>
 
(Get the interrupt from <code>board_wifi_info</code>, member <code>.host_wake_gpio</code>).
 
=== GPIO keys ===
 
Many Samsung devices use GPIO-based keys for the volume up/down and home keys. (The power button is handled by the PMIC, and the touchkeys (menu/back next to home button) are usually handled by the touchscreen).
 
<syntaxhighlight lang="c">
#define GPIO_KEYS_SETTINGS { \
      { KEY_VOLUMEUP,9, 1, "VOLUMEUP", EV_KEY, 0, 64}, \
{ KEY_HOME, 10, 1, "HOME", EV_KEY, 0, 64}, \
      { KEY_VOLUMEDOWN,11, 1, "VOLUMEDOWN", EV_KEY, 0, 64}, \
}
 
#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
static struct gpio_keys_button board_gpio_keys[] = GPIO_KEYS_SETTINGS;
/* ... */
#endif
</syntaxhighlight>
 
Equivalent DT fragment:
 
<syntaxhighlight lang="devicetree">
/ {
    /* ... */
 
/* Buttons */
gpio-keys {
compatible = "gpio-keys";
 
key-volume-up {
label = "Volume Up";
gpios = <&gpio 9 GPIO_ACTIVE_LOW>;
linux,code = <KEY_VOLUMEUP>;
};
 
key-home {
label = "Home";
gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
linux,code = <KEY_HOMEPAGE>;
};
 
key-volume-down {
label = "Volume Down";
gpios = <&gpio 11 GPIO_ACTIVE_LOW>;
linux,code = <KEY_VOLUMEDOWN>;
};
};
   
    /* ... */
};
</syntaxhighlight>
 
=== Keypad ===
 
{{note|Keypad is only added for BCM21664/BCM23550 at the moment; it's most likely the same for BCM28155, just a matter of adding the right node like [https://github.com/bcm-kona-mainline/linux/commit/86a15664bdfe457bcd994b2ae72f722509259b53 in the equivalent bcm21664-common.dtsi commit].}}
 
Some devices use the built-in keypad peripheral for volume up/down keys. If you have <code>CONFIG_KEYBOARD_BCM</code> enabled in downstream defconfig, look for <code>bcm_keymap</code> in your board file:
 
<syntaxhighlight lang="c">
#ifdef CONFIG_KEYBOARD_BCM
static struct bcm_keymap hawaii_keymap[] = {
{BCM_KEY_ROW_0, BCM_KEY_COL_0, "unused", 0},
{BCM_KEY_ROW_0, BCM_KEY_COL_1, "Vol Down Key", KEY_VOLUMEDOWN},
{BCM_KEY_ROW_0, BCM_KEY_COL_2, "unused", 0},
{BCM_KEY_ROW_0, BCM_KEY_COL_3, "Vol Up Key", KEY_VOLUMEUP},
{BCM_KEY_ROW_0, BCM_KEY_COL_4, "unused", 0},
{BCM_KEY_ROW_0, BCM_KEY_COL_5, "unused", 0},
    /* ... */
};
 
static struct bcm_keypad_platform_info hawaii_keypad_data = {
.row_num = 1,
.col_num = 5,
.keymap = hawaii_keymap,
.bcm_keypad_base = (void *)__iomem HW_IO_PHYS_TO_VIRT(KEYPAD_BASE_ADDR),
};
 
#endif
</syntaxhighlight>
 
Equivalent DTS:
 
<syntaxhighlight lang="devicetree">
&keypad {
keypad,num-rows = <1>;
keypad,num-columns = <5>;
 
linux,keymap = <MATRIX_KEY(0x00, 0x01, KEY_VOLUMEDOWN)>,
      <MATRIX_KEY(0x00, 0x03, KEY_VOLUMEUP)>;
 
status = "okay";
};
</syntaxhighlight>
 
== Building the kernel ==
 
To apply the pmOS Broadcom Kona defconfig:
 
<syntaxhighlight lang="shell-session">
$ make bcmkona_pmos_defconfig
</syntaxhighlight>
 
To build the kernel:
 
<syntaxhighlight lang="shell-session">
$ make -j$(nproc)
-- or, with mainline-tools: --
$ mb
</syntaxhighlight>
 
If you have mainline-tools, you can flash the kernel with:
 
<syntaxhighlight lang="shell-session">
$ mp
$ mf
</syntaxhighlight>

Latest revision as of 21:56, 23 August 2026

This page goes over the process of adding support for a Broadcom Kona device to the mainline Linux kernel.

General outline of the process:

  • Get the downstream/vendor kernel source code
    • Identify your device's board files
  • Get the mainline kernel source code
  • Add a DTS for your device based on values from downstream
  • Build the kernel and flash it to the device
  • Test it!

Introduction

In the mainline kernel, a device's components are described in a structure known as the device tree (or devicetree, as is the official spelling). Device trees are compiled into DTBs (device tree blobs) from DTS (device tree source) files.

Adding support for a new device involves creating a DTS for that device - this is the process covered in this guide.

Device trees appeared in the kernel around version 3.4, and didn't see widespread use in vendor kernels until a few LTS versions later. Before device trees, each device usually had a board file - a C file that contained structs with driver data and initialization functions.

The downstream Kona kernels used board files. (They did have some partial DT support, but it was never used for any production devices.)

Kernel compilation toolchain

To compile the kernel for the device, we will need a compiler toolchain for the ARM architecture.

For convenience, we will use pmbootstrap (the postmarketOS build tool) with its envkernel.sh script, which handles toolchain setup automatically (everything is installed in a chroot managed by pmbootstrap, so no manual work needs to be done on the host system), and gives us the extra benefit of building a ready initramfs and rootfs image to boot with.

mainline-tools setup

serialuart uses a convenience script that wraps around envkernel.sh and automates the setup process and most commonly used commands: mainline-tools.

This script sets up a couple of aliases, including:

  • mb (mainline-build) - shorthand for make -j$(nproc), used to build the kernel.
  • mp (mainline-package) - creates a boot.img image for the built kernel.
  • mf (mainline-flash) - flashes the boot.img generated with mp.
  • mbp (mainline-build-package) - updates the postmarketOS kernel package (linux-postmarketos-brcm-kona) based on the build done with mb.
  • msp (mainline-sideload-pkg) - sideloads the latest kernel package (needed to update modules).
Note A note about kernel packaging:
  • boot.img only contains the kernel and built-in drivers (Y in defconfig).
  • The kernel package (linux-postmarketos-brcm-kona) contains modules (M in defconfig).
Flashing boot.img (mp and mf) will only update built-in modules; to update non-built-in modules on the device, use mbp and msp to sideload over USB (or regenerate the image with pmbootstrap install).

To set up that script, run the following commands:

$ mkdir ~/code
$ cd ~/code
$ git clone https://gitlab.postmarketos.org/postmarketOS/pmbootstrap  # git clone is needed for envkernel.sh, even if you already have pmbootstrap installed
$ wget https://raw.githubusercontent.com/refractionware/linux/refs/heads/meta/mt/mt.sh
$ source ~/code/mt.sh

When you start a session, run source ~/code/mt.sh to enable mainline-tools.

Getting the mainline kernel source

Broadcom Kona development happens in a close-to-mainline fork hosted on GitHub: bcm-kona-mainline/linux

Clone the kernel from GitHub:

$ cd ~/code
$ git clone https://github.com/bcm-kona-mainline/linux

Getting the downstream kernel source

You will need to reference the downstream kernel for your device for the porting process; most likely someone has uploaded it to GitHub (look for android_kernel_(vendor)_(codename) repos).

Once you have the kernel downloaded, find the defconfig and board file for your device.

  • The defconfig is in arch/arm/configs; if you got a CyanogenMod/LineageOS kernel, look for a cyanogenmod_XXX_defconfig; otherwise, there should be a config with a name like bcmXXXXX_codename_revXX_defconfig (pick the highest revision number).
  • The board file is in arch/arm/mach-PLATFORM/board-ss_CODENAME.c (or just board-CODENAME.c), where PLATFORM is capri for BCM218xx, rhea for BCM21654, hawaii for BCM21664 and java for BCM23550.

Adding a device package in postmarketOS

Run pmbootstrap init. When asked for the device, provide the vendor and codename.

  • If you're asked to create a new device, follow the Porting Guide, using the linux-postmarketos-brcm-kona mainline kernel package.
  • If this ends up selecting an existing downstream/archived device, you'll need to update the device package - see the commit doing this for samsung-baffinlite as an example.

Adding a DTS for your device

The DTS describes the hardware of the device. DTS files for Broadcom-based devices are located in arch/arm/boot/dts/broadcom.

Hint bcm23550-samsung-baffinlite.dts is a good reference point for DTSes for all Kona chips.

Create a new file in arch/arm/boot/dts/broadcom for your device with the filename (soc)-(vendor)-(codename).dts - for example, for a BCM21664 device with the codename samsung-kylepro, the filename will be bcm21664-samsung-kylepro.dts. Start from the following template (remove the comments when you're done):

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Common device tree for Samsung Galaxy Trend Plus/S Duos 2
 * (kylepro/kyleprods).
 *
 * Copyright (c) 2026 Your Name <youremail@example.com>
 */

/dts-v1/;

#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/leds/common.h>

// Depending on which SoC your device uses, change the following to:
// BCM2166x - "bcm21664.dtsi"
// BCM23550 - "bcm23550.dtsi"
// BCM281x5 - "bcm11351.dtsi"
#include "bcm21664.dtsi"

/ {
	model = "Vendor Codename (MODEL-NUMBER)";
	compatible = "vendor,codename", "brcm,bcm21664";  // remember to change the SoC compatible
	chassis-type = "handset";  // usually "handset" (smartphone) or "tablet"

	memory@80000000 {
		device_type = "memory";
		reg = <0x80000000 0x30000000>; /* 768 MB; CHANGEME */
	};

	reserved-memory {
		#address-cells = <1>;
		#size-cells = <1>;
		ranges;

		coprocessor_mem: coprocessor-mem@80000000 {
			reg = <0x80000000 0x1e00000>;
			no-map;
		};

		secure_mem: secure-mem@9d800000 {
			reg = <0x9d800000 0x100000>;
			no-map;
		};

		mobicore: mobicore@9d900000 {
			reg = <0x9d900000 0x300000>;
			no-map;
		};
	};
};

Then, open arch/arm/boot/dts/broadcom/Makefile, and locate the following section:

dtb-$(CONFIG_ARCH_BCM_MOBILE) += \
	bcm28155-ap.dtb \
	bcm21664-garnet.dtb \
	bcm21664-samsung-kylepro.dtb \
	bcm23550-samsung-baffinlite.dtb \
	bcm23550-sparrow.dtb

Add your DTS to the list, changing .dts to .dtb.

Filling out the memory node

To get the memory size, as well as reserved regions, run the following command under a downstream kernel (e.g. under TWRP):

$ cat /proc/iomem
81e00000-afffffff : System RAM
  81e08000-823ec57b : Kernel code
  8251c000-826d72ff : Kernel data

From the above, the RAM would begin at 81e00000 and have a size of (0xafffffff+1) - 0x81e00000 = 0x2e200000.

TODO TODO: A lot of Samsung device kernels seem to be configured with a mem base of 0x81e00000, but in ATAG_MEM the 0x80000000 - 0x81e00000 chunk is also reported as a separate memory block as well.

This memory region is likely used for storing coprocessor firmware blobs and communication with the modem and other coprocessors (see arch/arm/mach-java/include/mach/comms/platform_mconfig_hawaii.h).

While from initial testing it seems safe to use with the modem disabled, I have included a coprocessor_mem block in the DTS snippet above to block out this region.

TODO - figure out how to print ATAGs without serial. Typically they get printed over UART by the bootloader right before loading the kernel.

UART

UART/serial is most notably used for sending debug logs over USB, but is also used for talking to the Bluetooth and GPS chip.

Check the downstream board file; look for a struct of type plat_serial8250_port. It should look something like this:

static struct plat_serial8250_port hawaii_uart_platform_data[] = {
	HAWAII_8250PORT(UART0, UARTB_PERI_CLK_NAME_STR, 48000000,
					"bluetooth", NULL),
	HAWAII_8250PORT(UART1, UARTB2_PERI_CLK_NAME_STR, 26000000,
				"gps", &power_save_enable),
	HAWAII_8250PORT(UART2, UARTB3_PERI_CLK_NAME_STR, 26000000,
				"console", NULL),
	{
		.flags = 0,
	},
}

Note the numbers in the XXX_8250PORT macros; these represent the clock frequency.

Also note the names given to the ports; there are a hint for what the ports are used for. As seen above, the layout is usually - uartb for Bluetooth, uartb2 for GPS and uartb3 for serial console.

At the bottom of your DTS, add (change clock frequencies to match board file):

&uartb {
	status = "okay";
	clock-frequency = <48000000>;
};

&uartb2 {
	status = "okay";
	clock-frequency = <26000000>;
};

&uartb3 {
	status = "okay";
	clock-frequency = <26000000>;
};

PMIC

Most Kona devices use BCM590xx series PMICs: BCM59054 for BCM21664/BCM23550, BCM59056 for BCM281xx and BCM59039 for BCM21654. The only known exceptions are the GT-I9150 (BCM281xx device with a BCM59054 and SMB358 for charging) and the GT-S6810 (Dialog DA2083).

The PMIC is connected to a separate PMIC I2C bus (or BSC, as Broadcom calls their "I2C-compatible" bus):

&pmu_bsc {
	clock-frequency = <3400000>; /* 3.2mhz? Downstream refers to this as a HS bus, but it doesn't give any speed information other than that. */

	pinctrl-0 = <&pmbsc_pins>;
	pinctrl-names = "default";

	status = "okay";

	pmu: pmu@8 {
		compatible = "brcm,bcm59054";
		reg = <0x08>;
		interrupt-parent = <&gpio>;
		interrupts = <29 IRQ_TYPE_EDGE_FALLING>;

		regulators {
			/* ... */
		};
	};
};

PMIC configuration is stored in a separate file in downstream, beginning with board-bcm59xxx-(...).c (same directory as the board file). Every regulator is described in this file, in regulator_init_data structs:

__weak struct regulator_consumer_supply rf_supply[] = {
	{.supply = "rf"},
};
static struct regulator_init_data bcm59xxx_rfldo_data = {
	.constraints = {
			.name = "rfldo",
			.min_uV = 1300000,
			.max_uV = 3300000,
			.valid_ops_mask = REGULATOR_CHANGE_STATUS |
			REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
			.valid_modes_mask = REGULATOR_MODE_NORMAL |
			REGULATOR_MODE_IDLE |
			REGULATOR_MODE_STANDBY,
			.always_on = 0,
			.initial_mode = REGULATOR_MODE_STANDBY,
			},
	.num_consumer_supplies = ARRAY_SIZE(rf_supply),
	.consumer_supplies = rf_supply,
};

Equivalent DTS fragment:

			rfldo_reg: rfldo {
				regulator-min-microvolt = <1300000>;
				regulator-max-microvolt = <3300000>;
				regulator-always-on;
				/* regulator-allowed-modes = <0x2 0x4 0x8>; */ /* NORMAL, IDLE, STANDBY */
				/* regulator-initial-mode = <0x8>; */ /* REGULATOR_MODE_STANDBY */
			};

Repeat for all regulator types; see DT bindings for reference (BCM59054, BCM59056).

SDIO/Storage

SDIO is used for internal storage, the SD card and WiFi.

In the downstream board file, look for sdio_platform_cfg structs. Here's an example:

static struct sdio_platform_cfg hawaii_sdio_param[] = {
	{
		.id = 0,
		.data_pullup = 0,
		.cd_gpio = SD_CARDDET_GPIO_PIN,
		.devtype = SDIO_DEV_TYPE_SDMMC,
		.flags = KONA_SDIO_FLAGS_DEVICE_REMOVABLE,
		.peri_clk_name = "sdio1_clk",
		.ahb_clk_name = "sdio1_ahb_clk",
		.sleep_clk_name = "sdio1_sleep_clk",
		.peri_clk_rate = 48000000,
		/*The SD card regulator*/
		.vddo_regulator_name = "vdd_sdio",
		/*The SD controller regulator*/
		.vddsdxc_regulator_name = "vdd_sdxc",
		.configure_sdio_pullup = configure_sdio_pullup,
	},
	{
		.id = 1,
		.data_pullup = 0,
		.is_8bit = 1,
		.devtype = SDIO_DEV_TYPE_EMMC,
		.flags = KONA_SDIO_FLAGS_DEVICE_NON_REMOVABLE ,
		.peri_clk_name = "sdio2_clk",
		.ahb_clk_name = "sdio2_ahb_clk",
		.sleep_clk_name = "sdio2_sleep_clk",
		.peri_clk_rate = 52000000,
		},
	{
		.id = 2,
		.data_pullup = 0,
		.devtype = SDIO_DEV_TYPE_WIFI,
		.flags = KONA_SDIO_FLAGS_DEVICE_REMOVABLE,
		.peri_clk_name = "sdio3_clk",
		.ahb_clk_name = "sdio3_ahb_clk",
		.sleep_clk_name = "sdio3_sleep_clk",
		.peri_clk_rate = 48000000,
#ifdef CONFIG_BRCM_UNIFIED_DHD_SUPPORT
		.wifi_gpio = {
			.reset          = 3,
			.reg            = -1,
			.host_wake      = 74,
			.shutdown       = -1,
		},
		.register_status_notify = hawaii_wifi_status_register,
#endif
        },
};

Things of note:

  • .peri_clk_rate is equivalent to max-frequency in DT;
  • .is_8bit = 1 is equivalent to bus-width = <8>;
  • .cd_gpio contains the card detect GPIO for the SD card slot. It is usually defined as a macro, the value is defined earlier in the board file. Here it's 91.
  • .vddo_regulator_name and .vddsdxc_regulator_name become vmmc-supply and vqmmc-supply respectively. To find which regulator they are attached to, look up the names in your board file; you might find a struct like such:
    struct regulator_consumer_supply sd_supply[] = {
    	{.supply = "sdldo_uc"},
    	REGULATOR_SUPPLY("vddmmc", "sdhci.3"), /* 0x3f1b0000.sdhci */
    	{.supply = "vdd_sdio"},
    };
    
    struct regulator_consumer_supply sdx_supply[] = {
    	{.supply = "sdxldo_uc"},
    	REGULATOR_SUPPLY("vddo", "sdhci.3"), /* 0x3f1b0000.sdhci */
    	{.supply = "vdd_sdxc"},
    };
    
The first listed supply should show you the name of the BCM59xxx supply - in this case, sdldo and sdxldo.

Equivalent mainline DTS:

/* SD card */
&sdio1 {
	max-frequency = <48000000>;

	cd-gpios = <&gpio 91 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
	vmmc-supply = <&sdldo_reg>;
	vqmmc-supply = <&sdxldo_reg>;

    /* NOTE: skip pinctrl here and in the other nodes for BCM28155, we don't have ready-made pinctrl definitions for it yet */
	pinctrl-0 = <&sd_width4_pins>;
	pinctrl-names = "default";

	status = "okay";
};

/* Internal storage */
&sdio2 {
	max-frequency = <52000000>;

	keep-power-in-suspend;
	non-removable;
	bus-width = <8>;

	pinctrl-0 = <&mmc0_width8_pins>;
	pinctrl-names = "default";

	status = "okay";
};

/* WiFi */
&sdio3 {
	max-frequency = <48000000>;
    
	#address-cells = <1>;
	#size-cells = <0>;

	pinctrl-0 = <&mmc1_width4_pins>;
	pinctrl-names = "default";

    // NOTE: Non-removable is hardcoded for the WiFi SDIO in the downstream driver, even if the board file claims it's removable.
	keep-power-in-suspend;
	non-removable;

	status = "okay";
};

I2C/BSC

Most sensors, as well as the touchscreen, are connected over I2C. Broadcom SoCs actually have an I2C-compatible bus called BSC.

Finding exactly which sensors are used is a bit difficult, because board files tend to have data for a lot of sensors that is unused. You will need to look for sections guarded with #ifdef CONFIG_...; for the config option values, reference the downstream defconfig file for your device.

In general, it's best to look through the defconfig first and find all the relevant options for sensors (look up keywords like SENSOR, PROXIMITY, ACCEL, MAG, LIGHT, TOUCH, INPUT...).

(Alternatively, if you have a Samsung device, reference the spreadsheet.)

In the board file, look for i2c_board_info structs:

#if defined(CONFIG_SENSORS_BMM050)
#define BMA2X2_SLAVE_ADDR  0x10
#else
#define BMA2X2_SLAVE_ADDR  0x18
#endif

static struct i2c_board_info __initdata bsc3_i2c_boardinfo[] =
{

#if defined(CONFIG_SENSORS_BMC150)
#if defined(CONFIG_SENSORS_BMA2X2)
        {
		I2C_BOARD_INFO("bma2x2", BMA2X2_SLAVE_ADDR),
		.platform_data = &bss_bma2x2,
        },
#endif
#if defined(CONFIG_SENSORS_BMM050)
        {
		I2C_BOARD_INFO("bmm050", 0x12),
		.platform_data = &bss_bmm050,
        },
#endif        
#endif

#if defined  (CONFIG_SENSORS_HSCDTD006A) || defined(CONFIG_SENSORS_HSCDTD008A) 
	{
		I2C_BOARD_INFO("hscd_i2c", 0x0c),
		.platform_data = &hscd_i2c_platform_data,               
	},
 #endif

#if defined(CONFIG_SENSORS_GP2AP002)
        {
             I2C_BOARD_INFO("gp2ap002",0x44),
             .platform_data = &gp2ap002_platform_data,
        }
#endif
}

Do a grep -iR with the codename in drivers in mainline to see if you can spot a driver for the sensor you want to add. Many sensors from that era are already supported in mainline. You can look at other device trees (arch/arm/boot/dts and arch/arm64/boot/dts) as well as DT bindings (Documentation/devicetree/bindings) to figure out how to add them to your DTS.

For the exact values to use in DT, you'll have to look at the platform_data struct, and sometimes at the driver for the peripheral (which you can usually find by grepping the downstream source code for the model number/driver name).

&bsc3 {
	clock-frequency = <400000>;

    // NOTE: again, pinctrl only applies to BCM21664/BCM23550 at the moment
	pinctrl-0 = <&bsc3_pins>;
	pinctrl-names = "default";

	status = "okay";

	magnetometer@c {
		compatible = "alps,hscdtd008a";
		reg = <0x0c>;
	};

	accel@18 {
		compatible = "bosch,bma254";
		reg = <0x18>;
        
		/* ... */
	};

	proximity@44 {
		compatible = "sharp,gp2ap002s00f";
		reg = <0x44>;
        
		/* ... */
	};
};

USB

USB comprises of 3 parts:

  • The USB OTG controller (usbotg node);
  • The USB PHY (usbphy node);
  • The USB mode switch (not all devices - BCM59039 devices don't seem to be paired with a switch).

Commonly used switches include the FSA9480/9485, TSUxxxx and RTxxxx. Look them up in your defconfig (keywords: FSA, TSU, RT).

On Samsung devices, the switches are often connected over I2C-GPIO (which is an I2C bus bitbanged over GPIO pins). Example from baffinlite:

#define GPIO_FSA9485_I2C_SDA 113
#define GPIO_FSA9485_I2C_SCL 114
#define GPIO_FSA9485_INT 56

/* ... */

static struct i2c_board_info  __initdata micro_usb_i2c_devices_info[]  = {
	{
		I2C_BOARD_INFO("fsa9485", 0x4A >> 1),
		.platform_data = &fsa9485_pdata,
		.irq = gpio_to_irq(GPIO_FSA9485_INT),
	},
};

static struct i2c_gpio_platform_data fsa_i2c_gpio_data={
	.sda_pin = GPIO_FSA9485_I2C_SDA,
	.scl_pin = GPIO_FSA9485_I2C_SCL,
	.udelay	 = 2,
};

Note - 0x4A >> 1 means a bit shift, you can paste it into the Python interpreter and it will give you the value (hex(0x4A >> 1) gives 0x25).

In mainline, this becomes:

/ {
/* (add this under the / node ) */

    /* ... */

	/* USB switch */
	i2c-musb {
		#address-cells = <1>;
		#size-cells = <0>;
		compatible = "i2c-gpio";
		sda-gpios = <&gpio 113 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
		scl-gpios = <&gpio 114 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
		i2c-gpio,delay-us = <2>;

		musb@25 {
			compatible = "fcs,fsa9480";
			reg = <0x25>;
			interrupt-parent = <&gpio>;
			interrupts = <56 IRQ_TYPE_LEVEL_HIGH>;

			connector {
				compatible = "samsung,usb-connector-11pin",
					     "usb-b-connector";
				label = "micro-USB";
				type = "micro";

				ports {
					#address-cells = <1>;
					#size-cells = <0>;

					port@0 {
						reg = <0>;

						muic_to_usb: endpoint {
							remote-endpoint = <&usb_to_muic>;
						};
					};
				};
			};

		};
	};
    /* ... */
};

Once you have the switch, you can add the remaining USB nodes:

&usbotg {
	vusb_d-supply = <&usbldo_reg>;
	vusb_a-supply = <&iosr1_reg>;
	vbus-supply = <&vbus_reg>;
	dr_mode = "otg";
	role-switch-default-mode = "peripheral";
	usb-role-switch;
	status = "okay";

    // if your device has no switch, drop the port node below
	port {
		usb_to_muic: endpoint {
			remote-endpoint = <&muic_to_usb>;
		};
	};
};

&usbphy {
	status = "okay";
};

WiFi and Bluetooth

Most Broadcom devices use Broadcom WiFi/Bluetooth chips from the BCM43xx series. (Look up BCM43 in your defconfig, or reference downstream kernel dmesg for the exact model.)

The below examples are for BCM4330. If you have another chip, you can grep for it in the mainline kernel (arch/arm/boot/dts) and see what compatibles are used by other devices.

The Bluetooth chip goes under the UART bus that was labeled as "bluetooth":

&uartb {
	status = "okay";
	clock-frequency = <48000000>;

	bluetooth {
		compatible = "brcm,bcm4330-bt";

		shutdown-gpios = <&gpio 28 GPIO_ACTIVE_HIGH>;
		device-wakeup-gpios = <&gpio 32 GPIO_ACTIVE_HIGH>;
		host-wakeup-gpios = <&gpio 72 GPIO_ACTIVE_HIGH>;
		reset-gpios = <&gpio 71 GPIO_ACTIVE_LOW>;
		interrupt-parent = <&gpio>;
		interrupts = <72 IRQ_TYPE_EDGE_FALLING>;
	};
};

To get the GPIOs:

  • Look for bcmbt_platform_data; .bt_wake_gpio becomes device-wakeup-gpios, and .host_wake_gpio becomes host-wakeup-gpios.
  • Look for bcmbt_rfkill_platform_data; .vreg_gpio becomes shutdown-gpios, and .n_reset_gpio becomes reset-gpios.

For WiFi, add the following under the / node:

/ {
    /* ... */
    
	/* GPIO regulator for WiFi */
	wl_reg: regulator-gpio-wlan {
		compatible = "regulator-fixed";
		regulator-name = "WL_REG_ON";
		regulator-min-microvolt = <3000000>;
		regulator-max-microvolt = <3000000>;
		startup-delay-us = <100000>;
		gpio = <&gpio 3 GPIO_ACTIVE_HIGH>;
		enable-active-high;
	};
    
    /* ... */
 };

(Get the GPIO from board_wifi_info, member .wl_reset_gpio).

Then, update the SDIO node for WiFi:

/* WiFi */
&sdio3 {
	max-frequency = <48000000>;

	/* Actually a regulator for the WiFi chip */
	vmmc-supply = <&wl_reg>;

	#address-cells = <1>;
	#size-cells = <0>;

	pinctrl-0 = <&mmc1_width4_pins>;
	pinctrl-names = "default";

	keep-power-in-suspend;
	non-removable;

	status = "okay";

	wifi@1 {
		compatible = "brcm,bcm4330-fmac", "brcm,bcm4329-fmac";
		reg = <1>;

		interrupt-parent = <&gpio>;
		interrupts = <74 IRQ_TYPE_EDGE_FALLING>;
		interrupt-names = "host-wake";
	};
};

(Get the interrupt from board_wifi_info, member .host_wake_gpio).

GPIO keys

Many Samsung devices use GPIO-based keys for the volume up/down and home keys. (The power button is handled by the PMIC, and the touchkeys (menu/back next to home button) are usually handled by the touchscreen).

#define GPIO_KEYS_SETTINGS { \
      { KEY_VOLUMEUP,9, 1, "VOLUMEUP", EV_KEY, 0, 64}, \
	{ KEY_HOME, 10, 1, "HOME", EV_KEY, 0, 64},	\
      { KEY_VOLUMEDOWN,11, 1, "VOLUMEDOWN", EV_KEY, 0, 64}, \
}

#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
static struct gpio_keys_button board_gpio_keys[] = GPIO_KEYS_SETTINGS;
/* ... */
#endif

Equivalent DT fragment:

/ {
    /* ... */

	/* Buttons */
	gpio-keys {
		compatible = "gpio-keys";

		key-volume-up {
			label = "Volume Up";
			gpios = <&gpio 9 GPIO_ACTIVE_LOW>;
			linux,code = <KEY_VOLUMEUP>;
		};

		key-home {
			label = "Home";
			gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
			linux,code = <KEY_HOMEPAGE>;
		};

		key-volume-down {
			label = "Volume Down";
			gpios = <&gpio 11 GPIO_ACTIVE_LOW>;
			linux,code = <KEY_VOLUMEDOWN>;
		};
	};
    
    /* ... */
 };

Keypad

Note Keypad is only added for BCM21664/BCM23550 at the moment; it's most likely the same for BCM28155, just a matter of adding the right node like in the equivalent bcm21664-common.dtsi commit.

Some devices use the built-in keypad peripheral for volume up/down keys. If you have CONFIG_KEYBOARD_BCM enabled in downstream defconfig, look for bcm_keymap in your board file:

#ifdef CONFIG_KEYBOARD_BCM
static struct bcm_keymap hawaii_keymap[] = {
	{BCM_KEY_ROW_0, BCM_KEY_COL_0, "unused", 0},
	{BCM_KEY_ROW_0, BCM_KEY_COL_1, "Vol Down Key", KEY_VOLUMEDOWN},
	{BCM_KEY_ROW_0, BCM_KEY_COL_2, "unused", 0},
	{BCM_KEY_ROW_0, BCM_KEY_COL_3, "Vol Up Key", KEY_VOLUMEUP},
	{BCM_KEY_ROW_0, BCM_KEY_COL_4, "unused", 0},
	{BCM_KEY_ROW_0, BCM_KEY_COL_5, "unused", 0},
    /* ... */
};

static struct bcm_keypad_platform_info hawaii_keypad_data = {
	.row_num = 1,
	.col_num = 5,
	.keymap = hawaii_keymap,
	.bcm_keypad_base = (void *)__iomem HW_IO_PHYS_TO_VIRT(KEYPAD_BASE_ADDR),
};

#endif

Equivalent DTS:

&keypad {
	keypad,num-rows = <1>;
	keypad,num-columns = <5>;

	linux,keymap = <MATRIX_KEY(0x00, 0x01, KEY_VOLUMEDOWN)>,
		       <MATRIX_KEY(0x00, 0x03, KEY_VOLUMEUP)>;

	status = "okay";
};

Building the kernel

To apply the pmOS Broadcom Kona defconfig:

$ make bcmkona_pmos_defconfig

To build the kernel:

$ make -j$(nproc)
-- or, with mainline-tools: --
$ mb

If you have mainline-tools, you can flash the kernel with:

$ mp
$ mf