diff --git a/base/include/util/mmio.h b/base/include/util/mmio.h new file mode 100644 index 0000000000..d4506fb5e5 --- /dev/null +++ b/base/include/util/mmio.h @@ -0,0 +1,327 @@ +/* + * \brief Generic MMIO accessor framework + * \author Martin stein + * \date 2011-10-26 + */ + +#ifndef _BASE__INCLUDE__UTIL__MMIO_H_ +#define _BASE__INCLUDE__UTIL__MMIO_H_ + +#include + + +namespace Genode +{ + /** + * A continuous MMIO region + */ + class Mmio + { + protected: + + /** + * Write 'value' typed to MMIO base + 'o' + */ + template + void _write(off_t const o, STORAGE_T const value); + + /** + * Read typed from MMIO base + 'o' + */ + template + STORAGE_T _read(off_t const o) const; + + public: + + enum { BYTE_EXP = 3, BYTE_WIDTH = 8 }; + + /** + * Holds additional info for an array of the inheriting type + */ + template + struct Array { enum { MAX_INDEX = SIZE-1 }; }; + + /** + * A POD-like region at offset 'MMIO_OFFSET' within a MMIO region + */ + template + struct Register + { + typedef STORAGE_T storage_t; + enum { OFFSET = MMIO_OFFSET }; + + /** + * A bitregion within a register + */ + template + struct Subreg + { + enum { + SHIFT = BIT_SHIFT, + WIDTH = BIT_SIZE, + MASK = (1 << WIDTH) - 1, + }; + + /** + * Back reference to containing register + */ + typedef Register Compound_reg; + + /** + * Get a register value with this subreg set to 'value' + * and the rest left zero + */ + static storage_t bits(storage_t const value) + { + return (value & MASK) << SHIFT; + }; + }; + + /** + * An array of 'SUBREGS' many similar bitregions + * FIXME: Side effects of a combination of 'Reg_array' and 'Subreg_array' + * are not evaluated + */ + template + struct Subreg_array : public Array + { + enum { + SHIFT = BIT_SHIFT, + WIDTH = BIT_SIZE, + MASK = (1 << WIDTH) - 1, + ITERATION_WIDTH = (SHIFT + WIDTH), + STORAGE_WIDTH = BYTE_WIDTH * sizeof(storage_t), + ARRAY_SIZE = (ITERATION_WIDTH * SUBREGS) >> BYTE_EXP, + }; + + /** + * Back reference to containing register + */ + typedef Register Compound_reg; + + /** + * Calculate the MMIO-relative offset 'offset' and shift 'shift' + * within the according 'storage_t' value to acces subreg no. 'index' + */ + inline static void access_dest(off_t & offset, unsigned long & shift, + unsigned long const index); + }; + }; + + /** + * An array of 'REGS' many similar 'Register's + */ + template + struct Reg_array : public Register, + public Array + { }; + + addr_t const base; + + /** + * Constructor + */ + inline Mmio(addr_t mmio_base); + + /** + * Typed address of register 'REGISTER' + */ + template + inline typename REGISTER::storage_t volatile * typed_addr() const; + + /** + * Read the whole register 'REGISTER' + */ + template + inline typename REGISTER::storage_t read() const; + + /** + * Read the subreg 'SUBREG' + */ + template + inline typename SUBREG::Compound_reg::storage_t read() const; + + /** + * Read the whole register no. 'index' of the array 'REG_ARRAY' + */ + template + inline typename REG_ARRAY::storage_t read(unsigned long const index) const; + + /** + * Read the subreg no. 'index' of the array 'SUBREG_ARRAY' + */ + template + inline typename SUBREG_ARRAY::Compound_reg::storage_t read(unsigned long const index); + + /** + * Write 'value' into the register 'REGISTER' + */ + template + inline void write(typename REGISTER::storage_t const value); + + /** + * Write 'value' into the register no. 'index' of the array 'REG_ARRAY' + */ + template + inline void write(typename REG_ARRAY::storage_t const value, + unsigned long const index); + + /** + * Write 'value' into the subregister 'SUBREG' + */ + template + inline void write(typename SUBREG::Compound_reg::storage_t const value); + + /** + * Write 'value' into the bitfield no. 'index' of the array 'SUBREG_ARRAY' + */ + template + inline void write(typename SUBREG_ARRAY::Compound_reg::storage_t const value, + unsigned long const index); + }; +} + + +template +template +void +Genode::Mmio::Register::Subreg_array::access_dest(Genode::off_t & offset, + unsigned long & shift, + unsigned long const index) +{ + unsigned long const bit_off = (index+1) * ITERATION_WIDTH - WIDTH; + offset = (off_t) ((bit_off / STORAGE_WIDTH) * sizeof(storage_t)); + shift = bit_off - ( offset << BYTE_EXP ); + offset += Compound_reg::OFFSET; +} + + +template +void Genode::Mmio::_write(off_t const o, STORAGE_T const value) +{ + *(STORAGE_T volatile *)((addr_t)base + o) = value; +} + + +template +STORAGE_T Genode::Mmio::_read(off_t const o) const +{ + return *(STORAGE_T volatile *)((addr_t)base + o); +} + + +Genode::Mmio::Mmio(addr_t mmio_base) : base(mmio_base) { } + + +template +typename REGISTER::storage_t volatile * Genode::Mmio::typed_addr() const +{ + return (typename REGISTER::storage_t volatile *)base + REGISTER::OFFSET; +} + + +template +typename REGISTER::storage_t Genode::Mmio::read() const +{ + return _read(REGISTER::OFFSET); +} + + +template +typename SUBREG::Compound_reg::storage_t Genode::Mmio::read() const +{ + typedef typename SUBREG::Compound_reg Register; + typedef typename Register::storage_t storage_t; + + return (_read(Register::OFFSET) >> SUBREG::SHIFT) & SUBREG::MASK; +} + + +template +typename REG_ARRAY::storage_t Genode::Mmio::read(unsigned long const index) const +{ + typedef typename REG_ARRAY::storage_t storage_t; + + if (index > REG_ARRAY::MAX_INDEX) return 0; + + addr_t const offset = REG_ARRAY::OFFSET + index*sizeof(storage_t); + return _read(offset); +} + + +template +typename SUBREG_ARRAY::Compound_reg::storage_t Genode::Mmio::read(unsigned long const index) +{ + enum { MASK = SUBREG_ARRAY::MASK }; + + typedef typename SUBREG_ARRAY::Compound_reg Register; + typedef typename Register::storage_t storage_t; + + if (index > SUBREG_ARRAY::MAX_INDEX) return; + + off_t const offset = Register::OFFSET + SUBREG_ARRAY::access_off(index); + unsigned long const shift = SUBREG_ARRAY::access_shift(index); + + return (_read(offset) >> shift) & MASK; +} + + +template +void Genode::Mmio::write(typename REGISTER::storage_t const value) +{ + _write(REGISTER::OFFSET, value); +} + + +template +void Genode::Mmio::write(typename REG_ARRAY::storage_t const value, + unsigned long const index) +{ + typedef typename REG_ARRAY::storage_t storage_t; + + if (index > REG_ARRAY::MAX_INDEX) return; + + addr_t const offset = REG_ARRAY::OFFSET + index*sizeof(storage_t); + _write(offset, value); +} + + +template +void Genode::Mmio::write(typename SUBREG::Compound_reg::storage_t const value) +{ + typedef typename SUBREG::Compound_reg Register; + typedef typename Register::storage_t storage_t; + + storage_t new_reg = read(); + + new_reg &= ~(SUBREG::MASK << SUBREG::SHIFT); + new_reg |= (value & SUBREG::MASK) << SUBREG::SHIFT; + + write(new_reg); +} + + +template +void Genode::Mmio::write(typename SUBREG_ARRAY::Compound_reg::storage_t const value, + unsigned long const index) +{ + enum { MASK = SUBREG_ARRAY::MASK }; + + typedef typename SUBREG_ARRAY::Compound_reg Register; + typedef typename Register::storage_t storage_t; + + if (index > SUBREG_ARRAY::MAX_INDEX) return; + + off_t offset; + unsigned long shift; + SUBREG_ARRAY::access_dest(offset, shift, index); + + storage_t new_field = _read(offset); + new_field &= ~(MASK << shift); + new_field |= (value & MASK) << shift; + + _write(offset, new_field); +} + + +#endif /* _BASE__INCLUDE__UTIL__MMIO_H_ */ diff --git a/base/src/test/util_mmio/main.cc b/base/src/test/util_mmio/main.cc new file mode 100644 index 0000000000..5534805133 --- /dev/null +++ b/base/src/test/util_mmio/main.cc @@ -0,0 +1,19 @@ +/* + * \brief Basic test for MMIO access framework + * \author Christian Helmuth + * \date 2012-01-09 + */ + +/* + * Copyright (C) 2011-2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + + +int main() +{ +} diff --git a/base/src/test/util_mmio/target.mk b/base/src/test/util_mmio/target.mk new file mode 100644 index 0000000000..42246e1dae --- /dev/null +++ b/base/src/test/util_mmio/target.mk @@ -0,0 +1,4 @@ +TARGET = test-util_mmio +SRC_CC = main.cc + +LIBS = cxx env