Regulators (SPMI/SMD)

Overview

Regulators are responsible for providing power to various components. It is the last essential piece of the puzzle for early mainlining, that will lay the groundwork for the rest of the port.

NOTE: These instructions apply for SPMI/SMD regulators, I haven’t researched in depth the other combinations (newer higher end chips user RPMH in place of SMD).

RPM/SMD Regulators

If you look downstream, you’ll find compatible = "qcom,rpm-smd" and under it all regulators, with an allowed range. The DS driver is also nothing complicated. The reason is that the driver simply forward voltage requests to the RPM chip (downstream firmware available), which does some magic and then controls the actual regulators over SPMI (likely). The main point I’m making here is that downstream driver/DT doesn’t need to know anything about the capabilities of a particular regulator (voltage range and step) to control it. NOTE: The range specified in downstream DT for each regulator is not the full regulator range. Instead, it is just the range that won’t fry the component on the other end, and thus is instance specific (each phone on the same SoC can have it’s own ranges depending on what is connected to those regulators).

Contrast this with the mainline driver (located under drivers/regulator/qcom_smd-regulator.c), and you’ll see that each regulator has a defined range and source group:

  • range: minimum and maximum voltage value, and step (all smd regulators have linear ranges). So if minimum is 0.5V and max is 3.0V (normally specified in microvolts, or uv: 500000 - 3000000), and a step of 0.005V (5000 uv), 0.5V, 0.505V, 0.510V etc until 2.995V, 3.0V are all allowed values.
  • source group: if a chip has 20 pins, power for those pins comes from multiple input pins (lets say 3), so first input pins gives power to 0-7, second to 8-15, and third to 16-19. Of course they’re not contiguous, so the source group specifies which pins receive power from the same pin. In some cases pins provide power to other pins (like pin 0 can provide power to pin 15-18), that is not important for source groups, just don’t freak out if you see it 🙂 The group is normally named with the short pin names of the pins in the group, for example vdd_l3_l8 is the source group for pin l3 and l8.

You can see these in action at the bottom of the file, in arrays of struct rpm_regulator_data.

Source groups are used to provide (via DT) the regulators that power each group, so the driver can make sure to power on the source regulator if any regulator in the group needs power. The range is used to make sure only valid values are sent to RPM chip.

Now you might be thinking, “OK, but where do we get the ranges and the source groups if the downstream code has neither”? Good question! Short answer is — it’s not easy. For a longer and very incomplete answer read below.

SPMI Regulators

Most downstream kernels don’t have SPMI regulators (they do have other things on the SPMI bus, though). Mainline source is in drivers/regulator/qcom_spmi-regulator.c. As you can see this driver is more complicated, and includes ops (operations) that can enable/disable the regulator, get/set voltage, list voltage ranges, get/set regulator mode.

Another key difference is that while in SMD each regulator has it’s own voltage range and source group, here this data is not directly available. Instead there is this huge mapping table, that specifies range, ops, and a few other things based on type, subtype and revision. The idea is, that each regulator can be queried at runtime, and can tell its type, subtype and revision, from there the mapping table is consulted and the correct ops/voltage range is assigned. This logic is in spmi_regulator_match function.

Option 1: the lucky one

So you found out which PM (power management) chip is present in your SoC (for billie2 it’s pm6125), and you grep and find no driver whatsoever in mainline. Bad! But then you copy over the SPMI setup from downstream, define compat string, list all available regulators and offsets and put a print in the match function and check dmesg (patch, dmesg).

WARNING: If you want to be on the safe side, skip over the actual matching to avoid connecting a regulator and accidentally calling any ops on it (if you don’t specify those regulators in DT it shouldn’t happen, but still).

So you can now check if the type/subtype/revision in dmesg actually matches anything in the mapping table. If all of your regulators have a match, you’re lucky! You can get the ranges for your regulators and write a SMD regulator driver (no source groups though). Thankfully I wrote a tool that can help with the manual labor of matching each regulator and printing the ranges. You can submit an RFC patch at this point, so others might fry their devices with your half-assed untested code and report their findings with warm regards for your mother.

Option 2: Code drop

So you did everything described in Option 1, but the type,subtype,revision is not in the mapping table (or maybe just partially). However, a friend (thank you Alexey Min) accidentally stumbles upon the complete SPMI driver for your particular PMIC in code aurora. Not only that, it was the first ever SPMI downstream driver published by qcom… talk about random chance!

So now all you need to do is use the ranges and your SMD driver is ready. However, for extra credit, you might port over the ops code and submit an SPMI driver as well, for completeness sake, like this.

Option 3: barely working

For a proper SMD driver you need range and source group, but who says you need a proper driver? Depending on the SPMI regulator you might be able to get the sel (an id from 0 to max that selects the voltage, older regulators) or actual microvolt value (newer regulators).

WARNING: If something can fry your phone (or parts of it), this is how! Make sure you understand a) what you’re doing and b) the risks involved.

So if you can read current microvolt regulator value via SPMI and can write any number via SMD, you can find a regulator that is on (on/off can be read via SPMI), and try to set (via SMD) its voltage slightly lower (by 4000,5000,8000 uv — these are common step values, check in driver for all existing options), and then see what value you read from SPMI. You might be able to find the voltage step this way. Setting it higher will also work, but it is more dangerous (hopefully deviating with 4000uv from the optimal voltage won’t fry anything in a short time, but YOU HAVE BEEN WARNED!).

Now that you have the voltage step and the range for you particular phone (from downstream DT), you might just set those in the driver, so it would work for turning regulators on/off and setting values in a narrow range. This won’t be accepted in Linus tree, but it can be a useful workaround, if you really need a regulator driver (like if you know via SPMI that regulator is off, but its needed for your important gadget you’re porting).

Option 4: All Inclusive

The astute reader might have noticed, that none of the options above solve the issue of the source groups. The ideal place to get the range AND source group information, is from the data sheet of the PMIC in question. If you have access to it, then why the hell are you reading this blog? If you don’t then you have one final option to do it, and it involves:

Ten percent luck, twenty percent skill
fifteen percent concentrated power of will
five percent pleasure, fifty percent pain

Eminem

If you haven’t figured it out yet — reverse engineer the RPM chip firmware and (assuming that it has the SPMI driver as part of it) take the needed pieces from there. You didn’t read it here, aaand don’t brag about it in your patch, and you should be good, but I’m not a lawyer so DO THIS AT YOUR OWN RISK.

If you do pull it off, you can share your findings and let somebody else write the driver, and/or you can write a tutorial on how you did it, so you won’t have to sweat next time qcom decides to push a new chip.

Happy hacking, keep a fire extinguisher handy and stay out of jail!