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

Knuxify (talk | contribs)
No edit summary
Knuxify (talk | contribs)
Line 54: Line 54:
=== Clock controllers ===
=== 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.
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 77: Line 83:
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.
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.


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


* <code>linux/platform_device.h</code> needs to be added to includes
* <code>&lt;linux/platform_device.h&gt;</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>.
* 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>.
* 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.
* <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[] = {
==== 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,
&disp_cc_pll0,
};</syntaxhighlight>
};</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[] = {
* <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 */
0xe054, /* DISP_CC_XO_CLK */
};</syntaxhighlight>
};</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)
* 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 */
/* Enable clock gating for MDP clocks */
regmap_update_bits(regmap, DISP_CC_MISC_CMD, 0x10, 0x10);
regmap_update_bits(regmap, DISP_CC_MISC_CMD, 0x10, 0x10);
}</syntaxhighlight>
}</syntaxhighlight>
** Then, add the driver_data struct:<syntaxhighlight lang="c">static struct qcom_cc_driver_data disp_cc_CODENAME_driver_data = {
* 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,
.alpha_plls = disp_cc_CODENAME_plls,
.num_alpha_plls = ARRAY_SIZE(disp_cc_CODENAME_plls),
.num_alpha_plls = ARRAY_SIZE(disp_cc_CODENAME_plls),
Line 102: Line 131:
.clk_regs_configure = disp_cc_CODENAME_clk_regs_configure,
.clk_regs_configure = disp_cc_CODENAME_clk_regs_configure,
};</syntaxhighlight>
};</syntaxhighlight>
** For the rest of the probe steps, see how other drivers do it.
 
For the rest of the probe steps, see how other drivers do it.


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