/* * \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 : public Genode::Register { enum { OFFSET = MMIO_OFFSET }; /** * A bitregion within a register */ template struct Subreg : public Genode::Register::template Subreg { /** * Back reference to containing register */ typedef Register Compound_reg; }; /** * An array of 'SUBREGS' many similar bitregions with distance 'BIT_SHIFT' * 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_ */