Add memory io handlers that glue the register API to the memory API. Just translation functions at this stage. Although it does allow for devices to be created without all-in-one mmio r/w handlers.
This patch also adds the RegisterInfoArray struct, which allows all of the individual RegisterInfo structs to be grouped into a single memory region. Signed-off-by: Peter Crosthwaite <peter.crosthwa...@xilinx.com> Signed-off-by: Alistair Francis <alistair.fran...@xilinx.com> --- V5: - Convert to using only one memory region hw/core/register.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/hw/register.h | 51 +++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/hw/core/register.c b/hw/core/register.c index 1656f71..e1cd0c4 100644 --- a/hw/core/register.c +++ b/hw/core/register.c @@ -146,3 +146,73 @@ void register_reset(RegisterInfo *reg) register_write_val(reg, reg->access->reset); } + +static inline void register_write_memory(void *opaque, hwaddr addr, + uint64_t value, unsigned size, bool be) +{ + RegisterInfoArray *reg_array = opaque; + RegisterInfo *reg = NULL; + uint64_t we = ~0; + int i, shift = 0; + + for (i = 0; i < reg_array->num_elements; i++) { + if (reg_array->r[i]->access->decode.addr == addr) { + reg = reg_array->r[i]; + break; + } + } + assert(reg); + + /* Generate appropriate write enable mask and shift values */ + if (reg->data_size < size) { + we = MAKE_64BIT_MASK(0, reg->data_size * 8); + shift = 8 * (be ? reg->data_size - size : 0); + } else if (reg->data_size >= size) { + we = MAKE_64BIT_MASK(0, size * 8); + } + + register_write(reg, value << shift, we << shift); +} + +void register_write_memory_be(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + register_write_memory(opaque, addr, value, size, true); +} + + +void register_write_memory_le(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + register_write_memory(opaque, addr, value, size, false); +} + +static inline uint64_t register_read_memory(void *opaque, hwaddr addr, + unsigned size, bool be) +{ + RegisterInfoArray *reg_array = opaque; + RegisterInfo *reg = NULL; + int i, shift; + + for (i = 0; i < reg_array->num_elements; i++) { + if (reg_array->r[i]->access->decode.addr == addr) { + reg = reg_array->r[i]; + break; + } + } + assert(reg); + + shift = 8 * (be ? reg->data_size - size : 0); + + return (register_read(reg) >> shift) & MAKE_64BIT_MASK(0, size * 8); +} + +uint64_t register_read_memory_be(void *opaque, hwaddr addr, unsigned size) +{ + return register_read_memory(opaque, addr, size, true); +} + +uint64_t register_read_memory_le(void *opaque, hwaddr addr, unsigned size) +{ + return register_read_memory(opaque, addr, size, false); +} diff --git a/include/hw/register.h b/include/hw/register.h index baa08f5..726a914 100644 --- a/include/hw/register.h +++ b/include/hw/register.h @@ -15,6 +15,7 @@ typedef struct RegisterInfo RegisterInfo; typedef struct RegisterAccessInfo RegisterAccessInfo; +typedef struct RegisterInfoArray RegisterInfoArray; /** * Access description for a register that is part of guest accessible device @@ -51,6 +52,10 @@ struct RegisterAccessInfo { void (*post_write)(RegisterInfo *reg, uint64_t val); uint64_t (*post_read)(RegisterInfo *reg, uint64_t val); + + struct { + hwaddr addr; + } decode; }; /** @@ -82,6 +87,26 @@ struct RegisterInfo { }; /** + * This structure is used to group all of the individual registers which are + * modeled using the RegisterInfo strucutre. + * + * @r is an aray containing of all the relevent RegisterInfo structures. + * + * @num_elements is the number of elements in the array r + * + * @mem: optional Memory region for the register + */ + +struct RegisterInfoArray { + /* <private> */ + MemoryRegion mem; + + /* <public> */ + int num_elements; + RegisterInfo **r; +}; + +/** * write a value to a register, subject to its restrictions * @reg: register to write to * @val: value to write @@ -105,4 +130,30 @@ uint64_t register_read(RegisterInfo *reg); void register_reset(RegisterInfo *reg); +/** + * Memory API MMIO write handler that will write to a Register API register. + * _be for big endian variant and _le for little endian. + * @opaque: RegisterInfo to write to + * @addr: Address to write + * @value: Value to write + * @size: Number of bytes to write + */ + +void register_write_memory_be(void *opaque, hwaddr addr, uint64_t value, + unsigned size); +void register_write_memory_le(void *opaque, hwaddr addr, uint64_t value, + unsigned size); + +/** + * Memory API MMIO read handler that will read from a Register API register. + * _be for big endian variant and _le for little endian. + * @opaque: RegisterInfo to read from + * @addr: Address to read + * @size: Number of bytes to read + * returns: Value read from register + */ + +uint64_t register_read_memory_be(void *opaque, hwaddr addr, unsigned size); +uint64_t register_read_memory_le(void *opaque, hwaddr addr, unsigned size); + #endif -- 2.5.0