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.

High-speed SPI support

Other Parts Discussed in Thread: TLC5951, SYSCONFIG

I'm having some trouble with some test code that I threw together that tries to run SPI through the /dev/mem device by manually manipulating the registers. The program always returns with a Bus Error as soon as I try to manipulate any registers in the SPI peripheral. I did some searching and found out that the peripheral clock may not be initialized so I added a few lines of code to try and do that. But unfortunately the Bus Error was still there. I'll post the code I'm using as well as the output at the end of the question here.

I also wanted to ask for some advice on my application, maybe someone here has some more experience with the 3359 than I do. I'm using a Beagle Bone and I've got to update 16 LED drivers every 150 microseconds. Is this a goal that can be realized with this platform or am I barking up the wrong tree here? It all seems possible to me in theory... If I was able to get each SPI port to chuck data at 24Mhz simultaneously it would only take 96 microseconds, and if I can run the LED drivers beyond the 33MHz spec (Ti TLC5951) and do 48Mhz, then I'd have even more headroom. I suspect DMA->SPI would be a necessity but I don't know if I should head down that path until someone wiser than me tells me I'm crazy for even trying ;)

Thanks a lot in advance for *any* insight you might have for me!

Source: Note register definitions come from AM335x StarterWare headers

#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "mcspi.h"
#include "soc_AM335x.h"
#include "hw_cm_per.h"

int main()
{
static volatile uint32_t * spi0_base_addr;
static volatile uint32_t * clocks_base_addr;

printf("Opening raw memory...\n");
int memfd = open("/dev/mem", O_RDWR | O_SYNC);
if(memfd < 0) {
printf("Error opening memory!\n");
return -1;
}

printf("Mapping registers to user space...\n");
spi0_base_addr = (uint32_t *)mmap(0,getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, memfd, SOC_SPI_0_REGS);
clocks_base_addr = (uint32_t *)mmap(0,getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, memfd, SOC_CM_PER_REGS);

if((int32_t) spi0_base_addr < 0 || (int32_t) clocks_base_addr < 0) {
printf("mmap failed: %s\n", strerror(errno));
return -1;
}

//Turn on clocks
clocks_base_addr[CM_PER_L4LS_CLKCTRL] |= CM_PER_L4LS_CLKCTRL_MODULEMODE_ENABLE;
clocks_base_addr[CM_PER_SPI0_CLKCTRL] |= CM_PER_SPI0_CLKCTRL_MODULEMODE_ENABLE;

//Wait to make sure there isn't a race condition
sleep(1);
//Check the SPI_GCLK status bit, should be non-zero
printf("CLock status: %08x\n", clocks_base_addr[CM_PER_L4LS_CLKSTCTRL] & (CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_SPI_GCLK_ACT << CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_SPI_GCLK_SHIFT));

//Read back the two clock registers that were enabled before
printf("clock registers %08x %08x \nWriting spi register...\n", clocks_base_addr[CM_PER_L4LS_CLKCTRL], clocks_base_addr[CM_PER_SPI0_CLKCTRL]);

//Soft reset of the SPI peripheral --------- BUS ERROR HAPPENS AFTER THIS STATEMENT ---------------
spi0_base_addr[MCSPI_SYSCONFIG] |= MCSPI_SYSCONFIG_SOFTRESET;

//McSPIReset((unsigned int)spi0_base_addr);

spi0_base_addr[MCSPI_TX(0)] = 1337;

//McSPITransmitData((unsigned int)spi0_base_addr, 1337, MCSPI_CHANNEL_1);
//McSPIClkConfig(spi0_base_addr, 24000000, 24000000, MCSPI_CHANNEL_0, MCSPI_CLK_MODE_0);

printf("Done.\n");

return 0;
}

Here'e the output:

