blob: 836948695585ba8cb15fc94d87c656adb2ff8279 [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>
/**
* 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);
}
/**
* 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);
}
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // OPENTITAN_SW_DEVICE_LIB_BASE_MMIO_H_