// Raspberry Pi DMA utility definitions; see https://iosoft.blog for details // // Copyright (c) 2020 Jeremy P Bentham // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include // Location of peripheral registers in physical memory #define PHYS_REG_BASE PI_23_REG_BASE #define PI_01_REG_BASE 0x20000000 // Pi Zero or 1 #define PI_23_REG_BASE 0x3F000000 // Pi 2 or 3 #define PI_4_REG_BASE 0xFE000000 // Pi 4 #define CLOCK_HZ 250000000 // Pi 2 - 4 //#define CLOCK_HZ 400000000 // Pi Zero // Location of peripheral registers in bus memory #define BUS_REG_BASE 0x7E000000 // If non-zero, print debug information #define DEBUG 0 // If non-zero, set PWM clock using VideoCore mailbox #define USE_VC_CLOCK_SET 0 // Size of memory page #define PAGE_SIZE 0x1000 // Round up to nearest page #define PAGE_ROUNDUP(n) ((n) % PAGE_SIZE == 0 ? (n) : ((n) + PAGE_SIZE) & ~(PAGE_SIZE - 1)) // Structure for mapped peripheral or memory typedef struct { int fd, // File descriptor h, // Memory handle size; // Memory size void *bus, // Bus address *virt, // Virtual address *phys; // Physical address } MEM_MAP; // Get virtual 8 and 32-bit pointers to register #define REG8(m, x) ((volatile uint8_t *)((uint32_t)(m.virt) + (uint32_t)(x))) #define REG32(m, x) ((volatile uint32_t *)((uint32_t)(m.virt) + (uint32_t)(x))) // Get bus address of register #define REG_BUS_ADDR(m, x) ((uint32_t)(m.bus) + (uint32_t)(x)) // Convert uncached memory virtual address to bus address #define MEM_BUS_ADDR(mp, a) ((uint32_t)a - (uint32_t)mp->virt + (uint32_t)mp->bus) // Convert bus address to physical address (for mmap) #define BUS_PHYS_ADDR(a) ((void *)((uint32_t)(a) & ~0xC0000000)) // GPIO register definitions #define GPIO_BASE (PHYS_REG_BASE + 0x200000) #define GPIO_MODE0 0x00 #define GPIO_SET0 0x1c #define GPIO_CLR0 0x28 #define GPIO_LEV0 0x34 #define GPIO_GPPUD 0x94 #define GPIO_GPPUDCLK0 0x98 // GPIO I/O definitions #define GPIO_IN 0 #define GPIO_OUT 1 #define GPIO_ALT0 4 #define GPIO_ALT1 5 #define GPIO_ALT2 6 #define GPIO_ALT3 7 #define GPIO_ALT4 3 #define GPIO_ALT5 2 #define GPIO_MODE_STRS "IN", "OUT", "ALT5", "ALT4", "ALT0", "ALT1", "ALT2", "ALT3" #define GPIO_NOPULL 0 #define GPIO_PULLDN 1 #define GPIO_PULLUP 2 // Videocore mailbox memory allocation flags, see: // https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface typedef enum { MEM_FLAG_DISCARDABLE = 1 << 0, // can be resized to 0 at any time. Use for cached data MEM_FLAG_NORMAL = 0 << 2, // normal allocating alias. Don't use from ARM MEM_FLAG_DIRECT = 1 << 2, // 0xC alias uncached MEM_FLAG_COHERENT = 2 << 2, // 0x8 alias. Non-allocating in L2 but coherent MEM_FLAG_ZERO = 1 << 4, // initialise buffer to all zeros MEM_FLAG_NO_INIT = 1 << 5, // don't initialise (default is initialise to all ones) MEM_FLAG_HINT_PERMALOCK = 1 << 6, // Likely to be locked for long periods of time MEM_FLAG_L1_NONALLOCATING = (MEM_FLAG_DIRECT | MEM_FLAG_COHERENT) // Allocating in L2 } VC_ALLOC_FLAGS; // VC flags for unchached DMA memory #define DMA_MEM_FLAGS (MEM_FLAG_DIRECT | MEM_FLAG_ZERO) // Mailbox command/response structure typedef struct { uint32_t len, // Overall length (bytes) req, // Zero for request, 1<<31 for response tag, // Command number blen, // Buffer length (bytes) dlen; // Data length (bytes) uint32_t uints[32 - 5]; // Data (108 bytes maximum) } VC_MSG __attribute__((aligned(16))); // DMA channels and data requests #define DMA_CHAN_A 10 #define DMA_CHAN_B 11 #define DMA_PWM_DREQ 5 #define DMA_SPI_TX_DREQ 6 #define DMA_SPI_RX_DREQ 7 #define DMA_BASE (PHYS_REG_BASE + 0x007000) // DMA register addresses offset by 0x100 * chan_num #define DMA_CS 0x00 #define DMA_CONBLK_AD 0x04 #define DMA_TI 0x08 #define DMA_SRCE_AD 0x0c #define DMA_DEST_AD 0x10 #define DMA_TXFR_LEN 0x14 #define DMA_STRIDE 0x18 #define DMA_NEXTCONBK 0x1c #define DMA_DEBUG 0x20 #define DMA_REG(ch, r) ((r) == DMA_ENABLE ? DMA_ENABLE : (ch)*0x100 + (r)) #define DMA_ENABLE 0xff0 // DMA register values #define DMA_WAIT_RESP (1 << 3) #define DMA_CB_DEST_INC (1 << 4) #define DMA_DEST_DREQ (1 << 6) #define DMA_CB_SRCE_INC (1 << 8) #define DMA_SRCE_DREQ (1 << 10) #define DMA_PRIORITY(n) ((n) << 16) // DMA control block (must be 32-byte aligned) typedef struct { uint32_t ti, // Transfer info srce_ad, // Source address dest_ad, // Destination address tfr_len, // Transfer length stride, // Transfer stride next_cb, // Next control block debug, // Debug register, zero in control block unused; } DMA_CB __attribute__((aligned(32))); // PWM controller registers #define PWM_BASE (PHYS_REG_BASE + 0x20C000) #define PWM_CTL 0x00 // Control #define PWM_STA 0x04 // Status #define PWM_DMAC 0x08 // DMA control #define PWM_RNG1 0x10 // Channel 1 range #define PWM_DAT1 0x14 // Channel 1 data #define PWM_FIF1 0x18 // Channel 1 fifo #define PWM_RNG2 0x20 // Channel 2 range #define PWM_DAT2 0x24 // Channel 2 data // PWM register values #define PWM_CTL_RPTL1 (1 << 2) // Chan 1: repeat last data when FIFO empty #define PWM_CTL_USEF1 (1 << 5) // Chan 1: use FIFO #define PWM_DMAC_ENAB (1 << 31) // Start PWM DMA #define PWM_ENAB 1 // Enable PWM #define PWM_PIN 12 // GPIO pin for PWM output, 12 or 18 // Clock registers and values #define CLK_BASE (PHYS_REG_BASE + 0x101000) #define CLK_PWM_CTL 0xa0 #define CLK_PWM_DIV 0xa4 #define CLK_PASSWD 0x5a000000 #define PWM_CLOCK_ID 0xa void fail(char *s); void *map_periph(MEM_MAP *mp, void *phys, int size); void *map_uncached_mem(MEM_MAP *mp, int size); void unmap_periph_mem(MEM_MAP *mp); void gpio_set(int pin, int mode, int pull); void gpio_pull(int pin, int pull); void gpio_mode(int pin, int mode); void gpio_out(int pin, int val); uint8_t gpio_in(int pin); void disp_mode_vals(uint32_t mode); int open_mbox(void); void close_mbox(int fd); uint32_t msg_mbox(int fd, VC_MSG *msgp); void *map_segment(void *addr, int size); void unmap_segment(void *addr, int size); uint32_t alloc_vc_mem(int fd, uint32_t size, VC_ALLOC_FLAGS flags); void *lock_vc_mem(int fd, int h); uint32_t unlock_vc_mem(int fd, int h); uint32_t free_vc_mem(int fd, int h); uint32_t set_vc_clock(int fd, int id, uint32_t freq); void disp_vc_msg(VC_MSG *msgp); void enable_dma(int chan); void start_dma(MEM_MAP *mp, int chan, DMA_CB *cbp, uint32_t csval); uint32_t dma_transfer_len(int chan); uint32_t dma_active(int chan); void stop_dma(int chan); void disp_dma(int chan); void init_pwm(int freq, int range, int val); void start_pwm(void); void stop_pwm(void); // EOF