mmapped GPIO with devicetree


I need to toggle a few bits from GPIO1 port from my application, so I
mmaped the gpio region and try to flip bits in the output register.
Pinmuxing is set by a DT overlay, looking at
/sys/kernel/debug/pinctrl/44e10800.pinmux/pins confirms that the pins
have been configured properly, but I I see no results on the connector
pins. To eliminate the possibility that the bit address was calculated
incorrectly, I wrote a simple test program which writes in a loop all
0s and all 1s to the output register. The user leds on the board blink
as expected, however I can't change the state of a single pin on the
connectors. The last possibility is that the overlay is incorrect,
however I ran out of ideas what can be wrong with it:


/ {
        compatible = "ti,beaglebone", "ti,beaglebone-black";

        /* identification */
        part-number = "tlc5946_gpio_pins";

        fragment@0 {
                target = <&am33xx_pinmux>;
                __overlay__ {
                        pinctrl_tlc5946: pinctrl_tlc5946_pins {
                        pinctrl-single,pins = <
                                0x074 0x37 /* xerr: P9_13 =
uart4_txd, INPUT_PULLUP | MODE7 */
                                0x040 0x17 /* mode: P9_15 = gpio1_16,
                                0x04c 0x17 /* blank: P9_16 = ehrpwm1b,
                                0x044 0x17 /* xhalf: P9_23 = gpio1_17,

        fragment@1 {
                target = <&ocp>;
                __overlay__ {
                        #address-cells = <1>;
                        #size-cells = <1>;
                        tlc_gpios {
                                compatible = "bone-pinmux-helper";
                                pinctrl-names = "default";
                                pinctrl-0 = <&pinctrl_tlc5946>;
                                status = "okay";


pinmuxing after loading the overlay:
root@BBBlack:~# cat
/sys/kernel/debug/pinctrl/44e10800.pinmux/pins|egrep '874|840|84c|844'
pin 16 (44e10840) 00000017 pinctrl-single
pin 17 (44e10844) 00000017 pinctrl-single
pin 19 (44e1084c) 00000017 pinctrl-single
pin 29 (44e10874) 00000037 pinctrl-single

The pin group shows up in all files in
/sys/kernel/debug/pinctrl/44e10800.pinmux, so it looks that this part
works correctly.
Anybody has an idea where is the problem?

Test code:
kernel version: Linux BBBlack 3.8.13 #1 SMP Fri May 31 12:58:04 CEST
2013 armv7l GNU/Linux
OS: Angstrom v2012.12


So, you enabled output for the physical pin, via the muxing, but I don’t see where you enabled output for the gpio signal going to that pin. Check out the output enable register at offset 0x134 from the gpio base (or you could use the sysfs entry).

I had trouble when using aggressive compiler optimization when building my final library, even with the OSYNC file open flag, volatile pointers, etc. The writes wouldn’t flush until the next read or write so what should have been a 50% duty cycle ended up being a 0.1% duty cycle. To fix it, I put “#pragma OPTIMIZE OFF” before and “#pragma OPTIMIZE ON” after the function that performed the actual memory writes, to turn optimization off for that function. So be aware if you see strangeness.

Mmap is definitely the way to go for speed!

Checking the OE reg was actually the first thing that came to my mind.
All the bits in GPIO_OE register are set to 0, what means that they
all should be outputs.
The LEDs on the board are connected go GPIO1 and I can see them
blinking, so the outputs are updated. I just can't change any of the
outputs routed to the headers.
The testing program changes the value of the output pins once a
second, so delayed flushes should not create problems. I'll try to
turn off all optimization and see if it helps.


Code compiled with -O0, mmapped memory declared as volatile. No
effect. Only the on-board LEDS blink.


ok, I was wrong. Bits in OE were not set, I just forgot to convert
byte address to word address and when I was checking the value of
GPIO_OE the program was reading from a wrong place.
Now when this is resolved, how can I enable pins in the overlay,
without having to write directly to the register? I assumed that it
would be done automatically, when the kernel sees that the pinmux is
set to "output".


When you enable output using the pinmux settings, you’re only enabling the output driver circuitry at the pin. When you change the mux, you’re selecting which internal signal gets connected to this pins output driver. So, the pin mux (physical pin) is completely separate from the gpio block (internal signal). You have to enable both.

If you don’t want to mmap the output enable registers, you can export the pin then use the sysfs gpio interface “direction” entry. Either way, something needs to set the registers.

I don’t believe this can be done with device tree since it’s more of a “random peripheral tied to a pin” type configuration.

It would be logical for bone-pinmux-helper to enable the pins. I like
DT mainly because it allows to detach configuration of the board from
the application and store it in one place. Fiddling with OE is useful
when its function should be changed during runtime, but for fixed
direction splitting configuration between the DT and the application
code looks inconsistent.
Anyway, thanks for pointing it out. It really helped me to track the problem.


Well, you could say that what your saying is nearly equivalent to having that pin mux helper go send out some configuration i2c messages when a pin is configured for i2c.

On the other hand, you could also say that a default pin state should be part a devices configuration, but that would be mostly a fib. The “default pin state” defined in your overlay would be loaded some random time after boot when the device tree stuffs finished loading. So anything you connect has to work with the fact that the pin is in the AM335x datasheet defined default pin state for the beginning period of the arbitrary boot time.

With that in mind, it makes sense, to me, that the gpio is treated as a separate peripheral, just like all the other random peripherals that can be tied to an SoC pin (hmdi, lcd, i2c, or any other infinite number of devices). The pin mux helper can enable the peripheral, but has no business/capability to modify it. Instead the enabled peripheral will start off in its datasheet defined default state which the application code is free to modify.

At least, I assume that’s the logic behind it.