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

Knuxify (talk | contribs)
Knuxify (talk | contribs)
No edit summary
 
(11 intermediate revisions by the same user not shown)
Line 2: Line 2:
{{Work-in-progress page|note=Currently in the middle of finding this out in real time :)}}
{{Work-in-progress page|note=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. <!--For the U-Boot guide, see TODO.-->
This guide will cover the process of adding support for a recent (~2020 or newer) Qualcomm SoC into mainline Linux. <!--For the U-Boot guide, see TODO.-->


This guide was tested with the SM7435. Some details might be different for other SoCs. If you have any hints, please contribute!
This guide was tested with the SM7435. Some details might be different for other SoCs. If you have any hints, please contribute!
Line 35: Line 35:
In order to begin mainlining your SoC, you need to have the following bits of information:
In order to begin mainlining your SoC, you need to have the following bits of information:


* The '''downstream codename''' ("DTS nickname"?) of your SoC. If you're lucky, it's probably already somewhere out there online. You'll find it in the driver names on the device, as well as in the main compatible string of the devicetree. This will be useful when looking things up in the downstream kernel, as it primarily uses the codename.
* The '''codenames''' of the SoC. Qualcomm SoCs appear to have two codenames<ref>The names of these codenames have been sourced from [https://wiki.postmarketos.org/wiki/Qualcomm_Snapdragon_8_Gen_1/8%2B_Gen_1/7%2B_Gen_2_(SM8450/SM8475/SM7475) the postmarketOS wiki page for the Snapdragon 8 Gen 1], not sure where that is sourced from.</ref>:
* The '''internal codename''' of your SoC. Around mid-2025, Qualcomm's mainline Linux engineers decided that instead of using the model name for SoC drivers, they should use the codename instead... except the codename in the downstream code isn't always the same as the ''actual'' SoC codename.
** '''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.
** Sometimes the downstream codename matches with the internal name (as might be the case for "waipio" given it's mentioned alongside the others in [https://mysupport.qualcomm.com/supportforums/s/question/0D54V00007gUfXASA0/whats-the-difference-of-v69-and-v73 this random Qualcomm support forums post]), other times it's different (SM7435 uses "parrot" in the kernel but is actually called "netrani"<ref>https://lore.kernel.org/lkml/c379aad4-96f6-4134-8b90-0f1eec8001a3@oss.qualcomm.com/</ref>, SM7635 uses "volcano" in downstream but is actually called "milos").
** '''Platform codename''' - presumably shared between multiple SoCs from the same family.
** ''Mere mortals'' shall not know these codenames... but you can probably ask your friendly neighborhood sympathizer of the mainline cause<ref>https://lore.kernel.org/lkml/888a7598-38d9-4640-9823-2b073da006f4@kernel.org/</ref>.
** 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<ref>https://lore.kernel.org/lkml/888a7598-38d9-4640-9823-2b073da006f4@kernel.org/</ref>. If you're unsure of the platform codename for your device, you can send the Qualcomm folks an email and ask them directly<ref>I ended up getting [https://lore.kernel.org/lkml/c379aad4-96f6-4134-8b90-0f1eec8001a3@oss.qualcomm.com/ a public reply to my query for SM7435].</ref>, 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 '''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 '''<code>qcom_proprietary_devicetree</code>''' or similar repo with the DTSI source files (because they're not in the kernel repo for whatever reason...). Search for <code>(downstream codename).dtsi</code> on GitHub and you'll find the right repository eventually.
* A copy of the '''<code>qcom_proprietary_devicetree</code>''' or similar repo with the DTSI source files (because they're not in the kernel repo for whatever reason...). Search for <code>(downstream codename).dtsi</code> on GitHub and you'll find the right repository eventually.
Line 48: Line 48:
todo
todo


== Porting individual parts ==
== Clock controllers ==


=== Clock controllers ===
{{note|'''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.)}}


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.
On recent Qualcomm SoCs, the downstream kernel uses the mainline Qualcomm clock driver with ''some'' vendor-specific quirks. This means that the clock drivers and their relevant headers can pretty much be copied verbatim from downstream, though quite a few modifications are needed.


First, create the '''GCC''' driver. Open <code>driver/clk/qcom/Kconfig</code> and add the relevant Kconfig option for your SoC (remember to keep it ordered alphabetically):
All clock controllers (gcc, dispcc, gpucc, videocc, camcc) use the same clock framework, so the following steps will work for all of them.
 
{{hint|Start with '''GCC''', as it's needed for most components. You may also need '''DISPCC''' for the display/framebuffer. Then do the remaining ones (GPUCC, VIDEOCC, CAMCC).}}
 
==== Creating the driver ====
 
First, create the driver. Open <code>driver/clk/qcom/Kconfig</code> and add the relevant Kconfig option for your SoC (remember to keep it ordered alphabetically):


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


<syntaxhighlight lang="makefile">
<syntaxhighlight lang="makefile">
obj-$(CONFIG_SM_GCC_xxxx) += gcc-smXXXX.o
obj-$(CONFIG_SM_GCC_CODENAME) += CODENAME-gcc.o
</syntaxhighlight>
</syntaxhighlight>


Open <code>driver/clk/qcom/gcc-smXXXX.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.
 
=== Simple changes ===
 
* <code>&lt;linux/platform_device.h&gt;</code> needs to be added to includes
* Anywhere <code>enable_safe_config</code> is set to <code>true</code>, remove it and change ops to <code>clk_rcg2_shared_ops</code><ref>https://lore.kernel.org/all/YbJMnvg%2FIDwHNeWS@ripper/</ref>.
* <code>CLK_DONT_HOLD_STATE</code> in clock flags is downstream-specific, it has no equivalent in mainline.
 
=== Clock VDDs/regulators ===
 
Remove anything related to "clock VDDs/regulators":
 
* The <code>"vdd-level.h"</code> include,
* The regulator defines at the top,
* <code>.clkr.vdd_data</code> members in clk data structs,
* regulator_data in final desc struct.
 
These have no mainline equivalent.
 
=== Clock parent data ===
 
Downstream and mainline differ in two significant ways when it comes to how clock parents are defined:
 
* Downstream uses clock names; mainline uses '''clock IDs''' according to an enum at the start of the driver that is meant to be in the same order as the DT IDs.
* Downstream puts the parent data directly in the clock struct; mainline puts it in separate structs.
 
=== Probe ===
 
The way probing is done is different in mainline.
 
Look at the probe function, and find the following components:
 
* 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 ==
 
{{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 <code>drivers/interconnect/qcom/{Kconfig,Makefile}</code> (see how the other drivers do it). Then, copy your interconnect driver to <code>drivers/interconnect/qcom/codename.c</code>.
 
'''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 <code>qcom_icc_node</code>:
** Remove the <code>.id</code> member;
** Replace <code>.links</code> with <code>.link-nodes</code> containing a list with pointers to a qcom_icc_desc. To figure out what to point to - see <code>static struct qcom_icc_node *XXXXXX_nodes[] = {</code> structs - the name will be the same.
 
''See https://lore.kernel.org/all/[email protected]/ for an example. Ignore the part where they add <code>alloc_dyn_id</code>, 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.


Paste the initialization code from another GCC driver (<syntaxhighlight inline lang="c">static struct platform_driver gcc_smXXXX_driver</syntaxhighlight> and onwards.)
* 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 <code>MSM_PIN_FUNCTION(gpio)</code> with <code>MSM_GPIO_PIN_FUNCTION(gpio)</code>
* Replace the probe and platform_device stuff with the same stuff copied from another driver in mainline


== Porting the PMIC(s) ==
== Porting the PMIC(s) ==