Landfall

Annotated pkg/devices/lib/common/include/hw_register_block.h

0:89a1bc19c1fc
2018-05-13 Paul Boddie Added device libraries and programs, configuration files and examples. Also added an installation script and copyright and licensing information.
paul@0 1
/*
paul@0 2
 * (c) 2014 Alexander Warg <alexander.warg@kernkonzept.com>
paul@0 3
 *
paul@0 4
 * This file is part of TUD:OS and distributed under the terms of the
paul@0 5
 * GNU General Public License 2.
paul@0 6
 * Please see the COPYING-GPL-2 file for details.
paul@0 7
 */
paul@0 8
#pragma once
paul@0 9
paul@0 10
#include <l4/sys/types.h>
paul@0 11
#include <l4/cxx/type_traits>
paul@0 12
paul@0 13
namespace Hw {
paul@0 14
paul@0 15
paul@0 16
/* EXAMPLE usage:
paul@0 17
paul@0 18
\code
paul@0 19
paul@0 20
void test()
paul@0 21
{
paul@0 22
  // create a register block reference for max. 16bit accesses, using a
paul@0 23
  // MMIO register block implementation (at address 0x1000).
paul@0 24
  Hw::Register_block<16> regs = new Hw::Mmio_register_block<16>(0x1000);
paul@0 25
paul@0 26
  // Alternatively it is allowed to use an implementation that allows
paul@0 27
  // wider access than actually needed.
paul@0 28
  Hw::Register_block<16> regs = new Hw::Mmio_register_block<32>(0x1000);
paul@0 29
paul@0 30
  // read a 16bit register at offset 8byte
paul@0 31
  unsigned short x = regs.r<16>(8);
paul@0 32
  unsigned short x1 = regs[8];      // alternative
paul@0 33
paul@0 34
  // read an 8bit register at offset 0byte
paul@0 35
  unsigned v = regs.r<8>(0);
paul@0 36
paul@0 37
  // do a 16bit write to register at offset 2byte (four variants)
paul@0 38
  regs[2] = 22;
paul@0 39
  regs.r<16>(2) = 22;
paul@0 40
  regs[2].write(22);
paul@0 41
  regs.r<16>().write(22);
paul@0 42
paul@0 43
  // do an 8bit write (two variants)
paul@0 44
  regs.r<8>(0) = 9;
paul@0 45
  regs.r<8>(0).write(9);
paul@0 46
paul@0 47
  // do 16bit read-modify-write (two variants)
paul@0 48
  regs[4].modify(0xf, 3); // clear 4 lowest bits and set them to 3
paul@0 49
  regs.r<16>(4).modify(0xf, 3);
paul@0 50
paul@0 51
  // do 8bit read-modify-write
paul@0 52
  regs.r<8>(0).modify(0xf, 3);
paul@0 53
paul@0 54
  // fails to compile, because of too wide access
paul@0 55
  // (32 bit access but regs is Hw::Register_block<16>)
paul@0 56
  unsigned long v = regs.r<32>(4)
paul@0 57
}
paul@0 58
paul@0 59
\endcode
paul@0 60
*/
paul@0 61
paul@0 62
paul@0 63
/**
paul@0 64
 * \brief Abstract register block interface
paul@0 65
 * \tparam MAX_BITS The maximum access width for the registers.
paul@0 66
 *
paul@0 67
 * This interfaces is based on virtual do_read_<xx> and do_write_<xx>
paul@0 68
 * methods that have to be implemented up to the maximum access width.
paul@0 69
 */
paul@0 70
template< unsigned MAX_BITS = 32 >
paul@0 71
struct Register_block_base;
paul@0 72
paul@0 73
template<>
paul@0 74
struct Register_block_base<8>
paul@0 75
{
paul@0 76
  virtual l4_uint8_t do_read_8(l4_addr_t reg) const = 0;
paul@0 77
  virtual void do_write_8(l4_uint8_t value, l4_addr_t reg) = 0;
paul@0 78
  virtual ~Register_block_base() = 0;
paul@0 79
};
paul@0 80
paul@0 81
inline Register_block_base<8>::~Register_block_base() {}
paul@0 82
paul@0 83
template<>
paul@0 84
struct Register_block_base<16> : Register_block_base<8>
paul@0 85
{
paul@0 86
  virtual l4_uint16_t do_read_16(l4_addr_t reg) const = 0;
paul@0 87
  virtual void do_write_16(l4_uint16_t value, l4_addr_t reg) = 0;
paul@0 88
};
paul@0 89
paul@0 90
template<>
paul@0 91
struct Register_block_base<32> : Register_block_base<16>
paul@0 92
{
paul@0 93
  virtual l4_uint32_t do_read_32(l4_addr_t reg) const = 0;
paul@0 94
  virtual void do_write_32(l4_uint32_t value, l4_addr_t reg) = 0;
paul@0 95
};
paul@0 96
paul@0 97
template<>
paul@0 98
struct Register_block_base<64> : Register_block_base<32>
paul@0 99
{
paul@0 100
  virtual l4_uint64_t do_read_64(l4_addr_t reg) const = 0;
paul@0 101
  virtual void do_write_64(l4_uint64_t value, l4_addr_t reg) = 0;
paul@0 102
};
paul@0 103
#undef REGBLK_READ_TEMPLATE
paul@0 104
#undef REGBLK_WRITE_TEMPLATE
paul@0 105
paul@0 106
template<typename CHILD>
paul@0 107
struct Register_block_modify_mixin
paul@0 108
{
paul@0 109
  template< typename T >
paul@0 110
  T modify(T clear_bits, T set_bits, l4_addr_t reg) const
paul@0 111
  {
paul@0 112
    CHILD const *c = static_cast<CHILD const *>(this);
paul@0 113
    T r = (c->template read<T>(reg) & ~clear_bits) | set_bits;
paul@0 114
    c->template write<T>(r, reg);
paul@0 115
    return r;
paul@0 116
  }
paul@0 117
paul@0 118
  template< typename T >
paul@0 119
  T set(T set_bits, l4_addr_t reg) const
paul@0 120
  { return this->template modify<T>(T(0), set_bits, reg); }
paul@0 121
paul@0 122
  template< typename T >
paul@0 123
  T clear(T clear_bits, l4_addr_t reg) const
paul@0 124
  { return this->template modify<T>(clear_bits, T(0), reg); }
paul@0 125
};
paul@0 126
paul@0 127
paul@0 128
#define REGBLK_READ_TEMPLATE(sz) \
paul@0 129
  template< typename T >          \
paul@0 130
  typename cxx::enable_if<sizeof(T) == (sz / 8), T>::type read(l4_addr_t reg) const \
paul@0 131
  { \
paul@0 132
    union X { T t; l4_uint##sz##_t v; } m; \
paul@0 133
    m.v = _b->do_read_##sz (reg); \
paul@0 134
    return m.t; \
paul@0 135
  }
paul@0 136
paul@0 137
#define REGBLK_WRITE_TEMPLATE(sz) \
paul@0 138
  template< typename T >          \
paul@0 139
  void write(T value, l4_addr_t reg, typename cxx::enable_if<sizeof(T) == (sz / 8), T>::type = T()) const \
paul@0 140
  { \
paul@0 141
    union X { T t; l4_uint##sz##_t v; } m; \
paul@0 142
    m.t = value; \
paul@0 143
    _b->do_write_##sz(m.v, reg); \
paul@0 144
  }
paul@0 145
paul@0 146
/**
paul@0 147
 * \brief Helper template that translates to the Register_block_base
paul@0 148
 *        interface.
paul@0 149
 * \tparam BLOCK The type of the Register_block_base interface to use.
paul@0 150
 *
paul@0 151
 * This helper translates read<T>(), write<T>(), set<T>(), clear<T>(),
paul@0 152
 * and modify<T>() calls to BLOCK::do_read_<xx> and BLOCK::do_write_<xx>.
paul@0 153
 */
paul@0 154
template< typename BLOCK >
paul@0 155
class Register_block_tmpl
paul@0 156
: public Register_block_modify_mixin<Register_block_tmpl<BLOCK> >
paul@0 157
{
paul@0 158
private:
paul@0 159
  BLOCK *_b;
paul@0 160
paul@0 161
public:
paul@0 162
  Register_block_tmpl(BLOCK *blk) : _b(blk) {}
paul@0 163
  Register_block_tmpl() = default;
paul@0 164
paul@0 165
  operator BLOCK * () const { return _b; }
paul@0 166
paul@0 167
  REGBLK_READ_TEMPLATE(8)
paul@0 168
  REGBLK_WRITE_TEMPLATE(8)
paul@0 169
  REGBLK_READ_TEMPLATE(16)
paul@0 170
  REGBLK_WRITE_TEMPLATE(16)
paul@0 171
  REGBLK_READ_TEMPLATE(32)
paul@0 172
  REGBLK_WRITE_TEMPLATE(32)
paul@0 173
  REGBLK_READ_TEMPLATE(64)
paul@0 174
  REGBLK_WRITE_TEMPLATE(64)
paul@0 175
};
paul@0 176
paul@0 177
paul@0 178
#undef REGBLK_READ_TEMPLATE
paul@0 179
#undef REGBLK_WRITE_TEMPLATE
paul@0 180
paul@0 181
namespace __Type_helper {
paul@0 182
  template<unsigned> struct Unsigned;
paul@0 183
  template<> struct Unsigned<8> { typedef l4_uint8_t type; };
paul@0 184
  template<> struct Unsigned<16> { typedef l4_uint16_t type; };
paul@0 185
  template<> struct Unsigned<32> { typedef l4_uint32_t type; };
paul@0 186
  template<> struct Unsigned<64> { typedef l4_uint64_t type; };
paul@0 187
};
paul@0 188
paul@0 189
paul@0 190
/**
paul@0 191
 * \brief Single read only register inside a Register_block_base interface.
paul@0 192
 * \tparam BITS The access with of the register in bits.
paul@0 193
 * \tparam BLOCK The type for the Register_block_base interface.
paul@0 194
 * \note Objects of this type must be used only in temporary contexts
paul@0 195
 *       not in global, class, or object scope.
paul@0 196
 *
paul@0 197
 * Allows simple read only access to a hardware register.
paul@0 198
 */
paul@0 199
template< unsigned BITS, typename BLOCK >
paul@0 200
class Ro_register_tmpl
paul@0 201
{
paul@0 202
protected:
paul@0 203
  BLOCK _b;
paul@0 204
  unsigned _o;
paul@0 205
paul@0 206
public:
paul@0 207
  typedef typename __Type_helper::Unsigned<BITS>::type value_type;
paul@0 208
paul@0 209
  Ro_register_tmpl(BLOCK const &blk, unsigned offset) : _b(blk), _o(offset) {}
paul@0 210
  Ro_register_tmpl() = default;
paul@0 211
paul@0 212
  /**
paul@0 213
   * \brief read the value from the hardware register.
paul@0 214
   * \return value read from the hardware register.
paul@0 215
   */
paul@0 216
  operator value_type () const
paul@0 217
  { return _b.template read<value_type>(_o); }
paul@0 218
paul@0 219
  /**
paul@0 220
   * \brief read the value from the hardware register.
paul@0 221
   * \return value from the hardware register.
paul@0 222
   */
paul@0 223
  value_type read() const
paul@0 224
  { return _b.template read<value_type>(_o); }
paul@0 225
};
paul@0 226
paul@0 227
paul@0 228
/**
paul@0 229
 * \brief Single hardware register inside a Register_block_base interface.
paul@0 230
 * \tparam BITS The access width for the register in bits.
paul@0 231
 * \tparam BLOCK the type of the Register_block_base interface.
paul@0 232
 * \note Objects of this type msut be used only in temporary contexts
paul@0 233
 *       not in global, class, or object scope.
paul@0 234
 */
paul@0 235
template< unsigned BITS, typename BLOCK >
paul@0 236
class Register_tmpl : public Ro_register_tmpl<BITS, BLOCK>
paul@0 237
{
paul@0 238
public:
paul@0 239
  typedef typename Ro_register_tmpl<BITS, BLOCK>::value_type value_type;
paul@0 240
paul@0 241
  Register_tmpl(BLOCK const &blk, unsigned offset)
paul@0 242
  : Ro_register_tmpl<BITS, BLOCK>(blk, offset)
paul@0 243
  {}
paul@0 244
paul@0 245
  Register_tmpl() = default;
paul@0 246
paul@0 247
  /**
paul@0 248
   * \brief write \a val into the hardware register.
paul@0 249
   * \param val the value to write into the hardware register.
paul@0 250
   */
paul@0 251
  Register_tmpl &operator = (value_type val)
paul@0 252
  { this->_b.template write<value_type>(val, this->_o); return *this; }
paul@0 253
paul@0 254
  /**
paul@0 255
   * \brief write \a val into the hardware register.
paul@0 256
   * \param val the value to write into the hardware register.
paul@0 257
   */
paul@0 258
  void write(value_type val)
paul@0 259
  { this->_b.template write<value_type>(val, this->_o); }
paul@0 260
paul@0 261
  /**
paul@0 262
   * \brief set bits in \a set_bits in the hardware register.
paul@0 263
   * \param set_bits bits to be set within the hardware register.
paul@0 264
   *
paul@0 265
   * This is a read-modify-write function that does a logical or
paul@0 266
   * of the old value from the register with \a set_bits.
paul@0 267
   *
paul@0 268
   * \code
paul@0 269
   * unsigned old_value = read();
paul@0 270
   * write(old_value | set_bits);
paul@0 271
   * \endcode
paul@0 272
   */
paul@0 273
  value_type set(value_type set_bits)
paul@0 274
  { return this->_b.template set<value_type>(set_bits, this->_o); }
paul@0 275
paul@0 276
  /**
paul@0 277
   * \brief clears bits in \a clear_bits in the hardware register.
paul@0 278
   * \param clear_bits bits to be cleared within the hardware register.
paul@0 279
   *
paul@0 280
   * This is a read-modify-write function that does a logical and
paul@0 281
   * of the old value from the register with the negated value of
paul@0 282
   * \a clear_bits.
paul@0 283
   *
paul@0 284
   * \code
paul@0 285
   * unsigned old_value = read();
paul@0 286
   * write(old_value & ~clear_bits);
paul@0 287
   * \endcode
paul@0 288
   */
paul@0 289
  value_type clear(value_type clear_bits)
paul@0 290
  { return this->_b.template clear<value_type>(clear_bits, this->_o); }
paul@0 291
paul@0 292
  /**
paul@0 293
   * \brief clears bits in \a clear_bits and sets bits in \a set_bits
paul@0 294
   *        in the hardware register.
paul@0 295
   * \param clear_bits bits to be cleared within the hardware register.
paul@0 296
   * \param set_bits bits to set in the hardware register.
paul@0 297
   *
paul@0 298
   * This is a read-modify-write function that first does a logical and
paul@0 299
   * of the old value from the register with the negated value of
paul@0 300
   * \a clear_bits and then does a logical or with \a set_bits.
paul@0 301
   *
paul@0 302
   * \code{.c}
paul@0 303
   * unsigned old_value = read();
paul@0 304
   * write((old_value & ~clear_bits) | set_bits);
paul@0 305
   * \endcode
paul@0 306
   */
paul@0 307
  value_type modify(value_type clear_bits, value_type set_bits)
paul@0 308
  { return this->_b.template modify<value_type>(clear_bits, set_bits, this->_o); }
paul@0 309
};
paul@0 310
paul@0 311
paul@0 312
/**
paul@0 313
 * \brief Handles a reference to a register block of the given
paul@0 314
 *        maximum access width.
paul@0 315
 * \tparam MAX_BITS Maximum access width for the registers in this
paul@0 316
 *         block.
paul@0 317
 * \tparam BLOCK Type implementing the register accesses (read<>(), write<>(),
paul@0 318
 *                modify<>(), set<>(), and clear<>()).
paul@0 319
 *
paul@0 320
 * Provides access to registers in this block via r<WIDTH>() and
paul@0 321
 * operator[]().
paul@0 322
 */
paul@0 323
template<
paul@0 324
  unsigned MAX_BITS,
paul@0 325
  typename BLOCK = Register_block_tmpl<
paul@0 326
                     Register_block_base<MAX_BITS>
paul@0 327
                   >
paul@0 328
>
paul@0 329
class Register_block
paul@0 330
{
paul@0 331
private:
paul@0 332
  template< unsigned B, typename BLK > friend class Register_block;
paul@0 333
  template< unsigned B, typename BLK > friend class Ro_register_block;
paul@0 334
  typedef  BLOCK Block;
paul@0 335
  Block _b;
paul@0 336
paul@0 337
public:
paul@0 338
  Register_block() = default;
paul@0 339
  Register_block(Block const &blk) : _b(blk) {}
paul@0 340
  Register_block &operator = (Block const &blk)
paul@0 341
  { _b = blk; return *this; }
paul@0 342
paul@0 343
  template< unsigned BITS >
paul@0 344
  Register_block(Register_block<BITS> blk) : _b(blk._b) {}
paul@0 345
paul@0 346
  typedef Register_tmpl<MAX_BITS, Block> Register;
paul@0 347
  typedef Ro_register_tmpl<MAX_BITS, Block> Ro_register;
paul@0 348
paul@0 349
  /**
paul@0 350
   * \brief Read only access to register at offset \a offset.
paul@0 351
   * \tparam BITS the access width in bits for the register.
paul@0 352
   * \param offset The offset of the register within the register file.
paul@0 353
   * \return register object allowing read only access with width \a BITS.
paul@0 354
   */
paul@0 355
  template< unsigned BITS >
paul@0 356
  Ro_register_tmpl<BITS, Block> r(unsigned offset) const
paul@0 357
  { return Ro_register_tmpl<BITS, Block>(this->_b, offset); }
paul@0 358
paul@0 359
  /**
paul@0 360
   * \brief Read only access to register at offset \a offset.
paul@0 361
   * \param offset The offset of the register within the register file.
paul@0 362
   * \return register object allowing read only access with width \a MAX_BITS.
paul@0 363
   */
paul@0 364
  Ro_register operator [] (unsigned offset) const
paul@0 365
  { return this->r<MAX_BITS>(offset); }
paul@0 366
paul@0 367
paul@0 368
  /**
paul@0 369
   * \brief Read/write access to register at offset \a offset.
paul@0 370
   * \tparam BITS the access width in bits for the register.
paul@0 371
   * \param offset The offset of the register within the register file.
paul@0 372
   * \return register object allowing read and write access with width \a BITS.
paul@0 373
   */
paul@0 374
  template< unsigned BITS >
paul@0 375
  Register_tmpl<BITS, Block> r(unsigned offset)
paul@0 376
  { return Register_tmpl<BITS, Block>(this->_b, offset); }
paul@0 377
paul@0 378
  /**
paul@0 379
   * \brief Read/write access to register at offset \a offset.
paul@0 380
   * \param offset The offset of the register within the register file.
paul@0 381
   * \return register object allowing read and write access with
paul@0 382
   *         width \a MAX_BITS.
paul@0 383
   */
paul@0 384
  Register operator [] (unsigned offset)
paul@0 385
  { return this->r<MAX_BITS>(offset); }
paul@0 386
};
paul@0 387
paul@0 388
/**
paul@0 389
 * \brief Handles a reference to a read only register block of the given
paul@0 390
 *        maximum access width.
paul@0 391
 * \tparam MAX_BITS Maximum access width for the registers in this block.
paul@0 392
 * \tparam BLOCK Type implementing the register accesses (read<>()),
paul@0 393
 *
paul@0 394
 * Provides read only access to registers in this block via r<WIDTH>()
paul@0 395
 * and operator[]().
paul@0 396
 */
paul@0 397
template<
paul@0 398
  unsigned MAX_BITS,
paul@0 399
  typename BLOCK = Register_block_tmpl<
paul@0 400
                     Register_block_base<MAX_BITS> const
paul@0 401
                   >
paul@0 402
>
paul@0 403
class Ro_register_block
paul@0 404
{
paul@0 405
private:
paul@0 406
  template< unsigned B, typename BLK > friend class Ro_register_block;
paul@0 407
  typedef  BLOCK Block;
paul@0 408
  Block _b;
paul@0 409
paul@0 410
public:
paul@0 411
  Ro_register_block() = default;
paul@0 412
  Ro_register_block(BLOCK const &blk) : _b(blk) {}
paul@0 413
paul@0 414
  template< unsigned BITS >
paul@0 415
  Ro_register_block(Register_block<BITS> const &blk) : _b(blk._b) {}
paul@0 416
paul@0 417
  typedef Ro_register_tmpl<MAX_BITS, Block> Ro_register;
paul@0 418
  typedef Ro_register Register;
paul@0 419
paul@0 420
  /**
paul@0 421
   * \brief Read only access to register at offset \a offset.
paul@0 422
   * \param offset The offset of the register within the register file.
paul@0 423
   * \return register object allowing read only access with width \a MAX_BITS.
paul@0 424
   */
paul@0 425
  Ro_register operator [] (unsigned offset) const
paul@0 426
  { return Ro_register(this->_b, offset); }
paul@0 427
paul@0 428
  /**
paul@0 429
   * \brief Read only access to register at offset \a offset.
paul@0 430
   * \tparam BITS the access width in bits for the register.
paul@0 431
   * \param offset The offset of the register within the register file.
paul@0 432
   * \return register object allowing read only access with width \a BITS.
paul@0 433
   */
paul@0 434
  template< unsigned BITS >
paul@0 435
  Ro_register_tmpl<BITS, Block> r(unsigned offset) const
paul@0 436
  { return Ro_register_tmpl<BITS, Block>(this->_b, offset); }
paul@0 437
};
paul@0 438
paul@0 439
paul@0 440
/**
paul@0 441
 * \brief Implementation helper for register blocks.
paul@0 442
 * \param BASE The class implementing read<> and write<> template functions
paul@0 443
 *             for accessing the registers. This class must inherit from
paul@0 444
 *             Register_block_impl.
paul@0 445
 * \param MAX_BITS The maximum access width for the register file.
paul@0 446
 *                 Supported values are 8, 16, 32, or 64.
paul@0 447
 *
paul@0 448
 *
paul@0 449
 * This template allows easy implementation of register files by providing
paul@0 450
 * read<> and write<> template functions, see Mmio_register_block
paul@0 451
 * as an example.
paul@0 452
 */
paul@0 453
template< typename BASE, unsigned MAX_BITS = 32 >
paul@0 454
struct Register_block_impl;
paul@0 455
paul@0 456
#define REGBLK_IMPL_RW_TEMPLATE(sz, ...) \
paul@0 457
  l4_uint##sz##_t do_read_##sz(l4_addr_t reg) const \
paul@0 458
  { return static_cast<BASE const *>(this)->template read<l4_uint##sz##_t>(reg); } \
paul@0 459
  \
paul@0 460
  void do_write_##sz(l4_uint##sz##_t value, l4_addr_t reg) \
paul@0 461
  { return static_cast<BASE*>(this)->template write<l4_uint##sz##_t>(value, reg); }
paul@0 462
paul@0 463
paul@0 464
template< typename BASE >
paul@0 465
struct Register_block_impl<BASE, 8> : public Register_block_base<8>
paul@0 466
{
paul@0 467
  REGBLK_IMPL_RW_TEMPLATE(8);
paul@0 468
};
paul@0 469
paul@0 470
template< typename BASE >
paul@0 471
struct Register_block_impl<BASE, 16> : public Register_block_base<16>
paul@0 472
{
paul@0 473
  REGBLK_IMPL_RW_TEMPLATE(8);
paul@0 474
  REGBLK_IMPL_RW_TEMPLATE(16);
paul@0 475
};
paul@0 476
paul@0 477
template< typename BASE >
paul@0 478
struct Register_block_impl<BASE, 32> : public Register_block_base<32>
paul@0 479
{
paul@0 480
  REGBLK_IMPL_RW_TEMPLATE(8);
paul@0 481
  REGBLK_IMPL_RW_TEMPLATE(16);
paul@0 482
  REGBLK_IMPL_RW_TEMPLATE(32);
paul@0 483
};
paul@0 484
paul@0 485
template< typename BASE >
paul@0 486
struct Register_block_impl<BASE, 64> : public Register_block_base<64>
paul@0 487
{
paul@0 488
  REGBLK_IMPL_RW_TEMPLATE(8);
paul@0 489
  REGBLK_IMPL_RW_TEMPLATE(16);
paul@0 490
  REGBLK_IMPL_RW_TEMPLATE(32);
paul@0 491
  REGBLK_IMPL_RW_TEMPLATE(64);
paul@0 492
};
paul@0 493
paul@0 494
#undef REGBLK_IMPL_RW_TEMPLATE
paul@0 495
paul@0 496
/** \brief Dummy register block to be used as a placeholder */
paul@0 497
extern Register_block<64> dummy_register_block;
paul@0 498
paul@0 499
}