#if !defined(__COMMON_H__)
#define __COMMON_H__

// 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

// 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))

// 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;

// Use mmap to obtain virtual address, given physical
void *map_periph(MEM_MAP *mp, void *phys, int size);

// Free mapped peripheral or memory
void unmap_periph_mem(MEM_MAP *mp);

void *map_segment(void *addr, int size);
void unmap_segment(void *addr, int size);

#endif // __COMMON_H__