kpwashere
Posts: 7
Joined: Wed Jun 05, 2019 8:43 pm

Pi Zero w. SPI0 assisted by DMA

Sat Jul 31, 2021 10:28 pm

I've managed to make SPI0 operate in Polled and Interrupt modes. I've made DMA work on memory to memory copies.

I'm now attempting to make SPI0 work with DMA. I'm using the mailbox to allocate memory from the VC. I'm working in EL1 mode with Physical memory map and translating to Bus memory for DMA.

I have the DMA channel set up (channel 4) to reading from SPI RX device (6), that looks OK.

When I provide the following control block to DMA channel 5 to write to SPI TX device (7), it looks like this :

(gdb) x/8x 0xea6b000
0xea6b000: 0x00060148 0xcea6b020 0xe0204004 0x000001e0
0xea6b010: 0x00000000 0x00000000 0xdeadbeef 0x00000006

The DMA control register looks like this just before setting the active bit:

(gdb) x/8x 0x20007500
0x20007500: 0x00000008 0xcea6b000 0x00060148 0xcea70200
0x20007510: 0xe0204004 0x00000000 0x00000000 0x00000000

After setting the active bit, the register looks like this :

(gdb) x/8x 0x20007500
0x20007500: 0x0000000a 0x00000000 0x00060148 0xcea6b200
0x20007510: 0xe0204004 0x00000000 0x00000000 0x00000000

What is the problem? I'm checking debug bits, there are no errors. The output on SPI shows the CS1 line going low end then immediately high. It looks like the transmission terminates immediately. The change in CS register from 0x08 to 0x0A is the End flag being set. The control block above (0xea6b000) has the source memory address at 0xcea6b020 and the destination is the fifo port at 0xe0204004.

HAH! Solved it. Merely writing it down for y'all makes the difference. I finish the post anyhow and share the result. When mapping physical memory address to a bus address, it is as you see below. BUT, for peripherals, they are mapped differently. So, I'd added and am using the DMA_PERF_TO_BUS(x) macro you see below for mapping the physical address to the SPI0->fifo port (it should be 0x7e204004 in the control block above).

#define DMA_BUS_TO_PHYS(x) (void *)((x) & ~0xC0000000)
#define DMA_PHYS_TO_BUS(x) ((uint32_t)(x) | 0xC0000000)
#define DMA_PERF_TO_BUS(x) ((uint32_t)(x) | 0x7E000000)

Reference: For the above memory dumps, they are looking at the following control structures:

// DMA Device register
typedef struct {
volatile uint32_t cs;
volatile uint32_t conblk_ad;
volatile uint32_t ti;
volatile uint32_t source_ad;
volatile uint32_t dest_ad;
volatile uint32_t txfr_len;
volatile uint32_t stride;
volatile uint32_t nextconbk;
volatile uint32_t debug;
} Dma;

// DMA Control block
typedef struct {
volatile uint32_t ti;
volatile uint32_t source_ad;
volatile uint32_t dest_ad;
volatile uint32_t txfr_len;
volatile uint32_t stride;
volatile uint32_t nextconbk;
volatile uint32_t debug[2];
} Dma_conbk;

Return to “Bare metal, Assembly language”