This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Precise External Abort on non-linefetch for User Mode SPI Access

I am attempting to write a User Mode SPI driver for my AM3896. I am able to mmap the spi address space but when I try to access any of the mapped memory I get a Precise External Abort on non-linefetch error.

I have the McSPI's power and clocks enabled as proved by the ioctl call to the Linux SPI driver. I also know that my mmap call is working properly because I am able to read the GPIO registers by only changing the base address of the mmap.

I am out of ideas of why this wouldn't work so any any help would be appreciated. Thank you in advance.

#include <asm/types.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/spi/spidev.h>
#include <sys/mman.h>

#define SPIDEV "/dev/spidev1.1"
static void do_spi_ioctl( void );
static void do_umspi( void );
int main( int argc, char ** argv )
{
    //prove the clock and pm are setup correctly
    do_spi_ioctl( );
    //now error on the user mode version
    do_umspi( );
}

static void do_spi_ioctl( )
{
    unsigned char tx_buffer[10] = { 0xAA };
    unsigned char rx_buffer[10] = { 0 };
    unsigned  spidev;
    /**
      struct spi_ioc_transfer {
         __u64          tx_buf;
         __u64          rx_buf;

         __u32          len;
         __u32          speed_hz;

         __u16          delay_usecs;
         __u8           bits_per_word;
         __u8           cs_change;
         __u32          pad;
      };
    */
    spidev = open(SPIDEV, O_RDWR);
    if (spidev == -1)
    {
        printf("Unable to open %s.\n", SPIDEV);
    }
    struct spi_ioc_transfer tr = {
                                  (unsigned)tx_buffer,
                                  (unsigned)rx_buffer,
                                  10,
                                  2000000,
                                  30,
                                  8,
                                  0,
                                  0
                                 };

    int ret = ioctl(spidev, SPI_IOC_MESSAGE(1), &tr);
    printf ("Read %d and wrote %d bytes to spidev\n", 10, ret);
    if (ret < 1)
    {
        printf ("Unable to send spi message.\n");
    }
    close(spidev);
}

static void do_umspi( )
{
    unsigned * s_spi_ptr;
    unsigned base;
    unsigned basepage;
    unsigned baseoff;
    int memoryFD = open( "/dev/mem", O_RDWR );
    if ( memoryFD == 0 )
    {
      printf( "Failed to open /dev/mem.\n" );
      return;
    }
    base = 0x48030000;//spi
    //base = 0x48032000;//gpio
    baseoff = base %  getpagesize();
    basepage = base - baseoff;

    char *registerPage_ptr = (char *)mmap( 0,
                                      getpagesize(),
                                      ( PROT_READ | PROT_WRITE ),
                                      MAP_SHARED,
                                      memoryFD,
                                      basepage );
    s_spi_ptr = (unsigned *)(registerPage_ptr + baseoff);
    printf( "um_spi_init: after mmap base x%x baseoff x%x basepage x%x registerPage_ptr x%x s_spi_ptr x%x pagesize x%x\n",
            base,
            baseoff,
            basepage,
            registerPage_ptr,
            s_spi_ptr,
            getpagesize() );
    if ( (unsigned)registerPage_ptr == 0xFFFFFFFF )
    {
        printf( "Error while mapping memory.\n" );
        return;
    }

    printf( "spi loc x%x\n", (s_spi_ptr) );
    printf( "spi version loc x%x\n", (s_spi_ptr) );
    //allow buffer to clear
    sleep( 1 );
    //the spi hw verion is the first register
    printf( "spi version x%x\n", *(s_spi_ptr) );
}

spi output

# ./spi_test.o
Read 10 and wrote 10 bytes to spidev
um_spi_init: after mmap base x48030000 baseoff x0 basepage x48030000 registerPage_ptr x2ad55000 s_spi_ptr x2ad55000 pagesize x1000
spi loc x2ad55000
spi version loc x2ad55000
[   97.921404] Unhandled fault: Precise External Abort on non-linefetch (0x1018) at 0x2ad55000
Bus error

gpio output

# ./spi_test.o
Read 10 and wrote 10 bytes to spidev
um_spi_init: after mmap base x48032000 baseoff x0 basepage x48032000 registerPage_ptr x2ad31000 s_spi_ptr x2ad31000 pagesize x1000
spi loc x2ad31000
spi version loc x2ad31000
spi version x50600801

  • Shane Whalen said:
    I get a Precise External Abort on non-linefetch error.

    Usually this indicates that the module is disabled in h/w. I am not too familiar with SPI so i don't know why you get

    Shane Whalen said:
    Read 10 and wrote 10 bytes to spidev
    maybe it's a buffered r/w?

    Since you are already making use of /dev/mem i would suggest explicitly enabling the SPI module by writing to its CLKCTRL register in PRCM. I would expect the error to go away after this is done.

    Regards,

    Vaibhav

  • Thank you very much this worked, however I am confused as to why it worked. I understand that the SPI module needs to be enabled, but I prove that it is enabled by writing out the 10 bytes using the ioctl call. Why do I have to enable the SPI module again in User Mode?

  • I guess the answer to that will depend on what the SPI_IOC_MESSAGE ioctl ends up doing. Note that the crash happens when you try to read the version from the register. Unless there's a register read happening when the ioctl is used, you can't be sure that the module is indeed enabled.

    Regards,

    Vaibhav