User:Knuxify/Draft:Qualcomm/Adding a new SoC to mainline Linux: Difference between revisions
No edit summary |
|||
| Line 54: | Line 54: | ||
=== Clock controllers === | === Clock controllers === | ||
On recent Qualcomm SoCs, the downstream kernel uses the mainline Qualcomm clock driver with | 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. | ||
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 " | 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 | 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. | ||
==== Simple changes ==== | |||
* <code>linux/platform_device.h</code> needs to be added to includes | * <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> | * 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. | * <code>CLK_DONT_HOLD_STATE</code> in clock flags is downstream-specific, it has no equivalent in mainline. | ||
* Look at the probe function | |||
==== 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[] = { | |||
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) | |||
{ | { | ||
/* 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 = { | |||
.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. | |||
=== Interconnect driver === | === Interconnect driver === | ||