raspberrypi / utils

A collection of scripts and simple applications

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[gpiolib] Why Pi 5 needs gpio_set_fsel() for setting direction, and Pi 4 just gpio_set_dir()

rafael2k opened this issue · comments

I just wrote some software using gpiolib (thanks a lot!!), but I noticed a small behavior difference, of when gpio_set_fsel() is needed (Pi 5 seems needed), and when not - just gpio_set_dir() (as in Pi 4).

For eg., to support the Pi 5, I added a call to gpio_set_fsel():
Rhizomatica/sbitx-core@df0d8fe

It's about efficiency, and a difference in the way the GPIO controllers work.

BCM2835 integrates the pinmux and GPIO hardware, so that GPIO_INPUT and GPIO_OUTPUT are two of the pinmux options. On Pi 5 (both for RP1 GPIOs and BCM2712 GPIOs) the GPIO is effectively separated from pinmux, with GPIO only occupying one of the available FSELs.

gpiolib was written to support both schemes, which is why the API has GPIO_FSEL_INPUT, GPIO_FSEL_OUTPUT and GPIO_FSEL_GPIO (which means reactivate the GPIO function, using whatever direction is currently set).

  • On RP1/BCM2712, GPIO_FSEL_INPUT/_OUTPUT sets the GPIO direction and set the pinmux; GPIO_FSEL_GPIO just sets the pinmux.
  • On BCM2835, GPIO_FSEL_INPUT/_OUTPUT sets GPIO direction and pinmux in a single operation, but GPIO_FSEL_GPIO does nothing because there is no GPIO state unless the pinmux has already enabled one of the GPIO modes on that pin. One could argue that perhaps it should be an alias for one of the directions (probably input) - opinions welcome.

For applications where one is repeatedly changing the GPIO direction, not being forced to also set the pinmux is an optimisation. The same is also true for only wanting to change the FSEL, but applications for that are harder to imagine.

For your application initialisation I suggest you use gpio_set_fsel to set the pinmux and direction. Setting the drive for outputs before the FSEL change will minimise glitches on the wire.

Thanks a lot @pelwell ! Perfect. Should I also "gpio_set_pull" before "gpio_set_fsel" in case of inputs?

With regards to thread safety - should I enforce all calls to gpiolib "gpio_set_*" are protected by mutual exclusion logic?

One last question: do you have a link to the datasheet of the Pi 5 chips?

Should I also "gpio_set_pull" before "gpio_set_fsel" in case of inputs?

Yes, I guess so, just in case you are adding a pull to maintain a level that was previously achieved by it being an output (or some other FSEL that drives the level).

should I enforce all calls to gpiolib "gpio_set_*" are protected by mutual exclusion logic?

I'm afraid so - as you'll have noticed there are no mutual exclusion or synchronisation calls or hooks in the library itself.

do you have a link to the datasheet of the Pi 5 chips?

Only the (incomplete) RP1 datasheet has been published: https://datasheets.raspberrypi.com/rp1/rp1-peripherals.pdf
I'd like a more complete one myself, rather than having to search our servers for IP documentation...

The use case is really for "startup" procedure, when I don't know the state. Better I check the state of each GPIO before setting it to INPUT or OUTPUT? The state does not change anymore during execution.

Thanks for the pdf link! Anyway, any problem I hit I let you know. Up to now - all good, thanks!

Better I check the state of each GPIO before setting it to INPUT or OUTPUT?

No, just choose what you want the configuration to be and set it in the order described above. If you don't care about glitching, don't worry about the order. If you don't care about the state of a pin, don't set it.

Thanks a lot @Pewell. For the record, I left the initialization gpio code of the software I'm writing like this:
Rhizomatica/sbitx-core@8758f4e

Btw, may be you already got used to the pdf. I found this (page 17):

"If any level-based interrupts are required, then the interrupt-to-message translation block (see PCIE MSIn_CFG
section) must enable the IACK mechanism to properly sequence software through the Pending, Active, and EOI
states. Interrupts may be missed by the host processor if this feature is not used."

So in theory we could use interrupts to get know level changes without "polling" the GPIO, right? Polling is fine, just to know if there could be some more optimized way in the future.

You can't get an interrupt to user-level - it would be a huge security problem - so you then need some kernel code to intercept the interrupt and wake the user thread, presumably telling it on which GPIO a change was detected. Before you know it you've re-invented libgpiod. And you've certainly reduced the maximum sampling rate, given the overheads of interrupt handling, calling into the kernel, context switches etc.

Got it.
: )

So with regards to GPIOLIB, I'm totally happy! I'll close this one, ok?

No objections here.