1// vi:set ft=cpp: -*- Mode: C++ -*-
3 * Copyright (C) 2014-2021, 2023-2024 Kernkonzept GmbH.
4 * Author(s): Alexander Warg <alexander.warg@kernkonzept.com>
6 * License: see LICENSE.spdx (in this directory or the directories above)
10#include <l4/sys/types.h>
11#include <l4/cxx/type_traits>
18 * \class Register_block
19 * \details Example usage:
24 * // create a register block reference for max. 16bit accesses, using a
25 * // MMIO register block implementation (at address 0x1000).
26 * Hw::Register_block<16> regs = new Hw::Mmio_register_block<16>(0x1000);
28 * // Alternatively it is allowed to use an implementation that allows
29 * // wider access than actually needed.
30 * Hw::Register_block<16> regs = new Hw::Mmio_register_block<32>(0x1000);
32 * // read a 16bit register at offset 8byte
33 * unsigned short x = regs.r<16>(8);
34 * unsigned short x1 = regs[8]; // alternative
36 * // read an 8bit register at offset 0byte
37 * unsigned v = regs.r<8>(0);
39 * // do a 16bit write to register at offset 2byte (four variants)
43 * regs.r<16>().write(22);
45 * // do an 8bit write (two variants)
47 * regs.r<8>(0).write(9);
49 * // do 16bit read-modify-write (two variants)
50 * regs[4].modify(0xf, 3); // clear 4 lowest bits and set them to 3
51 * regs.r<16>(4).modify(0xf, 3);
53 * // do 8bit read-modify-write
54 * regs.r<8>(0).modify(0xf, 3);
56 * // fails to compile, because of too wide access
57 * // (32 bit access but regs is Hw::Register_block<16>)
58 * unsigned long v = regs.r<32>(4)
65 * \brief Abstract register block interface
66 * \tparam MAX_BITS The maximum access width for the registers.
68 * This interfaces is based on virtual do_read_<xx> and do_write_<xx>
69 * methods that have to be implemented up to the maximum access width.
71template< unsigned MAX_BITS = 32 >
72struct Register_block_base;
75struct Register_block_base<8>
77 virtual l4_uint8_t do_read_8(l4_addr_t reg) const = 0;
78 virtual void do_write_8(l4_uint8_t value, l4_addr_t reg) = 0;
79 virtual ~Register_block_base() = 0;
82inline Register_block_base<8>::~Register_block_base() {}
85struct Register_block_base<16> : Register_block_base<8>
87 virtual l4_uint16_t do_read_16(l4_addr_t reg) const = 0;
88 virtual void do_write_16(l4_uint16_t value, l4_addr_t reg) = 0;
92struct Register_block_base<32> : Register_block_base<16>
94 virtual l4_uint32_t do_read_32(l4_addr_t reg) const = 0;
95 virtual void do_write_32(l4_uint32_t value, l4_addr_t reg) = 0;
99struct Register_block_base<64> : Register_block_base<32>
101 virtual l4_uint64_t do_read_64(l4_addr_t reg) const = 0;
102 virtual void do_write_64(l4_uint64_t value, l4_addr_t reg) = 0;
104#undef REGBLK_READ_TEMPLATE
105#undef REGBLK_WRITE_TEMPLATE
107template<typename CHILD>
108struct Register_block_modify_mixin
110 template< typename T >
111 T modify(T clear_bits, T set_bits, l4_addr_t reg) const
113 CHILD const *c = static_cast<CHILD const *>(this);
114 T r = (c->template read<T>(reg) & ~clear_bits) | set_bits;
115 c->template write<T>(r, reg);
119 template< typename T >
120 T set(T set_bits, l4_addr_t reg) const
121 { return this->template modify<T>(T(0), set_bits, reg); }
123 template< typename T >
124 T clear(T clear_bits, l4_addr_t reg) const
125 { return this->template modify<T>(clear_bits, T(0), reg); }
129#define REGBLK_READ_TEMPLATE(sz) \
130 template< typename T > \
131 typename cxx::enable_if<sizeof(T) == (sz / 8), T>::type read(l4_addr_t reg) const \
133 union X { T t; l4_uint##sz##_t v; } m; \
134 m.v = _b->do_read_##sz (reg); \
138#define REGBLK_WRITE_TEMPLATE(sz) \
139 template< typename T > \
140 void write(T value, l4_addr_t reg, typename cxx::enable_if<sizeof(T) == (sz / 8), T>::type = T()) const \
142 union X { T t; l4_uint##sz##_t v; } m; \
144 _b->do_write_##sz(m.v, reg); \
148 * \brief Helper template that translates to the Register_block_base
150 * \tparam BLOCK The type of the Register_block_base interface to use.
152 * This helper translates read<T>(), write<T>(), set<T>(), clear<T>(),
153 * and modify<T>() calls to BLOCK::do_read_<xx> and BLOCK::do_write_<xx>.
155template< typename BLOCK >
156class Register_block_tmpl
157: public Register_block_modify_mixin<Register_block_tmpl<BLOCK> >
163 Register_block_tmpl(BLOCK *blk) : _b(blk) {}
164 Register_block_tmpl() = default;
166 operator BLOCK * () const { return _b; }
168 REGBLK_READ_TEMPLATE(8)
169 REGBLK_WRITE_TEMPLATE(8)
170 REGBLK_READ_TEMPLATE(16)
171 REGBLK_WRITE_TEMPLATE(16)
172 REGBLK_READ_TEMPLATE(32)
173 REGBLK_WRITE_TEMPLATE(32)
174 REGBLK_READ_TEMPLATE(64)
175 REGBLK_WRITE_TEMPLATE(64)
179#undef REGBLK_READ_TEMPLATE
180#undef REGBLK_WRITE_TEMPLATE
182namespace __Type_helper {
183 template<unsigned> struct Unsigned;
184 template<> struct Unsigned<8> { typedef l4_uint8_t type; };
185 template<> struct Unsigned<16> { typedef l4_uint16_t type; };
186 template<> struct Unsigned<32> { typedef l4_uint32_t type; };
187 template<> struct Unsigned<64> { typedef l4_uint64_t type; };
192 * \brief Single read only register inside a Register_block_base interface.
193 * \tparam BITS The access with of the register in bits.
194 * \tparam BLOCK The type for the Register_block_base interface.
195 * \note Objects of this type must be used only in temporary contexts
196 * not in global, class, or object scope.
198 * Allows simple read only access to a hardware register.
200template< unsigned BITS, typename BLOCK >
201class Ro_register_tmpl
208 typedef typename __Type_helper::Unsigned<BITS>::type value_type;
210 Ro_register_tmpl(BLOCK const &blk, unsigned offset) : _b(blk), _o(offset) {}
211 Ro_register_tmpl() = default;
214 * \brief read the value from the hardware register.
215 * \return value read from the hardware register.
217 operator value_type () const
218 { return _b.template read<value_type>(_o); }
221 * \brief read the value from the hardware register.
222 * \return value from the hardware register.
224 value_type read() const
225 { return _b.template read<value_type>(_o); }
230 * \brief Single hardware register inside a Register_block_base interface.
231 * \tparam BITS The access width for the register in bits.
232 * \tparam BLOCK the type of the Register_block_base interface.
233 * \note Objects of this type must be used only in temporary contexts
234 * not in global, class, or object scope.
236template< unsigned BITS, typename BLOCK >
237class Register_tmpl : public Ro_register_tmpl<BITS, BLOCK>
240 typedef typename Ro_register_tmpl<BITS, BLOCK>::value_type value_type;
242 Register_tmpl(BLOCK const &blk, unsigned offset)
243 : Ro_register_tmpl<BITS, BLOCK>(blk, offset)
246 Register_tmpl() = default;
249 * \brief write \a val into the hardware register.
250 * \param val the value to write into the hardware register.
252 Register_tmpl &operator = (value_type val)
253 { this->_b.template write<value_type>(val, this->_o); return *this; }
256 * \brief write \a val into the hardware register.
257 * \param val the value to write into the hardware register.
259 void write(value_type val)
260 { this->_b.template write<value_type>(val, this->_o); }
263 * \brief set bits in \a set_bits in the hardware register.
264 * \param set_bits bits to be set within the hardware register.
266 * This is a read-modify-write function that does a logical or
267 * of the old value from the register with \a set_bits.
270 * unsigned old_value = read();
271 * write(old_value | set_bits);
274 value_type set(value_type set_bits)
275 { return this->_b.template set<value_type>(set_bits, this->_o); }
278 * \brief clears bits in \a clear_bits in the hardware register.
279 * \param clear_bits bits to be cleared within the hardware register.
281 * This is a read-modify-write function that does a logical and
282 * of the old value from the register with the negated value of
286 * unsigned old_value = read();
287 * write(old_value & ~clear_bits);
290 value_type clear(value_type clear_bits)
291 { return this->_b.template clear<value_type>(clear_bits, this->_o); }
294 * \brief clears bits in \a clear_bits and sets bits in \a set_bits
295 * in the hardware register.
296 * \param clear_bits bits to be cleared within the hardware register.
297 * \param set_bits bits to set in the hardware register.
299 * This is a read-modify-write function that first does a logical and
300 * of the old value from the register with the negated value of
301 * \a clear_bits and then does a logical or with \a set_bits.
304 * unsigned old_value = read();
305 * write((old_value & ~clear_bits) | set_bits);
308 value_type modify(value_type clear_bits, value_type set_bits)
309 { return this->_b.template modify<value_type>(clear_bits, set_bits, this->_o); }
314 * \brief Handles a reference to a register block of the given
315 * maximum access width.
316 * \tparam MAX_BITS Maximum access width for the registers in this
318 * \tparam BLOCK Type implementing the register accesses (`read<>()`,
319 * `write<>()`, `modify<>()`, `set<>()`, and `clear<>()`).
321 * Provides access to registers in this block via r<WIDTH>() and
326 typename BLOCK = Register_block_tmpl<
327 Register_block_base<MAX_BITS>
333 template< unsigned B, typename BLK > friend class Register_block;
334 template< unsigned B, typename BLK > friend class Ro_register_block;
339 Register_block() = default;
340 Register_block(Block const &blk) : _b(blk) {}
341 Register_block &operator = (Block const &blk)
342 { _b = blk; return *this; }
344 template< unsigned BITS >
345 Register_block(Register_block<BITS> blk) : _b(blk._b) {}
347 typedef Register_tmpl<MAX_BITS, Block> Register;
348 typedef Ro_register_tmpl<MAX_BITS, Block> Ro_register;
351 * \brief Read only access to register at offset \a offset.
352 * \tparam BITS the access width in bits for the register.
353 * \param offset The offset of the register within the register file.
354 * \return register object allowing read only access with width \a BITS.
356 template< unsigned BITS >
357 Ro_register_tmpl<BITS, Block> r(unsigned offset) const
358 { return Ro_register_tmpl<BITS, Block>(this->_b, offset); }
361 * \brief Read only access to register at offset \a offset.
362 * \param offset The offset of the register within the register file.
363 * \return register object allowing read only access with width \a MAX_BITS.
365 Ro_register operator [] (unsigned offset) const
366 { return this->r<MAX_BITS>(offset); }
370 * \brief Read/write access to register at offset \a offset.
371 * \tparam BITS the access width in bits for the register.
372 * \param offset The offset of the register within the register file.
373 * \return register object allowing read and write access with width \a BITS.
375 template< unsigned BITS >
376 Register_tmpl<BITS, Block> r(unsigned offset)
377 { return Register_tmpl<BITS, Block>(this->_b, offset); }
380 * \brief Read/write access to register at offset \a offset.
381 * \param offset The offset of the register within the register file.
382 * \return register object allowing read and write access with
385 Register operator [] (unsigned offset)
386 { return this->r<MAX_BITS>(offset); }
390 * \brief Handles a reference to a read only register block of the given
391 * maximum access width.
392 * \tparam MAX_BITS Maximum access width for the registers in this block.
393 * \tparam BLOCK Type implementing the register accesses (read<>()),
395 * Provides read only access to registers in this block via r<WIDTH>()
400 typename BLOCK = Register_block_tmpl<
401 Register_block_base<MAX_BITS> const
404class Ro_register_block
407 template< unsigned B, typename BLK > friend class Ro_register_block;
412 Ro_register_block() = default;
413 Ro_register_block(BLOCK const &blk) : _b(blk) {}
415 template< unsigned BITS >
416 Ro_register_block(Register_block<BITS> const &blk) : _b(blk._b) {}
418 typedef Ro_register_tmpl<MAX_BITS, Block> Ro_register;
419 typedef Ro_register Register;
422 * \brief Read only access to register at offset \a offset.
423 * \param offset The offset of the register within the register file.
424 * \return register object allowing read only access with width \a MAX_BITS.
426 Ro_register operator [] (unsigned offset) const
427 { return Ro_register(this->_b, offset); }
430 * \brief Read only access to register at offset \a offset.
431 * \tparam BITS the access width in bits for the register.
432 * \param offset The offset of the register within the register file.
433 * \return register object allowing read only access with width \a BITS.
435 template< unsigned BITS >
436 Ro_register_tmpl<BITS, Block> r(unsigned offset) const
437 { return Ro_register_tmpl<BITS, Block>(this->_b, offset); }
442 * \brief Implementation helper for register blocks.
443 * \param BASE The class implementing read<> and write<> template functions
444 * for accessing the registers. This class must inherit from
445 * Register_block_impl.
446 * \param MAX_BITS The maximum access width for the register file.
447 * Supported values are 8, 16, 32, or 64.
450 * This template allows easy implementation of register files by providing
451 * read<> and write<> template functions, see Mmio_register_block
454template< typename BASE, unsigned MAX_BITS = 32 >
455struct Register_block_impl;
457#define REGBLK_IMPL_RW_TEMPLATE(sz, ...) \
458 l4_uint##sz##_t do_read_##sz(l4_addr_t reg) const override \
459 { return static_cast<BASE const *>(this)->template read<l4_uint##sz##_t>(reg); } \
461 void do_write_##sz(l4_uint##sz##_t value, l4_addr_t reg) override \
462 { static_cast<BASE*>(this)->template write<l4_uint##sz##_t>(value, reg); }
465template< typename BASE >
466struct Register_block_impl<BASE, 8> : public Register_block_base<8>
468 REGBLK_IMPL_RW_TEMPLATE(8);
471template< typename BASE >
472struct Register_block_impl<BASE, 16> : public Register_block_base<16>
474 REGBLK_IMPL_RW_TEMPLATE(8);
475 REGBLK_IMPL_RW_TEMPLATE(16);
478template< typename BASE >
479struct Register_block_impl<BASE, 32> : public Register_block_base<32>
481 REGBLK_IMPL_RW_TEMPLATE(8);
482 REGBLK_IMPL_RW_TEMPLATE(16);
483 REGBLK_IMPL_RW_TEMPLATE(32);
486template< typename BASE >
487struct Register_block_impl<BASE, 64> : public Register_block_base<64>
489 REGBLK_IMPL_RW_TEMPLATE(8);
490 REGBLK_IMPL_RW_TEMPLATE(16);
491 REGBLK_IMPL_RW_TEMPLATE(32);
492 REGBLK_IMPL_RW_TEMPLATE(64);
495#undef REGBLK_IMPL_RW_TEMPLATE