blob: c13c9b54df552126ea54725ce4d2426be2e4e271 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#ifndef OPENTITAN_SW_DEVICE_LIB_BASE_MMIO_H_
#define OPENTITAN_SW_DEVICE_LIB_BASE_MMIO_H_
// This file is included in C and C++, and, as such, needs to be marked as
// extern "C" in C++ to make sure linking works out.
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "sw/device/lib/base/bitfield.h"
/**
* Memory-mapped IO functions, which either map to volatile accesses, or can be
* replaced with instrumentation calls at compile time, for use with tests.
*
* Compiling translation units that pull in this header with `-DMOCK_MMIO` will
* disable the definitions of `mmio_region_read` and `mmio_region_write`. These
* symbols can then be defined by a test harness to allow for instrumentation of
* MMIO accesses.
*/
#ifndef MOCK_MMIO
/**
* An mmio_region_t is an opaque handle to an MMIO region; it should only be
* modified using the functions provided in this header.
*/
typedef struct mmio_region { volatile void *base; } mmio_region_t;
/**
* Create a new `mmio_region_t` from the given address.
*
* @param address an address to an MMIO region.
* @return a `mmio_region_t` value representing that region.
*/
inline mmio_region_t mmio_region_from_addr(uintptr_t address) {
return (mmio_region_t){
.base = (volatile void *)address,
};
}
/**
* Reads an aligned uint8_t from the MMIO region `base` at the given byte
* offset.
*
* This function is guaranteed to commit a read to memory, and will not be
* reordered with respect to other MMIO manipulations.
*
* @param base the region to read from.
* @param offset the offset to read at, in bytes.
* @return the read value.
*/
inline uint8_t mmio_region_read8(mmio_region_t base, ptrdiff_t offset) {
return ((volatile uint8_t *)base.base)[offset / sizeof(uint8_t)];
}
/**
* Reads an aligned uint16_t from the MMIO region `base` at the given byte
* offset.
*
* This function is guaranteed to commit a read to memory, and will not be
* reordered with respect to other MMIO manipulations.
*
* @param base the region to read from.
* @param offset the offset to read at, in bytes.
* @return the read value.
*/
inline uint16_t mmio_region_read16(mmio_region_t base, ptrdiff_t offset) {
return ((volatile uint16_t *)base.base)[offset / sizeof(uint16_t)];
}
/**
* Reads an aligned uint32_t from the MMIO region `base` at the given byte
* offset.
*
* This function is guaranteed to commit a read to memory, and will not be
* reordered with respect to other MMIO manipulations.
*
* @param base the region to read from.
* @param offset the offset to read at, in bytes.
* @return the read value.
*/
inline uint32_t mmio_region_read32(mmio_region_t base, ptrdiff_t offset) {
return ((volatile uint32_t *)base.base)[offset / sizeof(uint32_t)];
}
/**
* Writes an aligned uint8_t to the MMIO region `base` at the given byte
* offset.
*
* This function is guaranteed to commit a write to memory, and will not be
* reordered with respect to other region manipulations.
*
* @param base the region to write to.
* @param offset the offset to write at, in bytes.
* @param value the value to write.
*/
inline void mmio_region_write8(mmio_region_t base, ptrdiff_t offset,
uint8_t value) {
((volatile uint8_t *)base.base)[offset / sizeof(uint8_t)] = value;
}
/**
* Writes an aligned uint16_t to the MMIO region `base` at the given byte
* offset.
*
* This function is guaranteed to commit a write to memory, and will not be
* reordered with respect to other region manipulations.
*
* @param base the region to write to.
* @param offset the offset to write at, in bytes.
* @param value the value to write.
*/
inline void mmio_region_write16(mmio_region_t base, ptrdiff_t offset,
uint16_t value) {
((volatile uint16_t *)base.base)[offset / sizeof(uint16_t)] = value;
}
/**
* Writes an aligned uint32_t to the MMIO region `base` at the given byte
* offset.
*
* This function is guaranteed to commit a write to memory, and will not be
* reordered with respect to other region manipulations.
*
* @param base the region to write to.
* @param offset the offset to write at, in bytes.
* @param value the value to write.
*/
inline void mmio_region_write32(mmio_region_t base, ptrdiff_t offset,
uint32_t value) {
((volatile uint32_t *)base.base)[offset / sizeof(uint32_t)] = value;
}
#else // MOCK_MMIO
/**
* "Instrumented" mmio_region_t.
*
* Instead of containing a volatile pointer, mmio_region_t becomes a `void *`
* when `-DMOCK_MMIO` is enabled. This makes it incompatible with the non-mock
* version of `mmio_region_t`, which prevents users from being able to access
* the pointer inside.
*/
typedef struct mmio_region { void *mock; } mmio_region_t;
/**
* Stubbed-out read/write operations for overriding by a testing library.
*/
uint8_t mmio_region_read8(mmio_region_t base, ptrdiff_t offset);
uint16_t mmio_region_read16(mmio_region_t base, ptrdiff_t offset);
uint32_t mmio_region_read32(mmio_region_t base, ptrdiff_t offset);
void mmio_region_write8(mmio_region_t base, ptrdiff_t offset, uint8_t value);
void mmio_region_write16(mmio_region_t base, ptrdiff_t offset, uint16_t value);
void mmio_region_write32(mmio_region_t base, ptrdiff_t offset, uint32_t value);
#endif // MOCK_MMIO
/**
* Reads the bits in `mask` from the MMIO region `base` at the given offset.
*
* This function has the same guarantees as `mmio_region_read32()` and
* `mmio_region_write32()`.
*
* @param base the region to mask.
* @param offset the offset to apply the mask at, in bytes.
* @param mask the mask to read from the selected register.
* @param mask_index mask position within the selected register.
* @retun return the value of the read mask.
*/
inline uint32_t mmio_region_read_mask32(mmio_region_t base, ptrdiff_t offset,
uint32_t mask, uint32_t mask_index) {
uint32_t value = mmio_region_read32(base, offset);
return (value >> mask_index) & mask;
}
/**
* Checks whether the `bit_index`th bit is set in the MMIO region `base` at
* the given offset.
*
* This function has the same guarantees as `mmio_region_read32()` and
* `mmio_region_write32()`.
*
* @param base the region to mask.
* @param offset the offset to apply the mask at.
* @param bit_index the bit to check.
* @return true if the bit is set, false otherwise
*/
inline bool mmio_region_get_bit32(mmio_region_t base, ptrdiff_t offset,
uint32_t bit_index) {
return (mmio_region_read32(base, offset) >> bit_index) & 0x1;
}
/**
* Clears the bits in `mask` from the MMIO region `base` at the given offset.
*
* This function performs a non-atomic read-write-modify operation on a
* MMIO region.
*
* @param base the region to mask.
* @param offset the offset to apply the mask at, in bytes.
* @param mask the mask to clear from the selected register.
* @param mask_index mask position within the selected register.
*/
inline void mmio_region_nonatomic_clear_mask32(mmio_region_t base,
ptrdiff_t offset, uint32_t mask,
uint32_t mask_index) {
uint32_t value = mmio_region_read32(base, offset);
uint32_t clear_mask = ~(mask << mask_index);
value &= clear_mask;
mmio_region_write32(base, offset, value);
}
/**
* Sets the bits in `mask` from the MMIO region `base` at the given offset.
*
* This function performs a non-atomic read-write-modify operation on a
* MMIO region.
*
* @param base the region to mask.
* @param offset the offset to apply the mask at, in bytes.
* @param mask the mask to set on the selected register.
* @param mask_index mask position within the selected register.
*/
inline void mmio_region_nonatomic_set_mask32(mmio_region_t base,
ptrdiff_t offset, uint32_t mask,
uint32_t mask_index) {
uint32_t value = mmio_region_read32(base, offset);
value |= (mask << mask_index);
mmio_region_write32(base, offset, value);
}
/**
* Sets the bits in `mask` from the MMIO region `base` at the given offset.
*
* This function is like `nonatomic_set_mask32`, but does not perform a
* read, for use with write-only memory.
*
* @param base the region to mask.
* @param offset the offset to apply the mask at, in bytes.
* @param mask the mask to set on the selected register.
* @param mask_index mask position within the selected register.
*/
inline void mmio_region_write_only_set_mask32(mmio_region_t base,
ptrdiff_t offset, uint32_t mask,
uint32_t mask_index) {
uint32_t value = (mask << mask_index);
mmio_region_write32(base, offset, value);
}
/**
* Sets the `field` from the MMIO region `base` at the given `offset`.
*
* This function performs a non-atomic read-write-modify operation on a
* MMIO region. The information of which portion of the register to set, is
* stored in the `field`. The semantics of this operation are similar to the
* `mmio_region_nonatomic_set_mask32`, however the appropriate portion of the
* register is zeroed before it is written to.
*
* @param base the region to set the field in.
* @param offset the offset to set the field at, in bytes.
* @param field field within selected register field to be set.
*/
inline void mmio_region_nonatomic_set_field32(mmio_region_t base,
ptrdiff_t offset,
bitfield_field32_t field) {
uint32_t register_value = mmio_region_read32(base, offset);
register_value = bitfield_set_field32(register_value, field);
mmio_region_write32(base, offset, register_value);
}
/**
* Sets the `field` from the MMIO region `base` at the given `offset`.
*
* This function is like `nonatomic_set_field32`, but does not perform a
* read, for use with write-only memory.
*
* @param base the region to set the field in.
* @param offset the offset to set the field at, in bytes.
* @param field field within selected register field to be set.
*/
inline void mmio_region_write_only_set_field32(mmio_region_t base,
ptrdiff_t offset,
bitfield_field32_t field) {
uint32_t register_value = 0;
register_value = bitfield_set_field32(register_value, field);
mmio_region_write32(base, offset, register_value);
}
/**
* Clears the `bit_index`th bit in the MMIO region `base` at the given offset.
*
* This function has the same guarantees as
* `mmio_region_nonatomic_clear_mask()`.
*
* @param base the region to mask.
* @param offset the offset to apply the mask at.
* @param bit_index the bit to clear.
*/
inline void mmio_region_nonatomic_clear_bit32(mmio_region_t base,
ptrdiff_t offset,
uint32_t bit_index) {
mmio_region_nonatomic_clear_mask32(base, offset, 0x1, bit_index);
}
/**
* Sets the `bit_index`th bit in the MMIO region `base` at the given offset.
*
* This function has the same guarantees as `mmio_region_nonatomic_set_mask()`.
*
* @param base the region to mask.
* @param offset the offset to apply the mask at.
* @param bit_index the bit to set.
*/
inline void mmio_region_nonatomic_set_bit32(mmio_region_t base,
ptrdiff_t offset,
uint32_t bit_index) {
mmio_region_nonatomic_set_mask32(base, offset, 0x1, bit_index);
}
/**
* Sets the `bit_index`th bit in the MMIO region `base` at the given offset.
*
* This function is like `nonatomic_set_bit32`, but does not perform a read, for
* use with write-only memory.
*
* There is no `write_only_clear32`, since such a function would be a no-op.
*
* @param base the region to mask.
* @param offset the offset to apply the mask at.
* @param bit_index the bit to set.
*/
inline void mmio_region_write_only_set_bit32(mmio_region_t base,
ptrdiff_t offset,
uint32_t bit_index) {
mmio_region_write_only_set_mask32(base, offset, 0x1, bit_index);
}
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // OPENTITAN_SW_DEVICE_LIB_BASE_MMIO_H_