l4re-base-25.08.0

This commit is contained in:
2025-09-12 15:55:45 +02:00
commit d959eaab98
37938 changed files with 9382688 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Jakub Jermar <jakub.jermar@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/l4int.h>
#include <x86/l4/drivers/asm_access.h>
namespace Asm_access {
inline
l4_uint64_t
read(l4_uint64_t const *mem)
{
l4_uint64_t val;
asm volatile ("movq %[mem], %[val]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
void
write(l4_uint64_t val, l4_uint64_t *mem)
{
asm volatile ("movq %[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Jakub Jermar <jakub.jermar@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/l4int.h>
namespace Asm_access {
inline
l4_uint8_t
read(l4_uint8_t const *mem)
{
l4_uint8_t val;
asm volatile ("ldrb %[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint16_t
read(l4_uint16_t const *mem)
{
l4_uint16_t val;
asm volatile ("ldrh %[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint32_t
read(l4_uint32_t const *mem)
{
l4_uint32_t val;
asm volatile ("ldr %[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
void
write(l4_uint8_t val, l4_uint8_t *mem)
{
asm volatile ("strb %[val], %[mem]" :[mem] "=m" (*mem) : [val] "r" (val));
}
inline
void
write(l4_uint16_t val, l4_uint16_t *mem)
{
asm volatile ("strh %[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
inline
void
write(l4_uint32_t val, l4_uint32_t *mem)
{
asm volatile ("str %[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Jakub Jermar <jakub.jermar@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/l4int.h>
namespace Asm_access {
inline
l4_uint8_t
read(l4_uint8_t const *mem)
{
l4_uint8_t val;
asm volatile ("ldrb %w[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint16_t
read(l4_uint16_t const *mem)
{
l4_uint16_t val;
asm volatile ("ldrh %w[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint32_t
read(l4_uint32_t const *mem)
{
l4_uint32_t val;
asm volatile ("ldr %w[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint64_t
read(l4_uint64_t const *mem)
{
l4_uint64_t val;
asm volatile ("ldr %[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
void
write(l4_uint8_t val, l4_uint8_t *mem)
{
asm volatile ("strb %w[val], %[mem]" :[mem] "=m" (*mem) : [val] "r" (val));
}
inline
void
write(l4_uint16_t val, l4_uint16_t *mem)
{
asm volatile ("strh %w[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
inline
void
write(l4_uint32_t val, l4_uint32_t *mem)
{
asm volatile ("str %w[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
inline
void
write(l4_uint64_t val, l4_uint64_t *mem)
{
asm volatile ("str %[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
}

View File

@@ -0,0 +1,10 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Jakub Jermar <jakub.jermar@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/drivers/asm_access_gen.h>

View File

@@ -0,0 +1,10 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Jakub Jermar <jakub.jermar@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/drivers/asm_access_gen.h>

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Georg Kotheimer <georg.kotheimer@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/l4int.h>
namespace Asm_access {
inline
l4_uint8_t
read(l4_uint8_t const *mem)
{
l4_uint8_t val;
asm volatile ("lb %[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint16_t
read(l4_uint16_t const *mem)
{
l4_uint16_t val;
asm volatile ("lh %[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint32_t
read(l4_uint32_t const *mem)
{
l4_uint32_t val;
asm volatile ("lw %[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
void
write(l4_uint8_t val, l4_uint8_t *mem)
{
asm volatile ("sb %[val], %[mem]" :[mem] "=m" (*mem) : [val] "r" (val));
}
inline
void
write(l4_uint16_t val, l4_uint16_t *mem)
{
asm volatile ("sh %[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
inline
void
write(l4_uint32_t val, l4_uint32_t *mem)
{
asm volatile ("sw %[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
#if __riscv_xlen == 64
inline
l4_uint64_t
read(l4_uint64_t const *mem)
{
l4_uint64_t val;
asm volatile ("ld %[val], %[mem]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
void
write(l4_uint64_t val, l4_uint64_t *mem)
{
asm volatile ("sd %[val], %[mem]" : [mem] "=m" (*mem) : [val] "r" (val));
}
#endif
}

View File

@@ -0,0 +1,10 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Jakub Jermar <jakub.jermar@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/drivers/asm_access_gen.h>

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Jakub Jermar <jakub.jermar@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/l4int.h>
namespace Asm_access {
inline
l4_uint8_t
read(l4_uint8_t const *mem)
{
l4_uint8_t val;
asm volatile ("movb %[mem], %[val]" : [val] "=q" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint16_t
read(l4_uint16_t const *mem)
{
l4_uint16_t val;
asm volatile ("movw %[mem], %[val]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
l4_uint32_t
read(l4_uint32_t const *mem)
{
l4_uint32_t val;
asm volatile ("movl %[mem], %[val]" : [val] "=r" (val) : [mem] "m" (*mem));
return val;
}
inline
void
write(l4_uint8_t val, l4_uint8_t *mem)
{
asm volatile ("movb %[val], %[mem]" :[mem] "=m" (*mem) : [val] "qi" (val));
}
inline
void
write(l4_uint16_t val, l4_uint16_t *mem)
{
asm volatile ("movw %[val], %[mem]" : [mem] "=m" (*mem) : [val] "ri" (val));
}
inline
void
write(l4_uint32_t val, l4_uint32_t *mem)
{
asm volatile ("movl %[val], %[mem]" : [mem] "=m" (*mem) : [val] "ri" (val));
}
}

View File

@@ -0,0 +1,8 @@
PKGDIR = ..
L4DIR ?= $(PKGDIR)/../..
PKGNAME = drivers
PC_FILENAME = drivers-frst
EXTRA_TARGET += hw_mmio_register_block hw_register_block
include $(L4DIR)/mk/include.mk

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2021, 2024 Kernkonzept GmbH.
* Author(s): Jakub Jermar <jakub.jermar@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/l4int.h>
#include <l4/cxx/type_traits>
namespace Asm_access {
template <typename T>
struct is_supported_type
{
static const bool value = cxx::is_same<T, l4_uint8_t>::value
|| cxx::is_same<T, l4_uint16_t>::value
|| cxx::is_same<T, l4_uint32_t>::value
|| cxx::is_same<T, l4_uint64_t>::value;
};
template <typename T>
inline
typename cxx::enable_if<is_supported_type<T>::value, T>::type
read(T const *mem)
{
return *reinterpret_cast<volatile T const *>(mem);
}
template <typename T>
inline
typename cxx::enable_if<is_supported_type<T>::value, void>::type
write(T val, T *mem)
{
*reinterpret_cast<volatile T *>(mem) = val;
}
}

View File

@@ -0,0 +1,51 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* Copyright (C) 2014-2021, 2023-2024 Kernkonzept GmbH.
* Author(s): Alexander Warg <alexander.warg@kernkonzept.com>
* Sarah Hoffmann <sarah.hoffmann@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/drivers/hw_register_block>
#include <l4/drivers/asm_access.h>
namespace L4drivers {
class Mmio_register_block_base
{
protected:
l4_addr_t _base;
l4_addr_t _shift;
public:
explicit Mmio_register_block_base(l4_addr_t base = 0, l4_addr_t shift = 0)
: _base(base), _shift(shift) {}
template< typename T >
T read(l4_addr_t reg) const
{ return Asm_access::read(reinterpret_cast<T const *>(_base + (reg << _shift))); }
template< typename T >
void write(T value, l4_addr_t reg) const
{ Asm_access::write(value, reinterpret_cast<T *>(_base + (reg << _shift))); }
void set_base(l4_addr_t base) { _base = base; }
void set_shift(l4_addr_t shift) { _shift = shift; }
};
/**
* An MMIO block with up to 64-bit register access (32-bit default) and little
* endian byte order.
*/
template< unsigned MAX_BITS = 32 >
struct Mmio_register_block
: Register_block_impl<Mmio_register_block<MAX_BITS>, MAX_BITS>,
Mmio_register_block_base
{
explicit Mmio_register_block(l4_addr_t base = 0, l4_addr_t shift = 0)
: Mmio_register_block_base(base, shift) {}
};
}

View File

@@ -0,0 +1,497 @@
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* Copyright (C) 2014-2021, 2023-2024 Kernkonzept GmbH.
* Author(s): Alexander Warg <alexander.warg@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/types.h>
#include <l4/cxx/type_traits>
namespace L4drivers {
/**
* Register block.
* \class Register_block
* \details Example usage:
*
* \code{.cpp}
* void test()
* {
* // create a register block reference for max. 16bit accesses, using a
* // MMIO register block implementation (at address 0x1000).
* Hw::Register_block<16> regs = new Hw::Mmio_register_block<16>(0x1000);
*
* // Alternatively it is allowed to use an implementation that allows
* // wider access than actually needed.
* Hw::Register_block<16> regs = new Hw::Mmio_register_block<32>(0x1000);
*
* // read a 16bit register at offset 8byte
* unsigned short x = regs.r<16>(8);
* unsigned short x1 = regs[8]; // alternative
*
* // read an 8bit register at offset 0byte
* unsigned v = regs.r<8>(0);
*
* // do a 16bit write to register at offset 2byte (four variants)
* regs[2] = 22;
* regs.r<16>(2) = 22;
* regs[2].write(22);
* regs.r<16>().write(22);
*
* // do an 8bit write (two variants)
* regs.r<8>(0) = 9;
* regs.r<8>(0).write(9);
*
* // do 16bit read-modify-write (two variants)
* regs[4].modify(0xf, 3); // clear 4 lowest bits and set them to 3
* regs.r<16>(4).modify(0xf, 3);
*
* // do 8bit read-modify-write
* regs.r<8>(0).modify(0xf, 3);
*
* // fails to compile, because of too wide access
* // (32 bit access but regs is Hw::Register_block<16>)
* unsigned long v = regs.r<32>(4)
* }
* \endcode
*/
/**
* \brief Abstract register block interface
* \tparam MAX_BITS The maximum access width for the registers.
*
* This interfaces is based on virtual do_read_<xx> and do_write_<xx>
* methods that have to be implemented up to the maximum access width.
*/
template< unsigned MAX_BITS = 32 >
struct Register_block_base;
template<>
struct Register_block_base<8>
{
virtual l4_uint8_t do_read_8(l4_addr_t reg) const = 0;
virtual void do_write_8(l4_uint8_t value, l4_addr_t reg) = 0;
virtual ~Register_block_base() = 0;
};
inline Register_block_base<8>::~Register_block_base() {}
template<>
struct Register_block_base<16> : Register_block_base<8>
{
virtual l4_uint16_t do_read_16(l4_addr_t reg) const = 0;
virtual void do_write_16(l4_uint16_t value, l4_addr_t reg) = 0;
};
template<>
struct Register_block_base<32> : Register_block_base<16>
{
virtual l4_uint32_t do_read_32(l4_addr_t reg) const = 0;
virtual void do_write_32(l4_uint32_t value, l4_addr_t reg) = 0;
};
template<>
struct Register_block_base<64> : Register_block_base<32>
{
virtual l4_uint64_t do_read_64(l4_addr_t reg) const = 0;
virtual void do_write_64(l4_uint64_t value, l4_addr_t reg) = 0;
};
#undef REGBLK_READ_TEMPLATE
#undef REGBLK_WRITE_TEMPLATE
template<typename CHILD>
struct Register_block_modify_mixin
{
template< typename T >
T modify(T clear_bits, T set_bits, l4_addr_t reg) const
{
CHILD const *c = static_cast<CHILD const *>(this);
T r = (c->template read<T>(reg) & ~clear_bits) | set_bits;
c->template write<T>(r, reg);
return r;
}
template< typename T >
T set(T set_bits, l4_addr_t reg) const
{ return this->template modify<T>(T(0), set_bits, reg); }
template< typename T >
T clear(T clear_bits, l4_addr_t reg) const
{ return this->template modify<T>(clear_bits, T(0), reg); }
};
#define REGBLK_READ_TEMPLATE(sz) \
template< typename T > \
typename cxx::enable_if<sizeof(T) == (sz / 8), T>::type read(l4_addr_t reg) const \
{ \
union X { T t; l4_uint##sz##_t v; } m; \
m.v = _b->do_read_##sz (reg); \
return m.t; \
}
#define REGBLK_WRITE_TEMPLATE(sz) \
template< typename T > \
void write(T value, l4_addr_t reg, typename cxx::enable_if<sizeof(T) == (sz / 8), T>::type = T()) const \
{ \
union X { T t; l4_uint##sz##_t v; } m; \
m.t = value; \
_b->do_write_##sz(m.v, reg); \
}
/**
* \brief Helper template that translates to the Register_block_base
* interface.
* \tparam BLOCK The type of the Register_block_base interface to use.
*
* This helper translates read<T>(), write<T>(), set<T>(), clear<T>(),
* and modify<T>() calls to BLOCK::do_read_<xx> and BLOCK::do_write_<xx>.
*/
template< typename BLOCK >
class Register_block_tmpl
: public Register_block_modify_mixin<Register_block_tmpl<BLOCK> >
{
private:
BLOCK *_b;
public:
Register_block_tmpl(BLOCK *blk) : _b(blk) {}
Register_block_tmpl() = default;
operator BLOCK * () const { return _b; }
REGBLK_READ_TEMPLATE(8)
REGBLK_WRITE_TEMPLATE(8)
REGBLK_READ_TEMPLATE(16)
REGBLK_WRITE_TEMPLATE(16)
REGBLK_READ_TEMPLATE(32)
REGBLK_WRITE_TEMPLATE(32)
REGBLK_READ_TEMPLATE(64)
REGBLK_WRITE_TEMPLATE(64)
};
#undef REGBLK_READ_TEMPLATE
#undef REGBLK_WRITE_TEMPLATE
namespace __Type_helper {
template<unsigned> struct Unsigned;
template<> struct Unsigned<8> { typedef l4_uint8_t type; };
template<> struct Unsigned<16> { typedef l4_uint16_t type; };
template<> struct Unsigned<32> { typedef l4_uint32_t type; };
template<> struct Unsigned<64> { typedef l4_uint64_t type; };
};
/**
* \brief Single read only register inside a Register_block_base interface.
* \tparam BITS The access with of the register in bits.
* \tparam BLOCK The type for the Register_block_base interface.
* \note Objects of this type must be used only in temporary contexts
* not in global, class, or object scope.
*
* Allows simple read only access to a hardware register.
*/
template< unsigned BITS, typename BLOCK >
class Ro_register_tmpl
{
protected:
BLOCK _b;
unsigned _o;
public:
typedef typename __Type_helper::Unsigned<BITS>::type value_type;
Ro_register_tmpl(BLOCK const &blk, unsigned offset) : _b(blk), _o(offset) {}
Ro_register_tmpl() = default;
/**
* \brief read the value from the hardware register.
* \return value read from the hardware register.
*/
operator value_type () const
{ return _b.template read<value_type>(_o); }
/**
* \brief read the value from the hardware register.
* \return value from the hardware register.
*/
value_type read() const
{ return _b.template read<value_type>(_o); }
};
/**
* \brief Single hardware register inside a Register_block_base interface.
* \tparam BITS The access width for the register in bits.
* \tparam BLOCK the type of the Register_block_base interface.
* \note Objects of this type must be used only in temporary contexts
* not in global, class, or object scope.
*/
template< unsigned BITS, typename BLOCK >
class Register_tmpl : public Ro_register_tmpl<BITS, BLOCK>
{
public:
typedef typename Ro_register_tmpl<BITS, BLOCK>::value_type value_type;
Register_tmpl(BLOCK const &blk, unsigned offset)
: Ro_register_tmpl<BITS, BLOCK>(blk, offset)
{}
Register_tmpl() = default;
/**
* \brief write \a val into the hardware register.
* \param val the value to write into the hardware register.
*/
Register_tmpl &operator = (value_type val)
{ this->_b.template write<value_type>(val, this->_o); return *this; }
/**
* \brief write \a val into the hardware register.
* \param val the value to write into the hardware register.
*/
void write(value_type val)
{ this->_b.template write<value_type>(val, this->_o); }
/**
* \brief set bits in \a set_bits in the hardware register.
* \param set_bits bits to be set within the hardware register.
*
* This is a read-modify-write function that does a logical or
* of the old value from the register with \a set_bits.
*
* \code
* unsigned old_value = read();
* write(old_value | set_bits);
* \endcode
*/
value_type set(value_type set_bits)
{ return this->_b.template set<value_type>(set_bits, this->_o); }
/**
* \brief clears bits in \a clear_bits in the hardware register.
* \param clear_bits bits to be cleared within the hardware register.
*
* This is a read-modify-write function that does a logical and
* of the old value from the register with the negated value of
* \a clear_bits.
*
* \code
* unsigned old_value = read();
* write(old_value & ~clear_bits);
* \endcode
*/
value_type clear(value_type clear_bits)
{ return this->_b.template clear<value_type>(clear_bits, this->_o); }
/**
* \brief clears bits in \a clear_bits and sets bits in \a set_bits
* in the hardware register.
* \param clear_bits bits to be cleared within the hardware register.
* \param set_bits bits to set in the hardware register.
*
* This is a read-modify-write function that first does a logical and
* of the old value from the register with the negated value of
* \a clear_bits and then does a logical or with \a set_bits.
*
* \code{.c}
* unsigned old_value = read();
* write((old_value & ~clear_bits) | set_bits);
* \endcode
*/
value_type modify(value_type clear_bits, value_type set_bits)
{ return this->_b.template modify<value_type>(clear_bits, set_bits, this->_o); }
};
/**
* \brief Handles a reference to a register block of the given
* maximum access width.
* \tparam MAX_BITS Maximum access width for the registers in this
* block.
* \tparam BLOCK Type implementing the register accesses (`read<>()`,
* `write<>()`, `modify<>()`, `set<>()`, and `clear<>()`).
*
* Provides access to registers in this block via r<WIDTH>() and
* operator[]().
*/
template<
unsigned MAX_BITS,
typename BLOCK = Register_block_tmpl<
Register_block_base<MAX_BITS>
>
>
class Register_block
{
private:
template< unsigned B, typename BLK > friend class Register_block;
template< unsigned B, typename BLK > friend class Ro_register_block;
typedef BLOCK Block;
Block _b;
public:
Register_block() = default;
Register_block(Block const &blk) : _b(blk) {}
Register_block &operator = (Block const &blk)
{ _b = blk; return *this; }
template< unsigned BITS >
Register_block(Register_block<BITS> blk) : _b(blk._b) {}
typedef Register_tmpl<MAX_BITS, Block> Register;
typedef Ro_register_tmpl<MAX_BITS, Block> Ro_register;
/**
* \brief Read only access to register at offset \a offset.
* \tparam BITS the access width in bits for the register.
* \param offset The offset of the register within the register file.
* \return register object allowing read only access with width \a BITS.
*/
template< unsigned BITS >
Ro_register_tmpl<BITS, Block> r(unsigned offset) const
{ return Ro_register_tmpl<BITS, Block>(this->_b, offset); }
/**
* \brief Read only access to register at offset \a offset.
* \param offset The offset of the register within the register file.
* \return register object allowing read only access with width \a MAX_BITS.
*/
Ro_register operator [] (unsigned offset) const
{ return this->r<MAX_BITS>(offset); }
/**
* \brief Read/write access to register at offset \a offset.
* \tparam BITS the access width in bits for the register.
* \param offset The offset of the register within the register file.
* \return register object allowing read and write access with width \a BITS.
*/
template< unsigned BITS >
Register_tmpl<BITS, Block> r(unsigned offset)
{ return Register_tmpl<BITS, Block>(this->_b, offset); }
/**
* \brief Read/write access to register at offset \a offset.
* \param offset The offset of the register within the register file.
* \return register object allowing read and write access with
* width \a MAX_BITS.
*/
Register operator [] (unsigned offset)
{ return this->r<MAX_BITS>(offset); }
};
/**
* \brief Handles a reference to a read only register block of the given
* maximum access width.
* \tparam MAX_BITS Maximum access width for the registers in this block.
* \tparam BLOCK Type implementing the register accesses (read<>()),
*
* Provides read only access to registers in this block via r<WIDTH>()
* and operator[]().
*/
template<
unsigned MAX_BITS,
typename BLOCK = Register_block_tmpl<
Register_block_base<MAX_BITS> const
>
>
class Ro_register_block
{
private:
template< unsigned B, typename BLK > friend class Ro_register_block;
typedef BLOCK Block;
Block _b;
public:
Ro_register_block() = default;
Ro_register_block(BLOCK const &blk) : _b(blk) {}
template< unsigned BITS >
Ro_register_block(Register_block<BITS> const &blk) : _b(blk._b) {}
typedef Ro_register_tmpl<MAX_BITS, Block> Ro_register;
typedef Ro_register Register;
/**
* \brief Read only access to register at offset \a offset.
* \param offset The offset of the register within the register file.
* \return register object allowing read only access with width \a MAX_BITS.
*/
Ro_register operator [] (unsigned offset) const
{ return Ro_register(this->_b, offset); }
/**
* \brief Read only access to register at offset \a offset.
* \tparam BITS the access width in bits for the register.
* \param offset The offset of the register within the register file.
* \return register object allowing read only access with width \a BITS.
*/
template< unsigned BITS >
Ro_register_tmpl<BITS, Block> r(unsigned offset) const
{ return Ro_register_tmpl<BITS, Block>(this->_b, offset); }
};
/**
* \brief Implementation helper for register blocks.
* \param BASE The class implementing read<> and write<> template functions
* for accessing the registers. This class must inherit from
* Register_block_impl.
* \param MAX_BITS The maximum access width for the register file.
* Supported values are 8, 16, 32, or 64.
*
*
* This template allows easy implementation of register files by providing
* read<> and write<> template functions, see Mmio_register_block
* as an example.
*/
template< typename BASE, unsigned MAX_BITS = 32 >
struct Register_block_impl;
#define REGBLK_IMPL_RW_TEMPLATE(sz, ...) \
l4_uint##sz##_t do_read_##sz(l4_addr_t reg) const override \
{ return static_cast<BASE const *>(this)->template read<l4_uint##sz##_t>(reg); } \
\
void do_write_##sz(l4_uint##sz##_t value, l4_addr_t reg) override \
{ static_cast<BASE*>(this)->template write<l4_uint##sz##_t>(value, reg); }
template< typename BASE >
struct Register_block_impl<BASE, 8> : public Register_block_base<8>
{
REGBLK_IMPL_RW_TEMPLATE(8);
};
template< typename BASE >
struct Register_block_impl<BASE, 16> : public Register_block_base<16>
{
REGBLK_IMPL_RW_TEMPLATE(8);
REGBLK_IMPL_RW_TEMPLATE(16);
};
template< typename BASE >
struct Register_block_impl<BASE, 32> : public Register_block_base<32>
{
REGBLK_IMPL_RW_TEMPLATE(8);
REGBLK_IMPL_RW_TEMPLATE(16);
REGBLK_IMPL_RW_TEMPLATE(32);
};
template< typename BASE >
struct Register_block_impl<BASE, 64> : public Register_block_base<64>
{
REGBLK_IMPL_RW_TEMPLATE(8);
REGBLK_IMPL_RW_TEMPLATE(16);
REGBLK_IMPL_RW_TEMPLATE(32);
REGBLK_IMPL_RW_TEMPLATE(64);
};
#undef REGBLK_IMPL_RW_TEMPLATE
}

View File

@@ -0,0 +1,239 @@
/*
* Copyright (C) 2012 Technische Universität Dresden.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
namespace L4
{
class Io_register_block
{
public:
/**
* \brief Read register with a byte access.
*/
virtual unsigned char read8(unsigned long reg) const = 0;
/**
* \brief Read register with a 2 byte access.
*/
virtual unsigned short read16(unsigned long reg) const = 0;
/**
* \brief Read register with a 4 byte access.
*/
virtual unsigned int read32(unsigned long reg) const = 0;
/*
* \brief Read register with an 8 byte access.
*/
//virtual unsigned long long read64(unsigned long reg) const = 0;
/**
* \brief Write register with a byte access.
*/
virtual void write8(unsigned long reg, unsigned char value) const = 0;
/**
* \brief Write register with a 2 byte access.
*/
virtual void write16(unsigned long reg, unsigned short value) const = 0;
/**
* \brief Write register with a 4 byte access.
*/
virtual void write32(unsigned long reg, unsigned int value) const = 0;
/*
* \brief Write register with an 8 byte access.
*/
//virtual void write64(unsigned long reg, unsigned long long value) const = 0;
/**
* \brief Get address of a register.
*/
virtual unsigned long addr(unsigned long reg) const = 0;
/**
* \brief A delay.
*
* Note, most likely this function does nothing.
*/
virtual void delay() const = 0;
virtual ~Io_register_block() = 0;
/**
* \brief Read register.
* \param reg Register to use.
* \return Value in the register
*
* The access width is defined by the return type.
*/
template< typename R >
R read(unsigned long reg) const
{
static_assert(sizeof(R) == 1 || sizeof(R) == 2 || sizeof(R) == 4,
"Invalid size");
switch (sizeof(R))
{
case 1: return read8(reg);
case 2: return read16(reg);
case 4: return read32(reg);
};
}
/**
* \brief Write register.
* \param reg Register to use.
* \param value Date to write to the register.
*
* The access width is defined by the type of the value
*/
template< typename R >
void write(unsigned long reg, R value) const
{
static_assert(sizeof(R) == 1 || sizeof(R) == 2 || sizeof(R) == 4,
"Invalid size");
switch (sizeof(R))
{
case 1: write8(reg, value); return;
case 2: write16(reg, value); return;
case 4: write32(reg, value); return;
};
}
/**
* \brief Modify register.
* \param reg Register to use.
* \param clear_bits Bits to clear in the register.
* \param set_bits Bits to set in the register.
* \return The written value.
*
* This function is a short form of
* write(reg, (read(reg) & ~clear_bits) | add_bits)
*/
template< typename R >
R modify(unsigned long reg, R clear_bits, R set_bits) const
{
R r = (read<R>(reg) & ~clear_bits) | set_bits;
write(reg, r);
return r;
}
/**
* \brief Set bits in register.
* \param reg Register to use.
* \param set_bits Bits to set in the registers.
* \return The written value.
*/
template< typename R >
R set(unsigned long reg, R set_bits) const
{
return modify<R>(reg, 0, set_bits);
}
/**
* \brief Clear bits in register.
* \param reg Register to use.
* \param clear_bits Bits to clear in the register.
* \return The written value.
*/
template< typename R >
R clear(unsigned long reg, R clear_bits) const
{
return modify<R>(reg, clear_bits, 0);
}
};
inline Io_register_block::~Io_register_block() {}
class Io_register_block_mmio : public Io_register_block
{
private:
template< typename R >
R _read(unsigned long reg) const
{ return *reinterpret_cast<volatile R *>(_base + (reg << _shift)); }
template< typename R >
void _write(unsigned long reg, R val) const
{ *reinterpret_cast<volatile R *>(_base + (reg << _shift)) = val; }
public:
Io_register_block_mmio(unsigned long base, unsigned char shift = 0)
: _base(base), _shift(shift)
{}
unsigned long addr(unsigned long reg) const override
{ return _base + (reg << _shift); }
unsigned char read8(unsigned long reg) const override
{ return _read<unsigned char>(reg); }
unsigned short read16(unsigned long reg) const override
{ return _read<unsigned short>(reg); }
unsigned int read32(unsigned long reg) const override
{ return _read<unsigned int>(reg); }
void write8(unsigned long reg, unsigned char val) const override
{ _write(reg, val); }
void write16(unsigned long reg, unsigned short val) const override
{ _write(reg, val); }
void write32(unsigned long reg, unsigned int val) const override
{ _write(reg, val); }
void delay() const override
{}
private:
unsigned long _base;
unsigned char _shift;
};
template<typename ACCESS_TYPE>
class Io_register_block_mmio_fixed_width : public Io_register_block
{
private:
template< typename R >
R _read(unsigned long reg) const
{ return *reinterpret_cast<volatile ACCESS_TYPE *>(_base + (reg << _shift)); }
template< typename R >
void _write(unsigned long reg, R val) const
{ *reinterpret_cast<volatile ACCESS_TYPE *>(_base + (reg << _shift)) = val; }
public:
Io_register_block_mmio_fixed_width(unsigned long base, unsigned char shift = 0)
: _base(base), _shift(shift)
{}
unsigned long addr(unsigned long reg) const
{ return _base + (reg << _shift); }
unsigned char read8(unsigned long reg) const override
{ return _read<unsigned char>(reg); }
unsigned short read16(unsigned long reg) const override
{ return _read<unsigned short>(reg); }
unsigned int read32(unsigned long reg) const override
{ return _read<unsigned int>(reg); }
void write8(unsigned long reg, unsigned char val) const override
{ _write(reg, val); }
void write16(unsigned long reg, unsigned short val) const override
{ _write(reg, val); }
void write32(unsigned long reg, unsigned int val) const override
{ _write(reg, val); }
void delay() const override
{}
private:
unsigned long _base;
unsigned char _shift;
};
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2012 Technische Universität Dresden.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "io_regblock.h"
namespace L4
{
class Io_register_block_port : public Io_register_block
{
public:
Io_register_block_port(unsigned long base)
: _base(base)
{}
unsigned long addr(unsigned long reg) const override { return _base + reg; }
unsigned char read8(unsigned long reg) const override
{
unsigned char val;
asm volatile("inb %w1, %b0" : "=a" (val) : "Nd" (_base + reg));
return val;
}
unsigned short read16(unsigned long reg) const override
{
unsigned short val;
asm volatile("inw %w1, %w0" : "=a" (val) : "Nd" (_base + reg));
return val;
}
unsigned int read32(unsigned long reg) const override
{
unsigned int val;
asm volatile("in %w1, %0" : "=a" (val) : "Nd" (_base + reg));
return val;
}
void write8(unsigned long reg, unsigned char val) const override
{ asm volatile("outb %b0, %w1" : : "a" (val), "Nd" (_base + reg)); }
void write16(unsigned long reg, unsigned short val) const override
{ asm volatile("outw %w0, %w1" : : "a" (val), "Nd" (_base + reg)); }
void write32(unsigned long reg, unsigned int val) const override
{ asm volatile("out %0, %w1" : : "a" (val), "Nd" (_base + reg)); }
void delay() const override
{ asm volatile ("outb %al,$0x80"); }
private:
unsigned long _base;
};
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2012 Technische Universität Dresden.
* Author(s): Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
namespace L4 {
/**
* Evaluate an expression for a maximum number of times.
*
* A typical use case is testing for a bit change in a hardware register for a
* maximum number of times (polling). For example:
*
* \code{c++}
* Mmio_register_block regs;
* Poll_timeout_counter i(3000000);
* while (i.test(!(regs.read<l4_uint32_t>(0x04) & 1)))
* ;
* \endcode
*
* The following usage is \b wrong:
* \code{c++}
* ...
* Poll_timeout_counter i(3000000);
* while (!i.test((regs.read<l4_uint32_t>(0x04) & 1)))
* ;
* \endcode
*
* This loop would never terminate if the hardware register doesn't change!
*/
class Poll_timeout_counter
{
public:
/**
* Constructor.
*
* \param counter_val Maximum number of times to repeat the test.
*/
Poll_timeout_counter(unsigned counter_val)
{
set(counter_val);
}
/**
* Set the counter to a certain value.
*
* \param counter_val New counter value for maximum number of times to
* repeat the test.
*/
void set(unsigned counter_val)
{
_c = counter_val;
}
/**
* Evaluate the expression for a maximum number of times.
*/
bool test(bool expression = true)
{
if (!expression)
return false;
if (_c)
{
--_c;
return true;
}
return false;
}
/**
* Indicator if the maximum number of tests was required.
*
* \retval true, if the maximum number of tests was required or if the
* counter was initialized to zero.
*/
bool timed_out() const { return _c == 0; }
private:
unsigned _c;
};
}