User:Knuxify/Draft:Qualcomm/Adding a new SoC to mainline Linux: Difference between revisions

From Mainlining Wiki
Jump to navigation Jump to search
Knuxify (talk | contribs)
Knuxify (talk | contribs)
No edit summary
Line 49: Line 49:


== Porting individual parts ==
== Porting individual parts ==
'''A note about licences and copying code:''' a good chunk of drivers can be copied from downstream with minimal modifications. In those cases, keep the original copyright at the top of the file (Copyright (c) 20xx, Qualcomm Innovation Center, Inc. etc), and add yourself below. (This seems to be how it was done for milos as well.)


=== Clock controllers ===
=== Clock controllers ===
Line 73: Line 75:
</syntaxhighlight>
</syntaxhighlight>


Copy your downstream driver to <code>driver/clk/qcom/CODENAME-gcc.c</code>.
Copy your downstream driver to <code>driver/clk/qcom/CODENAME-gcc.c</code>; also copy the dt-bindings from <code>include/dt-bindings/clock/qcom,gcc-CODENAME.h</code> to <code>include/dt-bindings/clock/qcom,CODENAME-gcc.h</code> in mainline.


Paste the initialization code from another GCC driver (<syntaxhighlight inline lang="c">static struct platform_driver gcc_smXXXX_driver</syntaxhighlight> and onwards.)
(TODO these mention dispcc, I think gcc used the same steps as well)


