blob: 0addcd98def35152d1bbccc045288c8980d98407 [file] [log] [blame]
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <assert.h>
#include <utils/util.h>
#include <sys/types.h>
#include <errno.h>
/* For clock.h and mux.h */
typedef struct ps_io_ops ps_io_ops_t;
#ifdef CONFIG_ARCH_ARM
#include <platsupport/clock.h>
#include <platsupport/mux.h>
#endif
#include <platsupport/irq.h>
#include <platsupport/interface_registration.h>
/**
* Memory usage hints. These indicate how memory is expected to be used
* allowing for better memory attributes or caching to be done.
* For example, memory that is only written to would be best mapped
* write combining if the architecture supports it.
*/
typedef enum ps_mem_flags {
PS_MEM_NORMAL, /* No hints, consider 'normal' memory */
PS_MEM_HR, /* Host typically reads */
PS_MEM_HW /* Host typically writes */
} ps_mem_flags_t;
/**
* Map the region of memory at the requested physical address
*
* @param cookie Cookie for the I/O Mapper
* @param paddr physical address to map.
* @param size amount of bytes to map.
* @param cached Whether region should be mapped cached or not
* @param flags Memory usage flags
* @return the virtual address at which the data at paddr can be accessed or NULL on failure
*/
typedef void *(*ps_io_map_fn_t)(
void *cookie,
uintptr_t paddr,
size_t size,
int cached,
ps_mem_flags_t flags);
/**
* Unmap a previously mapped I/O memory region
*
* @param cookie Cookie for the I/O Mapper
* @param vaddr a virtual address that has been returned by io_map
* @param size the same size in bytes this memory was mapped in with originally
*/
typedef void (*ps_io_unmap_fn_t)(
void *cookie,
void *vaddr,
size_t size);
typedef struct ps_io_mapper {
void *cookie;
ps_io_map_fn_t io_map_fn;
ps_io_unmap_fn_t io_unmap_fn;
} ps_io_mapper_t;
static inline void *ps_io_map(
const ps_io_mapper_t *io_mapper,
uintptr_t paddr,
size_t size,
int cached,
ps_mem_flags_t flags)
{
assert(io_mapper);
assert(io_mapper->io_map_fn);
return io_mapper->io_map_fn(io_mapper->cookie, paddr, size, cached, flags);
}
static inline void ps_io_unmap(
const ps_io_mapper_t *io_mapper,
void *vaddr,
size_t size)
{
assert(io_mapper);
assert(io_mapper->io_unmap_fn);
io_mapper->io_unmap_fn(io_mapper->cookie, vaddr, size);
}
/**
* Perform an architectural I/O 'in' operation (aka I/O ports on x86)
*
* @param cookie Cookie to the underlying I/O handler
* @param port Port to perform the 'in' on
* @param io_size Size in bytes of the I/O operation
* @param result Location to store the results. If io_size < 4 then unused bytes will be zeroed
*
* @return Returns 0 on success
*/
typedef int (*ps_io_port_in_fn_t)(
void *cookie,
uint32_t port,
int io_size,
uint32_t *result);
/**
* Perform an architectural I/O 'out' operation (aka I/O ports on x86)
*
* @param cookie Cookie to the underlying I/O handler
* @param port Port to perform the 'out' on
* @param io_size Size in bytes of the I/O operation
* @param val Value to send to the I/O port
*
* @return Returns 0 on success
*/
typedef int (*ps_io_port_out_fn_t)(
void *cookie,
uint32_t port,
int io_size,
uint32_t val);
typedef struct ps_io_port_ops {
void *cookie;
ps_io_port_in_fn_t io_port_in_fn;
ps_io_port_out_fn_t io_port_out_fn;
} ps_io_port_ops_t;
static inline int ps_io_port_in(
const ps_io_port_ops_t *port_ops,
uint32_t port,
int io_size,
uint32_t *result)
{
assert(port_ops);
assert(port_ops->io_port_in_fn);
return port_ops->io_port_in_fn(port_ops->cookie, port, io_size, result);
}
static inline int ps_io_port_out(
const ps_io_port_ops_t *port_ops,
uint32_t port,
int io_size,
uint32_t val)
{
assert(port_ops);
assert(port_ops->io_port_out_fn);
return port_ops->io_port_out_fn(port_ops->cookie, port, io_size, val);
}
typedef enum dma_cache_op {
DMA_CACHE_OP_CLEAN,
DMA_CACHE_OP_INVALIDATE,
DMA_CACHE_OP_CLEAN_INVALIDATE
} dma_cache_op_t;
/**
* Allocate a dma memory buffer. Must be contiguous in physical and virtual address,
* but my cross page boundaries. It is also guaranteed that this memory region can
* be pinned
*
* @param cookie Cookie for the dma manager
* @param size Size in bytes of the dma memory region
* @param align Alignment in bytes of the dma region
* @param cached Whether the region should be mapped cached or not
* @param flags Memory access flags
*
* @return NULL on failure, otherwise virtual address of allocation
*/
typedef void *(*ps_dma_alloc_fn_t)(
void *cookie,
size_t size,
int align,
int cached,
ps_mem_flags_t flags);
/**
* Free a previously allocated dma memory buffer
*
* @param cookie Cookie for the dma manager
* @param addr Virtual address of the memory buffer as given by the dma_alloc function
* @param size Original size of the allocated buffer
*/
typedef void (*ps_dma_free_fn_t)(
void *cookie,
void *addr,
size_t size);
/**
* Pin a piece of memory. This ensures it is resident and has a translation until
* it is unpinned. You should not pin a memory range that overlaps with another
* pinned range. If pinning is successful memory is guaranteed to be contiguous
* in physical memory.
*
* @param cookie Cookie for the dma manager
* @param addr Address of the memory to pin
* @param size Range of memory to pin
*
* @return 0 if memory could not be pinned, otherwise physical address
*/
typedef uintptr_t (*ps_dma_pin_fn_t)(
void *cookie,
void *addr,
size_t size);
/**
* Unpin a piece of memory. You should only unpin the exact same range
* that was pinned, do not partially unpin a range or unpin memory that
* was never pinned.
*
* @param cookie Cookie for the dma manager
* @param addr Address of the memory to unpin
* @param size Range of the memory to unpin
*/
typedef void (*ps_dma_unpin_fn_t)(
void *cookie,
void *addr,
size_t size);
/**
* Perform a cache operation on a dma memory region. Pinned and unpinned
* memory can have cache operations performed on it
*
* @param cookie Cookie for the dma manager
* @param addr Start address to perform the cache operation on
* @param size Size of the range to perform the cache operation on
* @param op Cache operation to perform
*/
typedef void (*ps_dma_cache_op_fn_t)(
void *cookie,
void *addr,
size_t size,
dma_cache_op_t op);
typedef struct ps_dma_man {
void *cookie;
ps_dma_alloc_fn_t dma_alloc_fn;
ps_dma_free_fn_t dma_free_fn;
ps_dma_pin_fn_t dma_pin_fn;
ps_dma_unpin_fn_t dma_unpin_fn;
ps_dma_cache_op_fn_t dma_cache_op_fn;
} ps_dma_man_t;
static inline void *ps_dma_alloc(
const ps_dma_man_t *dma_man,
size_t size,
int align,
int cache,
ps_mem_flags_t flags)
{
assert(dma_man);
assert(dma_man->dma_alloc_fn);
return dma_man->dma_alloc_fn(dma_man->cookie, size, align, cache, flags);
}
static inline void ps_dma_free(
const ps_dma_man_t *dma_man,
void *addr,
size_t size)
{
assert(dma_man);
assert(dma_man->dma_free_fn);
dma_man->dma_free_fn(dma_man->cookie, addr, size);
}
static inline uintptr_t ps_dma_pin(
const ps_dma_man_t *dma_man,
void *addr,
size_t size)
{
assert(dma_man);
assert(dma_man->dma_pin_fn);
return dma_man->dma_pin_fn(dma_man->cookie, addr, size);
}
static inline void ps_dma_unpin(
const ps_dma_man_t *dma_man,
void *addr,
size_t size)
{
assert(dma_man);
assert(dma_man->dma_unpin_fn);
dma_man->dma_unpin_fn(dma_man->cookie, addr, size);
}
static inline void ps_dma_cache_op(
const ps_dma_man_t *dma_man,
void *addr,
size_t size,
dma_cache_op_t op)
{
assert(dma_man);
assert(dma_man->dma_cache_op_fn);
dma_man->dma_cache_op_fn(dma_man->cookie, addr, size, op);
}
static inline void ps_dma_cache_clean(
const ps_dma_man_t *dma_man,
void *addr,
size_t size)
{
ps_dma_cache_op(dma_man, addr, size, DMA_CACHE_OP_CLEAN);
}
static inline void ps_dma_cache_invalidate(
const ps_dma_man_t *dma_man,
void *addr,
size_t size)
{
ps_dma_cache_op(dma_man, addr, size, DMA_CACHE_OP_INVALIDATE);
}
static inline void ps_dma_cache_clean_invalidate(
const ps_dma_man_t *dma_man,
void *addr,
size_t size)
{
ps_dma_cache_op(dma_man, addr, size, DMA_CACHE_OP_CLEAN_INVALIDATE);
}
/*
* Allocate some heap memory for the driver to use. Basically malloc.
*
* @param cookie Cookie for the allocator.
* @param size Amount of bytes to allocate.
* @param[out] ptr Pointer to store the result in.
*
* @return 0 on success, errno on error.
*/
typedef int (*ps_malloc_fn_t)(
void *cookie,
size_t size,
void **ptr);
/*
* Allocate and zero some heap memory for the driver to use. Basically calloc.
*
* @param cookie Cookie for the allocator.
* @param nmemb Amount of element to allocate.
* @param size Size of each element in bytes.
* @param[out] ptr Pointer to store the result in.
*
* @return 0 on success, errno on error.
*/
typedef int (*ps_calloc_fn_t)(
void *cookie,
size_t nmemb,
size_t size,
void **ptr);
/*
* Free allocated heap memory.
*
* @param ptr Pointer previously returned by alloc or calloc.
* @param size Amount of bytes to free.
* @param cookie Cookie for the allocator.
*
* @return 0 on success, errno on error.
*/
typedef int (*ps_free_fn_t)(
void *cookie,
size_t size,
void *ptr);
typedef struct {
ps_malloc_fn_t malloc;
ps_calloc_fn_t calloc;
ps_free_fn_t free;
void *cookie;
} ps_malloc_ops_t;
static inline int ps_malloc(
const ps_malloc_ops_t *ops,
size_t size,
void **ptr)
{
if (ops == NULL) {
ZF_LOGE("ops cannot be NULL");
return EINVAL;
}
if (ops->malloc == NULL) {
ZF_LOGE("not implemented");
return ENOSYS;
}
if (size == 0) {
/* nothing to do */
ZF_LOGW("called with size 0");
return 0;
}
if (ptr == NULL) {
ZF_LOGE("ptr cannot be NULL");
return EINVAL;
}
return ops->malloc(ops->cookie, size, ptr);
}
static inline int ps_calloc(
const ps_malloc_ops_t *ops,
size_t nmemb,
size_t size,
void **ptr)
{
if (ops == NULL) {
ZF_LOGE("ops cannot be NULL");
return EINVAL;
}
if (ops->calloc == NULL) {
ZF_LOGE("not implemented");
return ENOSYS;
}
if (size == 0 || nmemb == 0) {
/* nothing to do */
ZF_LOGW("called no bytes to allocate");
return 0;
}
if (ptr == NULL) {
ZF_LOGE("ptr cannot be NULL");
return EINVAL;
}
return ops->calloc(ops->cookie, nmemb, size, ptr);
}
static inline int ps_free(
const ps_malloc_ops_t *ops,
size_t size, void *ptr)
{
if (ops == NULL) {
ZF_LOGE("ops cannot be NULL");
return EINVAL;
}
if (ops->free == NULL) {
ZF_LOGE("not implemented");
return ENOSYS;
}
if (ptr == NULL) {
ZF_LOGE("ptr cannot be NULL");
return EINVAL;
}
return ops->free(ops->cookie, size, ptr);
}
/*
* Retrieves a copy of the FDT.
*
* @param cookie Cookie for the FDT interface.
*
* @return A pointer to a FDT object, NULL on error.
*/
typedef char *(*ps_io_fdt_get_fn_t)(
void *cookie);
typedef struct ps_fdt {
void *cookie;
ps_io_fdt_get_fn_t get_fn;
} ps_io_fdt_t;
static inline char *ps_io_fdt_get(
const ps_io_fdt_t *io_fdt)
{
if (io_fdt == NULL) {
ZF_LOGE("fdt cannot be NULL");
return NULL;
}
if (io_fdt->get_fn == NULL) {
ZF_LOGE("not implemented");
return NULL;
}
return io_fdt->get_fn(io_fdt->cookie);
}
/* Struct to collect all the different I/O operations together. This should contain
* everything a driver needs to function */
struct ps_io_ops {
ps_io_mapper_t io_mapper;
ps_io_port_ops_t io_port_ops;
ps_dma_man_t dma_manager;
ps_io_fdt_t io_fdt;
#ifdef CONFIG_ARCH_ARM
clock_sys_t clock_sys;
mux_sys_t mux_sys;
#endif
ps_interface_registration_ops_t interface_registration_ops;
ps_malloc_ops_t malloc_ops;
ps_irq_ops_t irq_ops;
};
/**
* In place reads/writes of device register bitfields
*
* eg, where var = 0x12345678
*
* read_masked(&var, 0x0000FFFF) ==> 0x00005678
* write_masked(&var, 0x0000FFFF, 0x000000CC) ==> var = 0x123400CC
* read_bits(&var, 8, 4) ==> 0x6
* write_bits(&var, 8, 4, 0xC) ==> var = 0x12345C78
*/
static inline uint32_t read_masked(
volatile uint32_t *addr,
uint32_t mask)
{
assert(addr);
return *addr & mask;
}
static inline void write_masked(
volatile uint32_t *addr,
uint32_t mask,
uint32_t value)
{
assert(addr);
assert((value & mask) == value);
*addr = read_masked(addr, ~mask) | value;
}
static inline uint32_t read_bits(
volatile uint32_t *addr,
unsigned int first_bit,
unsigned int nbits)
{
assert(addr);
assert(first_bit < 32);
assert(nbits <= 32 - first_bit);
return (*addr >> first_bit) & MASK(nbits);
}
static inline void write_bits(
volatile uint32_t *addr,
unsigned int first_bit,
unsigned int nbits,
uint32_t value)
{
assert(addr);
assert(first_bit < 32);
assert(nbits <= 32 - first_bit);
write_masked(addr, MASK(nbits) << first_bit, value << first_bit);
}
/*
* Populate a malloc ops with stdlib malloc wrappers.
*/
int ps_new_stdlib_malloc_ops(
ps_malloc_ops_t *ops);