2012-01-09 13:29:03 +01:00
|
|
|
/*
|
2012-01-14 02:42:45 +01:00
|
|
|
* \brief Generic MMIO access framework
|
2012-01-09 13:29:03 +01:00
|
|
|
* \author Martin stein
|
|
|
|
* \date 2011-10-26
|
|
|
|
*/
|
|
|
|
|
2012-01-10 12:56:45 +01:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2012-01-09 13:29:03 +01:00
|
|
|
#ifndef _BASE__INCLUDE__UTIL__MMIO_H_
|
|
|
|
#define _BASE__INCLUDE__UTIL__MMIO_H_
|
|
|
|
|
2012-01-09 21:05:49 +01:00
|
|
|
#include <util/register.h>
|
2012-01-14 02:42:45 +01:00
|
|
|
#include <base/printf.h>
|
2012-01-09 13:29:03 +01:00
|
|
|
|
|
|
|
|
|
|
|
namespace Genode
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* A continuous MMIO region
|
|
|
|
*/
|
|
|
|
class Mmio
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Write 'value' typed to MMIO base + 'o'
|
|
|
|
*/
|
|
|
|
template <typename STORAGE_T>
|
2012-01-14 02:42:45 +01:00
|
|
|
inline void _write(off_t const o, STORAGE_T const value);
|
2012-01-09 13:29:03 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Read typed from MMIO base + 'o'
|
|
|
|
*/
|
|
|
|
template <typename STORAGE_T>
|
2012-01-14 02:42:45 +01:00
|
|
|
inline STORAGE_T _read(off_t const o) const;
|
2012-01-09 13:29:03 +01:00
|
|
|
|
|
|
|
public:
|
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
enum { BYTE_WIDTH_LOG2 = 3, BYTE_WIDTH = 1 << BYTE_WIDTH_LOG2 };
|
2012-01-09 13:29:03 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A POD-like region at offset 'MMIO_OFFSET' within a MMIO region
|
|
|
|
*/
|
|
|
|
template <off_t MMIO_OFFSET, typename STORAGE_T>
|
2012-01-09 21:05:49 +01:00
|
|
|
struct Register : public Genode::Register<STORAGE_T>
|
2012-01-09 13:29:03 +01:00
|
|
|
{
|
|
|
|
enum { OFFSET = MMIO_OFFSET };
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A bitregion within a register
|
2012-01-14 02:42:45 +01:00
|
|
|
*
|
|
|
|
* \detail Subregs are read and written according to their range,
|
|
|
|
* so if we have a 'Subreg<2,3>' and write '0b11101' to it
|
|
|
|
* only '0b101' (shiftet by 2 bits) is written
|
2012-01-09 13:29:03 +01:00
|
|
|
*/
|
|
|
|
template <unsigned long BIT_SHIFT, unsigned long BIT_SIZE>
|
2012-01-09 21:05:49 +01:00
|
|
|
struct Subreg : public Genode::Register<STORAGE_T>::template Subreg<BIT_SHIFT, BIT_SIZE>
|
2012-01-09 13:29:03 +01:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Back reference to containing register
|
|
|
|
*/
|
2012-01-09 21:05:49 +01:00
|
|
|
typedef Register<OFFSET, STORAGE_T> Compound_reg;
|
2012-01-09 13:29:03 +01:00
|
|
|
};
|
2012-01-14 02:42:45 +01:00
|
|
|
};
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
/**
|
|
|
|
* An array of successive similar items
|
|
|
|
*
|
|
|
|
* \detail 'STORAGE_T' width must a power of 2. The array
|
|
|
|
* takes all subregs that are covered by an item width and
|
|
|
|
* iterates them successive 'ITEMS' times, thus subregs out of
|
|
|
|
* the range of the first item are not accessible. Furthermore
|
|
|
|
* the maximum item width is the width of 'STORAGE_T'.
|
|
|
|
* The array is not limited to one 'STORAGE_T' instance,
|
|
|
|
* it uses as much successive instances as needed.
|
|
|
|
*/
|
|
|
|
template <off_t MMIO_OFFSET, typename STORAGE_T, unsigned long ITEMS, unsigned long _ITEM_WIDTH_LOG2>
|
|
|
|
struct Reg_array : public Register<MMIO_OFFSET, STORAGE_T>
|
|
|
|
{
|
|
|
|
enum {
|
|
|
|
MAX_INDEX = ITEMS - 1,
|
|
|
|
ITEM_WIDTH_LOG2 = _ITEM_WIDTH_LOG2,
|
|
|
|
ITEM_WIDTH = 1 << ITEM_WIDTH_LOG2,
|
|
|
|
ITEM_MASK = (1 << ITEM_WIDTH) - 1,
|
|
|
|
ITEMS_PER_REG = (sizeof(STORAGE_T) << BYTE_WIDTH_LOG2) >> ITEM_WIDTH_LOG2,
|
|
|
|
ITEM_IS_REG = ITEMS_PER_REG == 1,
|
|
|
|
STORAGE_WIDTH = BYTE_WIDTH * sizeof(STORAGE_T),
|
2012-01-09 13:29:03 +01:00
|
|
|
|
|
|
|
/**
|
2012-01-14 02:42:45 +01:00
|
|
|
* Static sanity check
|
2012-01-09 13:29:03 +01:00
|
|
|
*/
|
2012-01-14 02:42:45 +01:00
|
|
|
ERROR_ITEMS_OVERSIZED = ITEMS_PER_REG / ITEMS_PER_REG,
|
|
|
|
};
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
/**
|
|
|
|
* A bitregion within a register array
|
|
|
|
*
|
|
|
|
* \detail Subregs are only written and read as far as the
|
|
|
|
* item width reaches, i.e. assume a 'Reg_array<0,uint8_t,6,2>'
|
|
|
|
* that contains a 'Subreg<1,5>' and we write '0b01101' to it, then
|
|
|
|
* only '0b101' is written and read in consequence
|
|
|
|
*/
|
|
|
|
template <unsigned long BIT_SHIFT, unsigned long BIT_SIZE>
|
|
|
|
struct Subreg : public Register<MMIO_OFFSET, STORAGE_T>::template Subreg<BIT_SHIFT, BIT_SIZE>
|
|
|
|
{
|
2012-01-09 13:29:03 +01:00
|
|
|
/**
|
2012-01-14 02:42:45 +01:00
|
|
|
* Back reference to containing register array
|
2012-01-09 13:29:03 +01:00
|
|
|
*/
|
2012-01-14 02:42:45 +01:00
|
|
|
typedef Reg_array<MMIO_OFFSET, STORAGE_T, ITEMS, ITEM_WIDTH_LOG2> Compound_array;
|
2012-01-09 13:29:03 +01:00
|
|
|
};
|
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
/**
|
|
|
|
* Calculate the MMIO-relative offset 'offset' and shift 'shift'
|
|
|
|
* within the according 'storage_t' instance to access this subreg
|
|
|
|
* from item 'index'
|
|
|
|
*/
|
|
|
|
static inline void access_dest(off_t & offset, unsigned long & shift,
|
|
|
|
unsigned long const index)
|
|
|
|
{
|
|
|
|
unsigned long const bit_off = index << ITEM_WIDTH_LOG2;
|
|
|
|
offset = (off_t) ((bit_off >> BYTE_WIDTH_LOG2) & ~(sizeof(STORAGE_T)-1) );
|
|
|
|
shift = bit_off - ( offset << BYTE_WIDTH_LOG2 );
|
|
|
|
offset += MMIO_OFFSET;
|
|
|
|
}
|
|
|
|
};
|
2012-01-09 13:29:03 +01:00
|
|
|
|
|
|
|
addr_t const base;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*/
|
2012-01-14 02:42:45 +01:00
|
|
|
inline Mmio(addr_t mmio_base) : base(mmio_base) { }
|
|
|
|
|
|
|
|
|
|
|
|
/*************************
|
|
|
|
** Access to registers **
|
|
|
|
*************************/
|
2012-01-09 13:29:03 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Typed address of register 'REGISTER'
|
|
|
|
*/
|
|
|
|
template <typename REGISTER>
|
|
|
|
inline typename REGISTER::storage_t volatile * typed_addr() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read the whole register 'REGISTER'
|
|
|
|
*/
|
|
|
|
template <typename REGISTER>
|
|
|
|
inline typename REGISTER::storage_t read() const;
|
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
/**
|
|
|
|
* Override the register 'REGISTER' with 'value'
|
|
|
|
*/
|
|
|
|
template <typename REGISTER>
|
|
|
|
inline void write(typename REGISTER::storage_t const value);
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************
|
|
|
|
** Access to subregs within registers **
|
|
|
|
****************************************/
|
|
|
|
|
2012-01-09 13:29:03 +01:00
|
|
|
/**
|
|
|
|
* Read the subreg 'SUBREG'
|
|
|
|
*/
|
|
|
|
template <typename SUBREG>
|
|
|
|
inline typename SUBREG::Compound_reg::storage_t read() const;
|
|
|
|
|
|
|
|
/**
|
2012-01-14 02:42:45 +01:00
|
|
|
* Override the subreg 'SUBREG' with 'value'
|
2012-01-09 13:29:03 +01:00
|
|
|
*/
|
2012-01-14 02:42:45 +01:00
|
|
|
template <typename SUBREG>
|
|
|
|
inline void write(typename SUBREG::Compound_reg::storage_t const value);
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
|
|
|
|
/*******************************
|
|
|
|
** Access to register arrays **
|
|
|
|
*******************************/
|
2012-01-09 13:29:03 +01:00
|
|
|
|
|
|
|
/**
|
2012-01-14 02:42:45 +01:00
|
|
|
* Read the whole item 'index' of the array 'REG_ARRAY'
|
2012-01-09 13:29:03 +01:00
|
|
|
*/
|
2012-01-14 02:42:45 +01:00
|
|
|
template <typename REG_ARRAY>
|
|
|
|
inline typename REG_ARRAY::storage_t read(unsigned long const index) const;
|
2012-01-09 13:29:03 +01:00
|
|
|
|
|
|
|
/**
|
2012-01-14 02:42:45 +01:00
|
|
|
* Override item 'index' of the array 'REG_ARRAY' with 'value'
|
2012-01-09 13:29:03 +01:00
|
|
|
*/
|
|
|
|
template <typename REG_ARRAY>
|
|
|
|
inline void write(typename REG_ARRAY::storage_t const value,
|
|
|
|
unsigned long const index);
|
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
|
|
|
|
/***************************************************
|
|
|
|
** Access to subregs within register array items **
|
|
|
|
***************************************************/
|
|
|
|
|
2012-01-09 13:29:03 +01:00
|
|
|
/**
|
2012-01-14 02:42:45 +01:00
|
|
|
* Read the subreg 'ARRAY_SUBREG' of item 'index' of the compound reg array
|
2012-01-09 13:29:03 +01:00
|
|
|
*/
|
2012-01-14 02:42:45 +01:00
|
|
|
template <typename ARRAY_SUBREG>
|
|
|
|
inline typename ARRAY_SUBREG::Compound_array::storage_t read(unsigned long const index) const;
|
2012-01-09 13:29:03 +01:00
|
|
|
|
|
|
|
/**
|
2012-01-14 02:42:45 +01:00
|
|
|
* Override subreg 'ARRAY_SUBREG' of item 'index' of the compound reg array with 'value'
|
2012-01-09 13:29:03 +01:00
|
|
|
*/
|
2012-01-14 02:42:45 +01:00
|
|
|
template <typename ARRAY_SUBREG>
|
|
|
|
inline void write(typename ARRAY_SUBREG::Compound_array::storage_t const value, long unsigned const index);
|
2012-01-09 13:29:03 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <typename STORAGE_T>
|
|
|
|
void Genode::Mmio::_write(off_t const o, STORAGE_T const value)
|
|
|
|
{
|
|
|
|
*(STORAGE_T volatile *)((addr_t)base + o) = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <typename STORAGE_T>
|
|
|
|
STORAGE_T Genode::Mmio::_read(off_t const o) const
|
|
|
|
{
|
|
|
|
return *(STORAGE_T volatile *)((addr_t)base + o);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
/*************************
|
|
|
|
** Access to registers **
|
|
|
|
*************************/
|
2012-01-09 13:29:03 +01:00
|
|
|
|
|
|
|
|
|
|
|
template <typename REGISTER>
|
|
|
|
typename REGISTER::storage_t volatile * Genode::Mmio::typed_addr() const
|
|
|
|
{
|
|
|
|
return (typename REGISTER::storage_t volatile *)base + REGISTER::OFFSET;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <typename REGISTER>
|
|
|
|
typename REGISTER::storage_t Genode::Mmio::read() const
|
|
|
|
{
|
|
|
|
return _read<typename REGISTER::storage_t>(REGISTER::OFFSET);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
template <typename REGISTER>
|
|
|
|
void Genode::Mmio::write(typename REGISTER::storage_t const value)
|
|
|
|
{
|
|
|
|
_write<typename REGISTER::storage_t>(REGISTER::OFFSET, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************
|
|
|
|
** Access to subregs within registers **
|
|
|
|
****************************************/
|
|
|
|
|
|
|
|
|
2012-01-09 13:29:03 +01:00
|
|
|
template <typename SUBREG>
|
|
|
|
typename SUBREG::Compound_reg::storage_t Genode::Mmio::read() const
|
|
|
|
{
|
|
|
|
typedef typename SUBREG::Compound_reg Register;
|
|
|
|
typedef typename Register::storage_t storage_t;
|
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
return SUBREG::get(_read<storage_t>(Register::OFFSET));
|
2012-01-09 13:29:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
template <typename SUBREG>
|
|
|
|
void Genode::Mmio::write(typename SUBREG::Compound_reg::storage_t const value)
|
2012-01-09 13:29:03 +01:00
|
|
|
{
|
2012-01-14 02:42:45 +01:00
|
|
|
typedef typename SUBREG::Compound_reg Register;
|
|
|
|
typedef typename Register::storage_t storage_t;
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
storage_t new_reg = read<Register>();
|
|
|
|
SUBREG::clear(new_reg);
|
|
|
|
SUBREG::set(new_reg, value);
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
write<Register>(new_reg);
|
2012-01-09 13:29:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
/************************************
|
|
|
|
** Access to register array items **
|
|
|
|
************************************/
|
|
|
|
|
|
|
|
|
|
|
|
template <typename REG_ARRAY>
|
|
|
|
typename REG_ARRAY::storage_t Genode::Mmio::read(unsigned long const index) const
|
2012-01-09 13:29:03 +01:00
|
|
|
{
|
2012-01-14 02:42:45 +01:00
|
|
|
/**
|
|
|
|
* Handle array overflow
|
|
|
|
*/
|
|
|
|
if (index > REG_ARRAY::MAX_INDEX) return 0;
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
off_t offset;
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
/**
|
|
|
|
* Optimize access if item width equals storage type width
|
|
|
|
*/
|
|
|
|
if (REG_ARRAY::ITEM_IS_REG) {
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
offset = REG_ARRAY::OFFSET + (index << REG_ARRAY::ITEM_WIDTH_LOG2);
|
|
|
|
return _read<typename REG_ARRAY::storage_t>(offset);
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
} else {
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
long unsigned shift;
|
|
|
|
REG_ARRAY::access_dest(offset, shift, index);
|
|
|
|
return (_read<typename REG_ARRAY::storage_t>(offset) >> shift) & REG_ARRAY::ITEM_MASK;
|
|
|
|
}
|
2012-01-09 13:29:03 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <typename REG_ARRAY>
|
|
|
|
void Genode::Mmio::write(typename REG_ARRAY::storage_t const value,
|
|
|
|
unsigned long const index)
|
|
|
|
{
|
2012-01-14 02:42:45 +01:00
|
|
|
/**
|
|
|
|
* Avoid array overflow
|
|
|
|
*/
|
2012-01-09 13:29:03 +01:00
|
|
|
if (index > REG_ARRAY::MAX_INDEX) return;
|
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
off_t offset;
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
/**
|
|
|
|
* Optimize access if item width equals storage type width
|
|
|
|
*/
|
|
|
|
if (REG_ARRAY::ITEM_IS_REG) {
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
offset = REG_ARRAY::OFFSET + (index << REG_ARRAY::ITEM_WIDTH_LOG2);
|
|
|
|
_write<typename REG_ARRAY::storage_t>(offset, value);
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
} else {
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
long unsigned shift;
|
|
|
|
REG_ARRAY::access_dest(offset, shift, index);
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
/**
|
|
|
|
* Insert new value into old register value
|
|
|
|
*/
|
|
|
|
typename REG_ARRAY::storage_t new_reg = _read<typename REG_ARRAY::storage_t>(offset);
|
|
|
|
new_reg &= ~(REG_ARRAY::ITEM_MASK << shift);
|
|
|
|
new_reg |= (value & REG_ARRAY::ITEM_MASK) << shift;
|
|
|
|
|
|
|
|
_write<typename REG_ARRAY::storage_t>(offset, new_reg);
|
|
|
|
}
|
2012-01-09 13:29:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
/***************************************************
|
|
|
|
** Access to subregs within register array items **
|
|
|
|
***************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
template <typename ARRAY_SUBREG>
|
|
|
|
void Genode::Mmio::write(typename ARRAY_SUBREG::Compound_array::storage_t const value,
|
|
|
|
long unsigned const index)
|
2012-01-09 13:29:03 +01:00
|
|
|
{
|
2012-01-14 02:42:45 +01:00
|
|
|
typedef typename ARRAY_SUBREG::Compound_array Reg_array;
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
typename Reg_array::storage_t new_reg = read<Reg_array>(index);
|
|
|
|
ARRAY_SUBREG::clear(new_reg);
|
|
|
|
ARRAY_SUBREG::set(new_reg, value);
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
write<Reg_array>(new_reg, index);
|
|
|
|
}
|
2012-01-09 13:29:03 +01:00
|
|
|
|
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
template <typename ARRAY_SUBREG>
|
|
|
|
typename ARRAY_SUBREG::Compound_array::storage_t Genode::Mmio::read(long unsigned const index) const
|
|
|
|
{
|
|
|
|
typedef typename ARRAY_SUBREG::Compound_array Array;
|
|
|
|
typedef typename Array::storage_t storage_t;
|
2012-01-09 13:29:03 +01:00
|
|
|
|
2012-01-14 02:42:45 +01:00
|
|
|
return ARRAY_SUBREG::get(read<Array>(index));
|
2012-01-09 13:29:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* _BASE__INCLUDE__UTIL__MMIO_H_ */
|
2012-01-09 21:05:49 +01:00
|
|
|
|