{{todo|There are some missing struct members, these can be ommited. IIRC one has a counterpart in mainline but I don't remember which one... revisit this, maybe?}}
* <code>linux/platform_device.h</code> needs to be added to includes
* Anywhere <code>enable_safe_config</code> is set, change ops to <code>clk_rcg2_shared_ops</code><ref>https://lore.kernel.org/all/YbJMnvg%2FIDwHNeWS@ripper/</ref>.
* Remove anything related to "clock VDDs/regulators" - notably the <code>"vdd-level.h"</code> include, <code>.clkr.vdd_data</code> members in clk data struct, regulator_data in final desc struct, and the declarations at the start. AFAICT these have no mainline equivalent.
* <code>CLK_DONT_HOLD_STATE</code> in clock flags is downstream-specific, it has no equivalent in mainline.
* Look at the probe function.
** Calls to <code>clk_lucid_evo_pll_configure</code>: note down the "&disp_cc_pllX" pointers, and place them in a struct: <syntaxhighlight lang="c">static struct clk_alpha_pll *disp_cc_CODENAME_plls[] = {
&disp_cc_pll0,
};</syntaxhighlight>
** <code>regmap_update_bits</code> calls prefaced by a comment saying "Keep clocks always enabled": note down the 0x... addresses and place them in a struct like so: <syntaxhighlight lang="c">static u32 disp_cc_CODENAME_critical_cbcrs[] = {
0xe054, /* DISP_CC_XO_CLK */
};</syntaxhighlight>
** Remaining <code>regmap_update_bits</code> calls: keep them, with the comments intact, and move them into a new function: <syntaxhighlight lang="c">static void disp_cc_CODENAME_clk_regs_configure(struct device *dev, struct regmap *regmap)
{
/* Enable clock gating for MDP clocks */
regmap_update_bits(regmap, DISP_CC_MISC_CMD, 0x10, 0x10);
}</syntaxhighlight>
** Then, add the driver_data struct:<syntaxhighlight lang="c">static struct qcom_cc_driver_data disp_cc_CODENAME_driver_data = {
.alpha_plls = disp_cc_CODENAME_plls,
.num_alpha_plls = ARRAY_SIZE(disp_cc_CODENAME_plls),
.clk_cbcrs = disp_cc_CODENAME_critical_cbcrs,
.num_clk_cbcrs = ARRAY_SIZE(disp_cc_CODENAME_critical_cbcrs),
.clk_regs_configure = disp_cc_CODENAME_clk_regs_configure,
};</syntaxhighlight>
** For the rest of the probe steps, see how other drivers do it.


=== Interconnect driver ===
=== Interconnect driver ===

Revision as of 11:28, 6 June 2026

🚧 This page is a work-in-progress. Some information contained within may be inaccurate or incomplete.
In particular: Currently in the middle of finding this out in real time :)

This guide will cover the process of adding support for a recent (~2020 or newer, todo?) Qualcomm SoC into mainline Linux. Most of the information contained within is also applicable to older chips, though more manual effort may be needed to get these to run.

This guide was tested with the SM7435. Some details might be different for other SoCs. If you have any hints, please contribute!

High-level overview

TODO TODO: Some of this will have to be moved out to the top-level Qualcomm page

The basic components of a Qualcomm SoC are:

  • The GCC, or Global Clock Controller; as the name suggests, it is the primary clock controller for most components.
  • The other clock controllers: CAMCC, DISPCC, GPUCC and VIDEOCC.
  • The RPM, or Resource and Power Manager (RPMH or RPM Hardened in SDM845 and later); todo explanation from qcom glossary.
    • Under the RPM is the RPM(H)CC (RPM clock controller) and RPM(H)PD (RPM power domains).
  • QUP, or Qualcomm Universal Peripheral; the part of the SoC that provides I2C or SPI busses, as well as UART.
  • TLMM (Top Level Mode Multiplexer); provides pinmux and GPIO pins.
  • Many NOC interconnects (Network on Chip).
  • A timer and an interrupt controller, both standard ARM parts.

These are just the most low-level components; from there, we can move on to the higher-level ones:

  • Thermal monitoring sensor
  • USB host controller and PHY
  • Display controller and DSI PHY
  • GPU
  • etc.

This page will touch on mainline porting of most of these parts.

Finding information about your SoC

In order to begin mainlining your SoC, you need to have the following bits of information:

  • The codenames of the SoC. Qualcomm SoCs appear to have two codenames[1]:
    • Board codename ("DTS nickname"?) - you'll find it in the driver names on the device, as well as in the main compatible string of the devicetree. This is what's used across most of the downstream kernel.
    • Platform codename - presumably shared between multiple SoCs from the same family.
    • Since SM7635 ("Volcano"/"Milos") getting upstreamed, Qualcomm appears to have changed their policy around driver/compatible naming in mainline and now expects developers to use the platform codename instead of the model number[2]. If you're unsure of the platform codename for your device, you can send the Qualcomm folks an email and ask them directly[3], or just use the model name for now and let them correct you (as was done in the Milos case).
  • A copy of the downstream kernel; preferably the one for your device, but you can also use the kernel from another device with the same SoC, or - as a last resort - any kernel with the relevant SoC drivers (hint: search by downstream codename).
  • A copy of the qcom_proprietary_devicetree or similar repo with the DTSI source files (because they're not in the kernel repo for whatever reason...). Search for (downstream codename).dtsi on GitHub and you'll find the right repository eventually.
  • An extracted DTB from your device. Dump this from a running device using FDT (todo: instructions, would probably be good on a generic page, maybe subpage of Devicetree)?
  • Find a similar SoC that is already supported. Usually flagship SoCs are available in mainline; try to find a flagship from around the same time as the SoC you're mainlining. Find its DTSIs as well; then you can compare the differences between downstream and mainline for the upstreamed SoC, and correlate them with differences in your SoC. You'll also be able to check the other SoC's drivers and use them as a base for your own drivers.

Adding the DTSI

todo

Porting individual parts

A note about licences and copying code: a good chunk of drivers can be copied from downstream with minimal modifications. In those cases, keep the original copyright at the top of the file (Copyright (c) 20xx, Qualcomm Innovation Center, Inc. etc), and add yourself below. (This seems to be how it was done for milos as well.)

Clock controllers

On recent Qualcomm SoCs, the downstream kernel uses the mainline Qualcomm clock driver with only some vendor-specific quirks. This means that the clock drivers and their relevant headers can pretty much be copied verbatim from downstream, with some slight modifications.

First, create the GCC driver. Open driver/clk/qcom/Kconfig and add the relevant Kconfig option for your SoC (remember to keep it ordered alphabetically):

config SM_GCC_xxxx
	tristate "SMxxxx Global Clock Controller"
	depends on ARM64 || COMPILE_TEST
	select QCOM_GDSC
	help
	  Support for the global clock controller on SMxxxx devices.
	  Say Y if you want to use peripheral devices such as UART,
	  SPI, I2C, USB, SD/UFS, PCIe etc.

Open driver/clk/qcom/Makefile and add the relevant entry:

obj-$(CONFIG_SM_GCC_CODENAME) += CODENAME-gcc.o

Copy your downstream driver to driver/clk/qcom/CODENAME-gcc.c; also copy the dt-bindings from include/dt-bindings/clock/qcom,gcc-CODENAME.h to include/dt-bindings/clock/qcom,CODENAME-gcc.h in mainline.

(TODO these mention dispcc, I think gcc used the same steps as well)

  • linux/platform_device.h needs to be added to includes
  • Anywhere enable_safe_config is set, change ops to clk_rcg2_shared_ops[4].
  • Remove anything related to "clock VDDs/regulators" - notably the "vdd-level.h" include, .clkr.vdd_data members in clk data struct, regulator_data in final desc struct, and the declarations at the start. AFAICT these have no mainline equivalent.
  • CLK_DONT_HOLD_STATE in clock flags is downstream-specific, it has no equivalent in mainline.
  • Look at the probe function.
    • Calls to clk_lucid_evo_pll_configure: note down the "&disp_cc_pllX" pointers, and place them in a struct:
      static struct clk_alpha_pll *disp_cc_CODENAME_plls[] = {
      	&disp_cc_pll0,
      };
      
    • regmap_update_bits calls prefaced by a comment saying "Keep clocks always enabled": note down the 0x... addresses and place them in a struct like so:
      static u32 disp_cc_CODENAME_critical_cbcrs[] = {
      	0xe054, /* DISP_CC_XO_CLK */
      };
      
    • Remaining regmap_update_bits calls: keep them, with the comments intact, and move them into a new function:
      static void disp_cc_CODENAME_clk_regs_configure(struct device *dev, struct regmap *regmap)
      {
      	/* Enable clock gating for MDP clocks */
      	regmap_update_bits(regmap, DISP_CC_MISC_CMD, 0x10, 0x10);
      }
      
    • Then, add the driver_data struct:
      static struct qcom_cc_driver_data disp_cc_CODENAME_driver_data = {
      	.alpha_plls = disp_cc_CODENAME_plls,
      	.num_alpha_plls = ARRAY_SIZE(disp_cc_CODENAME_plls),
      	.clk_cbcrs = disp_cc_CODENAME_critical_cbcrs,
      	.num_clk_cbcrs = ARRAY_SIZE(disp_cc_CODENAME_critical_cbcrs),
      	.clk_regs_configure = disp_cc_CODENAME_clk_regs_configure,
      };
      
    • For the rest of the probe steps, see how other drivers do it.

Interconnect driver

TODO figure out what the interconnect actually does.

Similar to clocks, interconnects are nearly the same as in downstream, but need some changes.

Add a Kconfig option and a Makefile entry in drivers/interconnect/qcom/{Kconfig,Makefile} (see how the other drivers do it). Then, copy your interconnect driver to drivers/interconnect/qcom/codename.c.

Do not copy the accompanying header file. Mainline stopped using static IDs for ICC nodes as of 2025-10; you'll need to convert your driver.

  • In every instance of qcom_icc_node:
    • Remove the .id member;
    • Replace .links with .link-nodes containing a list with pointers to a qcom_icc_desc. To figure out what to point to - see static struct qcom_icc_node *XXXXXX_nodes[] = { structs - the name will be the same.

See https://lore.kernel.org/all/[email protected]/ for an example. Ignore the part where they add alloc_dyn_id, that was removed in another commit as dynamic IDs are now the only option.

TLMM/pinctrl

Driver is in drivers/pinctrl/qcom.

Downstream driver is split into a .c file and a .h file; mainline has them both in the .c file.

  • You'll likely have a REG_BASE added to all registers; mainline drivers don't have it. Remove it and offset the DT node instead.
  • Replace MSM_PIN_FUNCTION(gpio) with MSM_GPIO_PIN_FUNCTION(gpio)
  • Replace the probe and platform_device stuff with the same stuff copied from another driver in mainline

Porting the PMIC(s)

Qualcomm devices use multiple PMICs for different purposes. Often, the PMIC is paired with an SoC or series of SoCs.

References