root@beaglebone:~/early_testing/dspi# ./mem
Opening raw memory...
Mapping registers to user space...
CLock status: 00000000
clock registers 00000000 00040002
Writing spi register...
Bus error

  • I'm honestly open to any discussion here, I've been down a few paths since I posted this, but I'ts still my number one option.

  • printf("clock registers %08x %08x \nWriting spi register...\n", clocks_base_addr[CM_PER_L4LS_CLKCTRL], clocks_base_addr[CM_PER_SPI0_CLKCTRL]);

    clock registers 00000000 00040002

    CM_PER_L4LS_CLKCTRL -s disabled.

    1:0             Control the way mandatory clocks are managed.

    MODULEMODE      0x0:             Module is disable by SW. Any OCP
                                     access to module results in an error,
                                     except if resulting from a module
                                     wakeup (asynchronous wakeup).
                    Read  0x1:       Reserved

                    0x2:             Module is explicitly enabled. Interface

                                     clock (if not used for functions) may be

                                     gated according to the clock domain

                                     state. Functional clocks are guarantied

                                     to stay present. As long as in this

                                     configuration, power domain sleep

                                     transition cannot happen.

                      Read  0x3:       Reserved

    To access registers of peripheral, interface clock should active. For SPI, CM_PER_L4LS_CLKCTRL is the interface clock. As interface clock is disabled, you will see Bus Error,

    So please make sure that interface clock is functional while accessing peripheral registers.

    Thanks Avinash

  • Thanks for responding, I can see that the register read-out indicates that the clock isn't started, but shouldn't this line in my test code enable that clock?

    clocks_base_addr[CM_PER_L4LS_CLKCTRL] |= CM_PER_L4LS_CLKCTRL_MODULEMODE_ENABLE;

    Or is there a different procedure that I need to follow? Is it possible that this is a conflict with the spidev driver in Linux?

  • These registers are sit in PRCM region which can only modify with sufficient privilege. Bare user can't modify these registers.

    you can crosscheck by reading the values before & after reading modifying register.

    Thanks Avinash

  • You can see from the output that I posted, that I'm running the program logged in as root. Is there a different context that has higher privileges that I'm not aware of? I modified the code a bit to print the state of those status registers right before and right after I set the enable bits. Here's the output:

    root@beaglebone:~/early_testing/dspi# ./a.out
    Opening raw memory...
    Mapping registers to user space...
    CLKSTCNTRL SPI GCLK Bit: 00000000
    Clock registers: L4LS:00000000 SPI0CLKCNTRL:00040002
    Enabling clocks...
    Clock registers: L4LS:00000000 SPI0CLKCNTRL:00040002
    Waiting...
    CLKSTCNTRL SPI GCLK Bit: 00000000
    clock registers L4LS:00000000 SPI0CLKCNTRL:00040002
    Writing spi register...
    Bus error

    Something is preventing the clocks from being enabled, I'll post the definitions from my starter ware files, I'm assuming they're right b/c they're sourced from TI directly.

    #define SOC_SPI_0_REGS (0x48030000)

    #define SOC_PRCM_REGS (0x44E00000)

    #define SOC_CM_PER_REGS (SOC_PRCM_REGS + 0)

    #define CM_PER_L4LS_CLKSTCTRL (0x0)

    #define CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_SPI_GCLK_ACT (0x1u)

    #define CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_SPI_GCLK_SHIFT (0x00000019u)

    #define CM_PER_L4LS_CLKCTRL (0x60)

    #define CM_PER_SPI0_CLKCTRL (0x4c)

    #define CM_PER_L4LS_CLKCTRL_MODULEMODE_ENABLE (0x2u)

    #define CM_PER_SPI0_CLKCTRL_MODULEMODE_ENABLE (0x2u)

    #define MCSPI_SYSCONFIG   (0x110)

    #define MCSPI_SYSCONFIG_SOFTRESET (0x00000002u)

    #define MCSPI_TX(n) (0x138 + (n * 0x14))

    They all match the TRM except for the soft reset register, I'm implementing it using the definitions from the TRM instead of from the starter ware. I've dotted my i's and crossed my t's here but I'm still not getting expected behavior...

    Again, thank you very much for your continued support Mr. Philip

  • Jon, i don't quite see it based on the code you shared, but something is probably not right when you are trying to enable the clocks.  You may want to step thru that code to make sure your C code is generating the right addresses to those clock registers at 0x4e00xxxx. 

    Regards,

    James

  • Hi,

    I'm after this approach to use SPI via /dev/mem and found this post.

    I wondering if this approach  was successful ?

    Thanks,

    ~Duy-Ky