For the imx6 we directly write to the SPI registers (from user space). We recently got it working on Apalis-imx6 with TorizonCore
- version:TorizonCore Upstream 5.7.0-devel-20220530110134+build.0 (dunfell)
To give a little more insight. This is how it looks
SPI register pointers
unsigned int *SPI1_Register_Array[10];
unsigned int *SPI2_Register_Array[10];
unsigned int *GPIO5_pointer[8];
Init SPI1
int mem_fd;
void *spi_map = 0;
void *CCGR_map = 0;
void *IOMUX_map = 0;
void *GPIO_map = 0;
unsigned int *CCGR_pointer = 0;
unsigned int *IOMUX_pointer = 0;
signed int temp;
//Gain access to IOMUX register to set correct function
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0)// open: /dev/mem
{
printf("can't open /dev/mem \n");
return -1;
}
IOMUX_map = mmap(NULL,0x0948,PROT_READ|PROT_WRITE,MAP_SHARED,mem_fd,0x20E0000);
if (IOMUX_map == MAP_FAILED)
{
printf("mmap error\n");//errno also set!
return -1;
}
IOMUX_pointer = IOMUX_map + 0x0274;
*IOMUX_pointer = 0x00000005; //Set SPI1 to GPIO, we do manually set it
IOMUX_pointer = IOMUX_map + 0x08F8;
*IOMUX_pointer = 0x00000002; //Selecting alt 3 mode
close(mem_fd);
munmap(IOMUX_pointer,0x0948);
//Gain access to IOMUX register to set correct function
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0)// open: /dev/mem
{
printf("can't open /dev/mem \n");
return -1;
}
GPIO_map = mmap(NULL,0x0948,PROT_READ|PROT_WRITE,MAP_SHARED,mem_fd,0x20AC000);
if (GPIO_map == MAP_FAILED)
{
printf("mmap error\n");//errno also set!
return -1;
}
GPIO5_pointer[0] = GPIO_map + 0x0;
temp = *GPIO5_pointer[0];
temp |= 1 << 25;
*GPIO5_pointer[0] = temp;//0x00000002; //Selecting alt 3 mode
GPIO5_pointer[1] = GPIO_map + 0x4;
temp = *GPIO5_pointer[1];
temp |= 1 << 25;// set output0x00000005; //Set SPI1 to GPIO, we do manually set it
*GPIO5_pointer[1] = temp;
close(mem_fd);
//Gain access to clock registers to be able to enable the SPI clock
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0)// open: /dev/mem
{
printf("can't open /dev/mem \n");
return -1;
}
CCGR_map = mmap(NULL,0x4088,PROT_READ|PROT_WRITE,MAP_SHARED,mem_fd,0x020C4000);
if (CCGR_map == MAP_FAILED)
{
printf("mmap error\n");//errno also set!
return -1;
}
CCGR_pointer = CCGR_map + 0x6C ;
*CCGR_pointer |= 0x00000005; //Enable SPI1 & SPI2 clock
close(mem_fd);
munmap(CCGR_pointer,4);
//Gain access to the SPI registers to be able to initialize and use SPI
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0)// open: /dev/mem
{
printf("can't open /dev/mem \n");
return -1;
}
spi_map = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,mem_fd,0x02008000);
if (spi_map == MAP_FAILED)
{
printf("mmap error\n");//errno also set!
return -1;
}
SPI1_Register_Array[0] = spi_map + 0x00;// Always use volatile pointer!
SPI1_Register_Array[1] = spi_map + 0x04;
SPI1_Register_Array[2] = spi_map + 0x08;
SPI1_Register_Array[3] = spi_map + 0x0C;
SPI1_Register_Array[4] = spi_map + 0x10;
SPI1_Register_Array[5] = spi_map + 0x14;
SPI1_Register_Array[6] = spi_map + 0x18;
SPI1_Register_Array[7] = spi_map + 0x1C;
SPI1_Register_Array[8] = spi_map + 0x20;
SPI1_Register_Array[9] = spi_map + 0x40;
*SPI1_Register_Array[2] = 0x007050F1;
//BURST_LENGTH = 8 -> a burst contains 8 bits
//CHANNEL_SELECT = 0 -> SS0 will be used
//DRCTL = 0 -> SPI_RDY don't care,
//PRE_DEVIDER = 5 -> Root is 60 MHz, so devide by 6 this will result in 10 MHz
//POST_DEVIDER = 0 -> Root is PRE_DEVIDER, so devide by 0 this will in a FINAL ~10 kHz CLK
//CHANNEL_MODE[0] = F -> Make this the master, all other slaves
//SMC = 0 -> Start a burst when XCH bit is set
//XCH = 0 -> Dont start a burst (is only needed in the transmission function)
//HT = 0 -> Disable HT mode (is also not supported)
//EN = 1 -> Enable the SPI2
*SPI1_Register_Array[3] = 0x00000F0F;
//HT_LENGTH = 0 -> Is not supported
//SCLK_CTL[0] = 0 -> Stay low
//DATA_CTL[0] = 0 -> Stay high
//SS_POL[0] = 0 -> Active low
//SS_CTL[0] = 1 -> for master operation: Only one SPI burst will be send (to empty TXFIFO use 1)
//SCLK_POL[0] = 0 -> Active high polarity
//SCLK_PHA[0] = 1 -> 0 Phase 0 operation
*SPI1_Register_Array[4] = 0x00000000; //Disable all interupt sources
*SPI1_Register_Array[5] = 0x00000000; //Disable DMA for TXFIFO and RXFIFO
*SPI1_Register_Array[6] = 0x000000C0; //Clear status register by writing 1
*SPI1_Register_Array[7] = 0x00000000; //No additional wait delays
*SPI1_Register_Array[8] = 0x00000000; //Disable Loop Back Control (LBC)
close(mem_fd);
SPI1 Write
unsigned int fifo_free = FIFO_SIZE - (*SPI1_Register_Array[8] & 0xFF);
signed int CurrentBitSize;
bit_size -= 1;
if((bit_size / 32) <= fifo_free)
{
for(CurrentBitSize = bit_size; CurrentBitSize > 0; CurrentBitSize -= 8)
{
*SPI1_Register_Array[1] = (data[CurrentBitSize/32]>>(((CurrentBitSize-7)%32)));// & 0x000000FF;
}
return FIFO_SIZE - (*SPI1_Register_Array[8] & 0xFF);
}
else
return fifo_free - (bit_size / 32);
SP1 Send data stored
signed int temp;
temp = *GPIO5_pointer[0];
temp &= ~(1 << 25);
*GPIO5_pointer[0] = temp;//0x00000002; //Selecting alt 3 mode
while((*SPI1_Register_Array[8] & 0xFF) != 0)
{
*SPI1_Register_Array[2] |= 0x04; //Enable 32 bit transmission
while ((*SPI1_Register_Array[2] & 0x4) >> 2)
{
usleep(1);
}
}
temp = *GPIO5_pointer[0];
temp |= 1 << 25;
*GPIO5_pointer[0] = temp;//0x00000002; //Selecting alt 3 mode
}
SPI2 Init (slave)
int mem_fd;
void *spi_map = 0;
void *IOMUX_map = 0;
unsigned int *IOMUX_pointer = 0;
//Gain access to IOMUX register to set correct function
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0)// open: /dev/mem
{
printf("can't open /dev/mem \n");
return -1;
}
IOMUX_map = mmap(NULL,0x0948,PROT_READ|PROT_WRITE,MAP_SHARED,mem_fd,0x20E0000);
if (IOMUX_map == MAP_FAILED)
{
printf("mmap error\n");//errno also set!
return -1;
}
IOMUX_pointer = IOMUX_map + 0x0104;
*IOMUX_pointer = 0x00000002; //Set SPI1 to SS function
close(mem_fd);
munmap(IOMUX_pointer,0x0948);
//Gain access to the SPI registers to be able to initialize and use SPI
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0)// open: /dev/mem
{
printf("can't open /dev/mem \n");
return -1;
}
spi_map = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,mem_fd,0x0200C000);
if (spi_map == MAP_FAILED)
{
printf("mmap error\n");//errno also set!
return -1;
}
SPI2_Register_Array[0] = spi_map + 0x00;// Always use volatile pointer!
SPI2_Register_Array[1] = spi_map + 0x04;
SPI2_Register_Array[2] = spi_map + 0x08;
SPI2_Register_Array[3] = spi_map + 0x0C;
SPI2_Register_Array[4] = spi_map + 0x10;
SPI2_Register_Array[5] = spi_map + 0x14;
SPI2_Register_Array[6] = spi_map + 0x18;
SPI2_Register_Array[7] = spi_map + 0x1C;
SPI2_Register_Array[8] = spi_map + 0x20;
SPI2_Register_Array[9] = spi_map + 0x40;
*SPI2_Register_Array[2] = 0x00705001;//0x00705001 for slave
if(*SPI2_Register_Array[2] == 0x00705001)
printf("SPI2 Control Register is correctly set.\r\n");
//BURST_LENGTH = 8 -> a burst contains 8 bits
//CHANNEL_SELECT = 0 -> SS0 will be used
//DRCTL = 0 -> SPI_RDY don't care,
//PRE_DEVIDER = 5 -> Root is 60 MHz, so devide by 6 this will result in 10 MHz
//POST_DEVIDER = 0 -> Root is PRE_DEVIDER, so devide by 0 this will in a FINAL ~10 kHz CLK
//CHANNEL_MODE[0] = 0 -> All slave
//SMC = 0 -> Start a burst when XCH bit is set
//XCH = 0 -> Dont start a burst (is only needed in the transmission function)
//HT = 0 -> Disable HT mode (is also not supported)
//EN = 1 -> Enable the SPI2
*SPI2_Register_Array[3] = 0x00000F0F;//for slave 0x00000F00
if(*SPI2_Register_Array[3] == 0x00000F0F)
printf("SPI2 Config Register is correctly set.\r\n");
//HT_LENGTH = 0 -> Is not supported
//SCLK_CTL[0] = 0 -> Stay low
//DATA_CTL[0] = 0 -> Stay high
//SS_POL[0] = 0 -> Active low
//SS_CTL[0] = F -> for master operation: Only one SPI burst will be send (to empty TXFIFO use 1)
//SCLK_POL[0] = 0 -> Active high polarity
//SCLK_PHA[0] = 0 -> 0 Phase 0 operation
*SPI2_Register_Array[4] = 0x00000000; //Disable all interupt sources
*SPI2_Register_Array[5] = 0x00000000; //Disable DMA for TXFIFO and RXFIFO
*SPI2_Register_Array[7] = 0x00000000; //No additional wait delays
*SPI2_Register_Array[8] = 0x00000000; //Disable Loop Back Control (LBC)
close(mem_fd);
return 0;
SPI2 read
unsigned int RXCNT = (*SPI2_Register_Array[8] & 0xFF00) >> 8;
unsigned int i;
unsigned int RXDATA;
//printf("STATREG SPI1 = %x. / STATREG SPI2 = %X\r",*SPI1_Register_Array[6], *SPI2_Register_Array[6]);
//printf("RXCNT = %i.\r",RXCNT);
for(i = 0; i < RXCNT; i++)
{
RXDATA = *SPI2_Register_Array[0];
read_data_array[i] = RXDATA;
//printf("RX FIFO = %X.\r",RXDATA);
}
return RXCNT;
Because of different Architecture of the iMX8 it is no longer possible to directly write to these registers as far as I understood from reading the iMX8 documentation.