/*
* \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_ */