Overview
On downstream power domains and regular regulators are somewhat mixed together (like this). On mainline, power domains go in one place and regulators in another, because it turns out, they are completely different beasts.
I can’t really explain what the difference is, but power domains are like a cooktop you can configure from 0 (off) to N (highest power).
Setup
The source is mainly DT and a little bit of header files. Keep in mind that the regulator code is contained in a few different source dtsi files, so it’s best to look at the final full device DT.
Look under the qcom,rpm-smd
node, there are a bunch of rpm-regulator-XXXX
nodes, which have subnodes. Those have to be translated to something like this:
DEFINE_RPMPD_PAIR(sm6115, vddcx, vddcx_ao, RWCX, LEVEL, 0);
DEFINE_RPMPD_VFL(sm6115, vddcx_vfl, RWCX, 0);
Resource name
First of all, we need to figure out which rpm-regulator-XXX
nodes are power domains, and which are regulators; for this we need to look at the qcom,resource-name
field, if it’s ldoa
or smpa
then it’s regulator (most are regulators). NOTE: Of course with time this can change, treat this more as an example than an exact process. Another thing to keep in mind that sometimes the resource names differ from DS and ML. Check troubleshooting section.
Supply type
I’m not really sure how this is called. Anyway, the subnodes under rpm-regulator-XXX
(for power supplies) have interesting suffixes, like level
, leve-ao
, floor-level
(some might status = "disabled"
, so keep an eye for that).
So apparently the level
and level-ao
(ao
stands for always-on
), are a pair, so they are defined using DEFINE_RPMPD_PAIR
. The floor-level
on the other hand is defined using DEFINE_RPMPD_VFL
(FL
stands for floor-level
). Also other platforms might have the word corner
which is an alternative to level
.
Resource ID
Lastly there is the qcom,resource-id
under rpm-regulator-XXX
, this goes as the last argument of the declaring macro.
Max State
There is one last bit of data needed — the maximum state allowed by the hardware for all power supplies on the platform. It goes in the plat_desc
struct in ML. To get it you need to look for the file included by all the clk DS drivers (in my case the file is called vdd-level-bengal.h
). So take the last corner value (in my case RPMH_REGULATOR_LEVEL_TURBO_L1
corresponds to 416), and then find the right constant name from include/dt-bindings/power/qcom-rpmpd.h
(in ML), in my case:
#define RPM_SMD_LEVEL_TURBO_NO_CPR 416
So I stick RPM_SMD_LEVEL_TURBO_NO_CPR
in .max_state
and that’s it.
Power supply names
In my case they were comments inside the source dts (you loose comments if you look at decompiled DT, so you have to search through the source files to find the original definitions). So PM6125 S3/S4 - VDD_CX supply
becomes vddcx
in mainline (look for previously defined names for inspiration.
TL;DR
So you’ll either get a level
, level-ao
and floor-level
, or a corner
, corner-ao
and optionally a floor-corner
. That is for each power supply defined in Downstream.
So if you have both a level+ao or corner+ao you use the PAIR macro with LEVEL
or CORNER
second-to-last arg, then you put the resource name as third-to-last arg, and the resource-id as last arg:
DEFINE_RPMPD_PAIR(platform_name, name, name_ao, RES_NAME, CORNER_OR_LEVEL, RES_ID);
Otherwise you define them one by one with the corresponding macro:
DEFINE_RPMPD_TYPE(platform_name, name, RES_NAME, RES_ID);
where TYPE
is CORNER
, LEVEL
, VFC
(floor-corder
) or VFL
(floor-level
).
Then you have to stick those inside the platform_name_rpmpds
, but that part is obvious. The indexes are arbitrary and go in the binding include file, so they can be included and used from DT files.
Troubleshooting
If the resource name from downstream doesn’t match, you’ll have to look at the magic constants and try to match them with DS:
// from drivers/soc/qcom/rpmpd.c in mainline
#define RPMPD_SMPA 0x61706d73
#define RPMPD_LDOA 0x616f646c
#define RPMPD_SMPB 0x62706d73
#define RPMPD_LDOB 0x626f646c
#define RPMPD_RWCX 0x78637772
#define RPMPD_RWMX 0x786d7772
#define RPMPD_RWLC 0x636c7772
#define RPMPD_RWLM 0x6d6c7772
#define RPMPD_RWSC 0x63737772
#define RPMPD_RWSM 0x6d737772
All in all there is not much to go wrong, but when I first stumbled with it it was totally NOT obvious what goes where and where it comes from, so hopefully this helps somebody in the future.
Special thanks to user aka_
and Konrad from PostmarketOS Mainlining channel for the help in figuring this out.