|
|
Subscribe / Log in / New account

GPIO in the kernel: an introduction

GPIO in the kernel: an introduction

Posted Jan 18, 2013 8:44 UTC (Fri) by russell (guest, #10458)
Parent article: GPIO in the kernel: an introduction

Everyone thinks GPIO is easy, and that probably explains why the interfaces are incomplete. GPIO usually has a lot more functionality than can be accessed.

1. GPIO can be both output and input at the same time. Yep you may want to read back the value of an output. MCUs I've used in the past offered this. After all output are really just stronger versions of the pull up/down :)

2. GPIO have pull up/down resistors.

3. And this is the BIG one. GPIO has state BEFORE the kernel boots. Bootstrap code can set the state of GPIO during the first few cycles of operation. This can eliminate the need for extra external hardware. However, the kernel blows this away and you have to wait for userspace to boot before you get control!!!! This behaviour on for example RPi required extra hardware to be added to the circuit to prevent this kind of transient boot behaviour from glitching relays.


to post comments

GPIO in the kernel: an introduction

Posted Jan 18, 2013 11:50 UTC (Fri) by etienne (guest, #25256) [Link] (2 responses)

> GPIO can be both output and input at the same time

Reading back output GPIO is described in Documentation/gpio.txt
My main problem is that people (even software manager responsible of software "quality") do think it is clean to use the C preprocessor, either in C or C++.
That leads to massive amount of #define, to define the address, the value when active, the value when inactive, the shift position for each GPIO and the mask for that GPIO (you never know if the mask is shifted or not).
Then you have a set of GPIO to represent a 3 bit value, and the preprocessor will never tell you that you are trying to write 0x15 in this 3 bit value.
Also those quality specialists tell you to use the macro:
#define IOWRITE32BYTE(addr, val) *((volatile unsigned *)(addr)) = (val)
(Hint: try to write the same addr twice with a newer compiler).
Anyway on newer architecture, those are memory mapped and IHMO it is a lot cleaner to use C to declare them:
volatile struct my_IO_s {
#ifdef BIG_ENDIAN
enum { healthy, fail } power_state : 1;
unsigned power_active : 1;
#else
unsigned power_active : 1;
enum { healthy, fail } power_state : 1;
#endif
} * const my_IO = (volatile struct my_IO_s *)0xDC002000;
Instead of pages and pages of #define.

GPIO in the kernel: an introduction

Posted Jan 19, 2013 0:33 UTC (Sat) by jimparis (guest, #38647) [Link] (1 responses)

Also those quality specialists tell you to use the macro:
#define IOWRITE32BYTE(addr, val) *((volatile unsigned *)(addr)) = (val)
(Hint: try to write the same addr twice with a newer compiler).

The volatile should ensure that both writes occur in that case.

Anyway on newer architecture, those are memory mapped and IHMO it is a lot cleaner to use C to declare them:
volatile struct my_IO_s {
enum { healthy, fail } power_state : 1;
unsigned power_active : 1;
} * const my_IO = (volatile struct my_IO_s *)0xDC002000;
Instead of pages and pages of #define.

I agree, and I do the same in embedded development, but it's not perfect. The three biggest problems:

  • You lose control over the access size. It can vary between compilers, compiler versions, architectures, and even ABIs for particular architectures. For example, see GCC bug #23623
  • They don't map well to unusual registers. Consider an interrupt flag register which behaves like:
        read 0: interrupt has not occurred
        read 1: interrupt has occurred
        write 0: no action
        write 1: clear interrupt flag
    
    To clear a flag, you can't just do:
        my_IO->timer_interrupt = 1;
    
    because this can become
        tmp = *my_IO_addr | (1 << TIMER_INTERRUPT_SHIFT);
        *my_IO_addr = tmp;
    
    Instead, you'd need to use something like:
        *my_IO = (struct my_IO_s) {
            .timer_interrupt = 1
        };
    
    which is not much better than the equivalent:
        *my_IO_addr = (1 << TIMER_INTERRUPT_SHIFT);
    
  • Even for a more normal register like a GPIO port, it's quite difficult to set two bits simultaneously with a single write. You can't do:
        *my_IO_addr |= (1 << PIN0_SHIFT) | (1 << PIN1_SHIFT);
    
    without something like:
       struct my_IO_s tmp = *my_IO;
       tmp.pin0 = 1;
       tmp.pin1 = 1;
       *my_IO = tmp;
    
Really it's one of the more annoying parts of C when doing embedded development on a constrained system, as it's hard to avoid without adding some layer of performance-killing indirection (like Arduino does).

GPIO in the kernel: an introduction

Posted Jan 21, 2013 11:36 UTC (Mon) by etienne (guest, #25256) [Link]

>> #define IOWRITE32BITS(addr, val) *((volatile unsigned *)(addr)) = (val)
> The volatile should ensure that both writes occur in that case.

Last time I tried few days ago it did not work, you have to do:
#define IOWRITE32BITS(addr, val) do { \
volatile unsigned *ptr = (volatile unsigned *)(addr); \
*ptr = (val); \
} while (0)

> You lose control over the access size

Yes, I would also like to have bitfields accessed like they are declared, i.e. "char abit : 2;" accessed as a byte and "unsigned abit :2;" accessed as a 32 bits, because it is usual to have devices which cannot do short word access (lots of system on chip).

> [... using "(struct my_IO_s) {}" extension ...]

My system is more complex than Arduino, I have arrays of structures containing other arrays in these I/O areas, and all those #define blessed by software-quality people drive me mad.
#define FPGA1_FILTER_2_PARAMETER_4_ACTIVE 1
How to do a loop for each filters?
In short, in C or C++, unlike BASIC, the linker is made to manage addresses, the compiler manages offsets into these addresses; the C pre-processor has nothing to do there.
IHMO even GPIO in the kernel shall be identified with an address, not a #define.

GPIO in the kernel: an introduction

Posted Jan 18, 2013 14:42 UTC (Fri) by dougg (guest, #1894) [Link]

To your second and third points, welcome to pinctrl introduced in lk 3.7 and 3.8 . And while you are at it, look at the device tree stuff (aka "open firmware" and hence lots of "of_" adorned names). gpio pin states can and should be set before the Linux kernel gets a look in (e.g. in uboot or earlier), and when it does the kernel should resist the temptation to set those lines to some arbitrary state.

Things are moving so fast in the device tree/pinctrl area that it might be worth LWN writing another article on the subject.

GPIO in the kernel: an introduction

Posted Jan 28, 2013 21:27 UTC (Mon) by BenHutchings (subscriber, #37955) [Link]

1. GPIO can be both output and input at the same time. Yep you may want to read back the value of an output.

Indeed this is an absolute requirement for a GPIO used as an I2C SCL line.


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds