Add pinctrl driver

Overview

If you have played with arduino or rpi you might have come across the concept of pins to get input from sensors or control stuff (like relays or motors). Modern SoCs have hundreds of those, and each needs to be configured appropriately so the device that is attached on the other end behaves accordingly.

The pinctrl driver is responsible for this configuration, and is high on the list of things to port from downstream, if you want to get more devices going (like SD card, UFS and pretty much everything other than USB, thankfully :)).

Setup

The downstream driver is located under drivers/pinctrl/qcom/pinctrl-bengal.c, and if you compare it with an existing mainline driver, say drivers/pinctrl/qcom/pinctrl-sdm660.c, you will see there are some minor differences in the PINGROUP macro (DS, vs ML). More specifically the base is replaced with a tile. If you look around the implementation you’d notice that tile is just a more formal way to refer to different offsets (west, east, south). Also note that these offsets now need to be provided in the DTS, careful with relative offsets 😉

So after you tweak the code a bit to replace base with tile, and remove egpio_* and wake_* stuff (not yet available on ML, and likely not very important, given that latest flagship SoCs (8150, 8250, 8350) can do without, it is pretty much ready.

Almost…

Troubleshooting

So I load it up and I got the helpful crash dump screen. Note that normally the kernel starts printing on the screen but nothing shows up, meaning it crashed before it managed to initialize the (fake-ish) screen.

PINpoint issue

To be 100% sure it is pinctrl (and not the SDHC which I also ported in DT), I compiled the pinctrl driver as a module (use make menuconfig, then type forward slash /, type search term (like the codename of your driver, 6115 in my case), and choose from the list), then copy it over to the phone via nc: (on the phone run nc -l -p 3002 > driver.ko, on the PC: cat path/to/driver.ko | nc 172.16.42.1 3002). On some systems this just works like a charm, sometimes you might need to press enter a few times, kill one of them, then the other, and hope the file appeared, if not, repeat… after a few times you’ll get the hang of it. So I load the driver with insmod driver.ko and the phone shuts down. Bingo!

Gather intelligence

One possibility is of course the driver is crap, but that was less likely. A patch I used for inspiration (Sony Xperia 10 II, here), I noticed some pins were disabled via gpio-reserved-ranges, so now the question was which pins to disable. I asked in the PmOS mainline channel and got a few hints:

  1. konradybcio suggested to disable less and less pins until it works, somewhat humorously. In mainlining as in love, nothing is forbidden, so I kept that option as a last resort
  2. Alexey Min suggested msm_gpio_get_direction I think is first called first when gpios are enumerated, so if I put a print in there I can see what is the last pin printed before the crash.
  3. aka_ totally nailed it with this link, basically:
    if (i == 14 || i == 15 || i == 16 || i == 17) inside the downstream driver that lists the pins. That was a pretty strong hint 🙂
  4. Finally Alexey Min noted, that cat /sys/kernel/debug/gpio on downstream would be missing these pins, so you don’t even have to try to sieve through the code (that is, in case the above command doesn’t crash :))

So just to be sure, I added prints in msm_gpio_get_direction, also a short delay with usleep_range(500000, 500000) for .5s sleep. If you don’t sleep at all it is possible that the phone dies before it manages to show the print, even though technically the print happens earlier, not to mention that it will happen very fast and even if it is displayed it will be for a fraction of a second. And it last printed pin 14.

So after I stuffed gpio-reserved-ranges = <14 4>; in my DT it all went with flying colors.

Merging it upstream (update 2021.06.28)

So after I sent the patch to LKML first time, I got a bunch of suggestions, most importantly to merge a bunch of redundant functions and fix some issues with the DT bindings.

First important thing that Bjorn pointed out is that function names are only named-indexes, so it matters that they differ for the same pin. So if there is a function name bla1, bla2, bla3 and they are all used on different pins these can be merged to one function bla used on all 3 pins.

The other issue was that in DT you can nest the actual pin specification arbitrary, like this (example from qcom,sc8180x-pinctrl.yaml):

pinctrl@3100000 {
	compatible = "qcom,sc8180x-tlmm";
	reg = <0x03100000 0x300000>,
	      <0x03500000 0x700000>,
	      <0x03d00000 0x300000>;
	reg-names = "west", "east", "south";
	interrupts = <GIC_SPI 208 IRQ_TYPE_LEVEL_HIGH>;
	gpio-controller;
	#gpio-cells = <2>;
	interrupt-controller;
	#interrupt-cells = <2>;
	gpio-ranges = <&tlmm 0 0 190>;

	// simple nesting
	gpio-wo-subnode-state {
		pins = "gpio1";
		function = "gpio";
	};

	// complex nesting
	uart-w-subnodes-state {
		rx {
			pins = "gpio4";
			function = "qup6";
			bias-pull-up;
		};

		tx {
			pins = "gpio5";
			function = "qup6";
			bias-disable;
		};
	};
};

So in order to define this we use a separate $defs section in the binding and then reference it twice inside patternProperties, like in here.

Spoilers for next episode

I tried to get the SDHC to work (on most phones up until…now the SD card and internal storage were connected on that bus, newer/higher end phones now use UFS, including mine), it did probe but couldn’t detect an inserted SD card. Then I tried enabling UFS but it turned out it has a phy, similar to usb phy from last post, and this time the phy is harder to tame, so I’m hoping to get that going next.

This will be an uphill battle, because the phone is encrypted, AND uses the super partition (custom android scheme for putting a bunch of partitions inside one), so even if I get UFS to work it might not be trivial to use the disk like it’s 95.