|
|
Subscribe / Log in / New account

GPIO in the kernel: an introduction

GPIO in the kernel: an introduction

Posted Jan 18, 2013 11:50 UTC (Fri) by etienne (guest, #25256)
In reply to: GPIO in the kernel: an introduction by russell
Parent article: GPIO in the kernel: an introduction

> 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.


to post comments

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.


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