RPM power domains (rpmpd)

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.