GPIO in the kernel: an introduction
GPIO in the kernel: an introduction
Posted Jan 19, 2013 0:33 UTC (Sat) by jimparis (guest, #38647)In reply to: GPIO in the kernel: an introduction by etienne
Parent article: GPIO in the kernel: an introduction
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 flagTo clear a flag, you can't just do:my_IO->timer_interrupt = 1;because this can becometmp = *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;
