Port-arm archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Raspberry Pi 3B: /dev/mem mmap GPIO works on earmv7hf but not on aarch64



Hi,

I'm trying to control Raspberry Pi GPIO via /dev/mem + mmap(2)
from userland of NetBSD 11.0_RC1, to control YM2149F PSG chip:
 https://x.com/tsutsuii/status/2022913343913390424

On Raspberry Pi 3 Model B, the same user program behaves
correctly on an earmv7hf kernel, but behaves incorrectly
on an aarch64 kernel.

Hardware
--------

- Raspberry Pi 3 Model B (can boot both 32-bit and 64-bit NetBSD)

Kernels
-------

- NetBSD 11.0_RC1 tagged source tree

- evbarm 32-bit (earmv7hf):
   sys/arch/evbarm/conf/GENERIC
    + options INSECURE (force kern.securelevel=-1 for mmap /dev/mem)
    + options HZ=1000  (to get 1ms tick to access YM2149F in each ~2ms)

- evbarm 64-bit (aarch64):
   sys/arch/evbarm/conf/GENERIC64
    + options INSECURE
    + options HZ=1000

Reproduction
------------

- Boot the earmv7hf kernel and run the attached gpio_mmap_test.c:
 -> GPIO20 LED turns ON then OFF as expected.

- Boot the aarch64 kernel and run the same test program
 -> GPIO20 LED does not turn on.
    Sometimes an unrelated GPIO (e.g. GPIO22) turns on.
    In some cases the kernel SDHC driver starts reporting CRC errors
    repeatedly and stalls.
``` (typed from video screen)
sdhc0: cmd crc error
sdhc0: cmd timeout error
sdmmc1: extended I/O error 60, r=40992 p=0xffffc000b1680dac I=4 read
```
 - The same earmv7hf binary (that works on the earmv7hf kernel) on aarch64
   (via COMPAT_NETBSD32) also gets the same failures.

Test program
------------

This program maps the GPIO registers and toggles GPIO20 via GPSET0/GPCLR0.
(Uses PERI_BASE = 0x3F000000 for Pi 2/3.)

https://gist.github.com/tsutsui/0c0d6c1d7f4e2d5dbaaf210789c51ad7

```c
/*
 * gpio_mmap_test.c
 *  Minimal Raspberry Pi GPIO test using GPIO access via /dev/mem mmap(2)
 *
 * Wiring:
 *   GPIO20     -> LED (1: on, 0: off)
 *
 * Build:
 *   cc -O2 -Wall -Wextra -o gpio_mmap_test gpio_mmap_test.c
 */

#include <sys/mman.h>

#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

// #define PERI_BASE   0x20000000u /* BCM2835 (Pi 1/Zero) */
#define PERI_BASE   0x3F000000u /* BCM2836/2837 (Pi 2/3) */
// #define PERI_BASE   0xFE000000u /* BCM2711 (Pi 4) */

#define GPIO_BASE   (PERI_BASE + 0x200000u)
#define GPIO_SIZE   0x1000u

/* GPIO registers */
#define GPFSEL0     0x00
#define GPFSEL1     0x04
#define GPFSEL2     0x08
#define GPSET0      0x1c
#define GPCLR0      0x28
#define GPLEV0      0x34

enum {
    PIN_D0   = 20, /* DA0 */
};

#define MASK_D0 (1u << PIN_D0)

static volatile uint32_t *gpio;

static void
die(const char *msg)
{
    perror(msg);
    exit(EXIT_FAILURE);
}

/* Minimal memory barrier (ordering for MMIO) */
static inline void
mmio_barrier(void)
{
#if (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__)
    __asm__ volatile("dmb ish" ::: "memory");
#else
    __sync_synchronize();
#endif
}

/* Set GPIO function to output: fsel=001 */
static void
gpio_config_output(int pin)
{
    uint32_t reg = pin / 10;
    uint32_t shift = (pin % 10) * 3;
    volatile uint32_t *fsel = &gpio[GPFSEL0 / 4 + reg];

    uint32_t v = *fsel;
    v &= ~(7u << shift);
    v |=  (1u << shift);
    *fsel = v;
    mmio_barrier();
}

static void
gpio_config(void)
{
    gpio_config_output(PIN_D0);
}

static inline void
gpio_write_masks(uint32_t set_mask, uint32_t clr_mask)
{
    if (clr_mask)
        gpio[GPCLR0 / 4] = clr_mask;
    if (set_mask)
        gpio[GPSET0 / 4] = set_mask;
    mmio_barrier();
}

static inline void
gpio_d0_on()
{
    gpio_write_masks(MASK_D0, 0);
}

static inline void
gpio_d0_off()
{
    gpio_write_masks(0, MASK_D0);
}

int
main(int argc, char **argv)
{

    int fd = open("/dev/mem", O_RDWR | O_SYNC);
    if (fd == -1)
        die("open(/dev/mem)");

    void *p = mmap(NULL, GPIO_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED,
      fd, GPIO_BASE);
    if (p == MAP_FAILED)
        die("mmap(GPIO)");

    gpio = (volatile uint32_t *)p;

    gpio_config();

    printf("D0 (GPIO%d): ON\n", PIN_D0);
    gpio_d0_on();

    sleep(2);

    printf("D0 (GPIO%d): OFF\n", PIN_D0);
    gpio_d0_off();

    munmap((void*)gpio, GPIO_SIZE);
    close(fd);
    exit(EXIT_SUCCESS);
}
```

Questions
---------

- Is /dev/mem + mmap expected to work for BCM283x/2711 peripheral MMIO
  on evbarm/aarch64?
- If yes, where should I look for the correct mapping attributes
  (device vs normal cacheable) for /dev/mem mappings on aarch64?
- Does the described behavior sound like a cache attribute / pmap issue,
  or could it be an address translation issue?

Any comments would be appreciated. If you need additional information
(dmesg etc.) I can provide it.

Thanks,
---
Izumi Tsutsui


Home | Main Index | Thread Index | Old Index