/* * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include namespace ams::reg { template concept UnsignedNonConstIntegral = std::unsigned_integral && !std::is_const::value; using BitsValue = std::tuple; using BitsMask = std::tuple; constexpr ALWAYS_INLINE u32 GetOffset(const BitsMask v) { return static_cast(std::get<0>(v)); } constexpr ALWAYS_INLINE u32 GetOffset(const BitsValue v) { return static_cast(std::get<0>(v)); } constexpr ALWAYS_INLINE u32 GetWidth(const BitsMask v) { return static_cast(std::get<1>(v)); } constexpr ALWAYS_INLINE u32 GetWidth(const BitsValue v) { return static_cast(std::get<1>(v)); } constexpr ALWAYS_INLINE u32 GetValue(const BitsValue v) { return static_cast(std::get<2>(v)); } constexpr ALWAYS_INLINE ::ams::reg::BitsValue GetValue(const BitsMask m, const u32 v) { return ::ams::reg::BitsValue{GetOffset(m), GetWidth(m), v}; } constexpr ALWAYS_INLINE u32 EncodeMask(const BitsMask v) { return (~0u >> (BITSIZEOF(u32) - GetWidth(v))) << GetOffset(v); } constexpr ALWAYS_INLINE u32 EncodeMask(const BitsValue v) { return (~0u >> (BITSIZEOF(u32) - GetWidth(v))) << GetOffset(v); } constexpr ALWAYS_INLINE u32 EncodeValue(const BitsValue v) { return ((GetValue(v) << GetOffset(v)) & EncodeMask(v)); } template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) constexpr ALWAYS_INLINE u32 Encode(const Values... values) { return (EncodeValue(values) | ...); } template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) constexpr ALWAYS_INLINE u32 EncodeMask(const Masks... masks) { return (EncodeMask(masks) | ...); } template requires UnsignedNonConstIntegral ALWAYS_INLINE void Write(volatile IntType *reg, std::type_identity_t val) { *reg = val; } template requires UnsignedNonConstIntegral ALWAYS_INLINE void Write(volatile IntType ®, std::type_identity_t val) { reg = val; } ALWAYS_INLINE void Write(uintptr_t reg, u32 val) { Write(reinterpret_cast(reg), val); } template requires UnsignedNonConstIntegral && ((sizeof...(Values) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE void Write(volatile IntType *reg, const Values... values) { return Write(reg, static_cast((EncodeValue(values) | ...))); } template requires UnsignedNonConstIntegral && ((sizeof...(Values) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE void Write(volatile IntType ®, const Values... values) { return Write(reg, static_cast((EncodeValue(values) | ...))); } template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE void Write(uintptr_t reg, const Values... values) { return Write(reg, (EncodeValue(values) | ...)); } template requires UnsignedNonConstIntegral ALWAYS_INLINE IntType Read(volatile IntType *reg) { return *reg; } template requires UnsignedNonConstIntegral ALWAYS_INLINE IntType Read(volatile IntType ®) { return reg; } ALWAYS_INLINE u32 Read(uintptr_t reg) { return Read(reinterpret_cast(reg)); } template requires UnsignedNonConstIntegral ALWAYS_INLINE IntType Read(volatile IntType *reg, std::type_identity_t mask) { return *reg & mask; } template requires UnsignedNonConstIntegral ALWAYS_INLINE IntType Read(volatile IntType ®, std::type_identity_t mask) { return reg & mask; } ALWAYS_INLINE u32 Read(uintptr_t reg, u32 mask) { return Read(reinterpret_cast(reg), mask); } template requires UnsignedNonConstIntegral && ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE IntType Read(volatile IntType *reg, const Masks... masks) { return Read(reg, static_cast((EncodeMask(masks) | ...))); } template requires UnsignedNonConstIntegral && ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE IntType Read(volatile IntType ®, const Masks... masks) { return Read(reg, static_cast((EncodeMask(masks) | ...))); } template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE u32 Read(uintptr_t reg, const Masks... masks) { return Read(reg, (EncodeMask(masks) | ...)); } template requires UnsignedNonConstIntegral && ((sizeof...(Values) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE bool HasValue(volatile IntType *reg, const Values... values) { return Read(reg, static_cast((EncodeMask(values) | ...))) == static_cast(Encode(values...)); } template requires UnsignedNonConstIntegral && ((sizeof...(Values) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE bool HasValue(volatile IntType ®, const Values... values) { return Read(reg, static_cast((EncodeMask(values) | ...))) == static_cast(Encode(values...)); } template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE bool HasValue(uintptr_t reg, const Values... values) { return Read(reg, (EncodeMask(values) | ...)) == Encode(values...); } template requires UnsignedNonConstIntegral ALWAYS_INLINE IntType GetValue(volatile IntType *reg, const BitsMask mask) { return Read(reg, mask) >> GetOffset(mask); } template requires UnsignedNonConstIntegral ALWAYS_INLINE IntType GetValue(volatile IntType ®, const BitsMask mask) { return Read(reg, mask) >> GetOffset(mask); } ALWAYS_INLINE u32 GetValue(uintptr_t reg, const BitsMask mask) { return Read(reg, mask) >> GetOffset(mask); } template requires UnsignedNonConstIntegral ALWAYS_INLINE void ReadWrite(volatile IntType *reg, std::type_identity_t val, std::type_identity_t mask) { *reg = (*reg & (~mask)) | (val & mask); } template requires UnsignedNonConstIntegral ALWAYS_INLINE void ReadWrite(volatile IntType ®, std::type_identity_t val, std::type_identity_t mask) { reg = ( reg & (~mask)) | (val & mask); } ALWAYS_INLINE void ReadWrite(uintptr_t reg, u32 val, u32 mask) { ReadWrite(reinterpret_cast(reg), val, mask); } template requires UnsignedNonConstIntegral && ((sizeof...(Values) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE void ReadWrite(volatile IntType *reg, const Values... values) { return ReadWrite(reg, static_cast((EncodeValue(values) | ...)), static_cast((EncodeMask(values) | ...))); } template requires UnsignedNonConstIntegral && ((sizeof...(Values) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE void ReadWrite(volatile IntType ®, const Values... values) { return ReadWrite(reg, static_cast((EncodeValue(values) | ...)), static_cast((EncodeMask(values) | ...))); } template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE void ReadWrite(uintptr_t reg, const Values... values) { return ReadWrite(reg, (EncodeValue(values) | ...), (EncodeMask(values) | ...)); } template requires UnsignedNonConstIntegral ALWAYS_INLINE void SetBits(volatile IntType *reg, std::type_identity_t mask) { *reg = *reg | mask; } template requires UnsignedNonConstIntegral ALWAYS_INLINE void SetBits(volatile IntType ®, std::type_identity_t mask) { reg = reg | mask; } ALWAYS_INLINE void SetBits(uintptr_t reg, u32 mask) { SetBits(reinterpret_cast(reg), mask); } template requires UnsignedNonConstIntegral && ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE void SetBits(volatile IntType *reg, const Masks... masks) { return SetBits(reg, static_cast((EncodeMask(masks) | ...))); } template requires UnsignedNonConstIntegral && ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE void SetBits(volatile IntType ®, const Masks... masks) { return SetBits(reg, static_cast((EncodeMask(masks) | ...))); } template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE void SetBits(uintptr_t reg, const Masks... masks) { return SetBits(reg, (EncodeMask(masks) | ...)); } template requires UnsignedNonConstIntegral ALWAYS_INLINE void ClearBits(volatile IntType *reg, std::type_identity_t mask) { *reg = *reg & ~mask; } template requires UnsignedNonConstIntegral ALWAYS_INLINE void ClearBits(volatile IntType ®, std::type_identity_t mask) { reg = reg & ~mask; } ALWAYS_INLINE void ClearBits(uintptr_t reg, u32 mask) { ClearBits(reinterpret_cast(reg), mask); } template requires UnsignedNonConstIntegral && ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE void ClearBits(volatile IntType *reg, const Masks... masks) { return ClearBits(reg, static_cast((EncodeMask(masks) | ...))); } template requires UnsignedNonConstIntegral && ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE void ClearBits(volatile IntType ®, const Masks... masks) { return ClearBits(reg, static_cast((EncodeMask(masks) | ...))); } template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE void ClearBits(uintptr_t reg, const Masks... masks) { return ClearBits(reg, (EncodeMask(masks) | ...)); } template requires UnsignedNonConstIntegral ALWAYS_INLINE void MaskBits(volatile IntType *reg, std::type_identity_t mask) { *reg = *reg & mask; } template requires UnsignedNonConstIntegral ALWAYS_INLINE void MaskBits(volatile IntType ®, std::type_identity_t mask) { reg = reg & mask; } ALWAYS_INLINE void MaskBits(uintptr_t reg, u32 mask) { MaskBits(reinterpret_cast(reg), mask); } template requires UnsignedNonConstIntegral && ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE void MaskBits(volatile IntType *reg, const Masks... masks) { return MaskBits(reg, static_cast((EncodeMask(masks) | ...))); } template requires UnsignedNonConstIntegral && ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE void MaskBits(volatile IntType ®, const Masks... masks) { return MaskBits(reg, static_cast((EncodeMask(masks) | ...))); } template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) ALWAYS_INLINE void MaskBits(uintptr_t reg, const Masks... masks) { return MaskBits(reg, (EncodeMask(masks) | ...)); } #define REG_BITS_MASK(OFFSET, WIDTH) ::ams::reg::BitsMask{OFFSET, WIDTH} #define REG_BITS_VALUE(OFFSET, WIDTH, VALUE) ::ams::reg::BitsValue{OFFSET, WIDTH, VALUE} #define REG_BITS_VALUE_FROM_MASK(MASK, VALUE) ::ams::reg::GetValue(MASK, VALUE) #define REG_NAMED_BITS_MASK(PREFIX, NAME) REG_BITS_MASK(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH) #define REG_NAMED_BITS_VALUE(PREFIX, NAME, VALUE) REG_BITS_VALUE(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH, VALUE) #define REG_NAMED_BITS_ENUM(PREFIX, NAME, ENUM) REG_BITS_VALUE(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH, PREFIX##_##NAME##_##ENUM) #define REG_NAMED_BITS_ENUM_SEL(PREFIX, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_BITS_VALUE(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH, (__COND__) ? PREFIX##_##NAME##_##TRUE_ENUM : PREFIX##_##NAME##_##FALSE_ENUM) #define REG_DEFINE_NAMED_REG(PREFIX, NAME, __OFFSET__, __WIDTH__) \ constexpr inline u32 PREFIX##_##NAME##_OFFSET = __OFFSET__; \ constexpr inline u32 PREFIX##_##NAME##_WIDTH = __WIDTH__ #define REG_DEFINE_NAMED_BIT_ENUM(PREFIX, NAME, __OFFSET__, ZERO, ONE) \ REG_DEFINE_NAMED_REG(PREFIX, NAME, __OFFSET__, 1); \ \ enum PREFIX##_##NAME { \ PREFIX##_##NAME##_##ZERO = 0, \ PREFIX##_##NAME##_##ONE = 1, \ }; #define REG_DEFINE_NAMED_TWO_BIT_ENUM(PREFIX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) \ REG_DEFINE_NAMED_REG(PREFIX, NAME, __OFFSET__, 2); \ \ enum PREFIX##_##NAME { \ PREFIX##_##NAME##_##ZERO = 0, \ PREFIX##_##NAME##_##ONE = 1, \ PREFIX##_##NAME##_##TWO = 2, \ PREFIX##_##NAME##_##THREE = 3, \ }; #define REG_DEFINE_NAMED_THREE_BIT_ENUM(PREFIX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) \ REG_DEFINE_NAMED_REG(PREFIX, NAME, __OFFSET__, 3); \ \ enum PREFIX##_##NAME { \ PREFIX##_##NAME##_##ZERO = 0, \ PREFIX##_##NAME##_##ONE = 1, \ PREFIX##_##NAME##_##TWO = 2, \ PREFIX##_##NAME##_##THREE = 3, \ PREFIX##_##NAME##_##FOUR = 4, \ PREFIX##_##NAME##_##FIVE = 5, \ PREFIX##_##NAME##_##SIX = 6, \ PREFIX##_##NAME##_##SEVEN = 7, \ }; #define REG_DEFINE_NAMED_FOUR_BIT_ENUM(PREFIX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) \ REG_DEFINE_NAMED_REG(PREFIX, NAME, __OFFSET__, 4); \ \ enum PREFIX##_##NAME { \ PREFIX##_##NAME##_##ZERO = 0, \ PREFIX##_##NAME##_##ONE = 1, \ PREFIX##_##NAME##_##TWO = 2, \ PREFIX##_##NAME##_##THREE = 3, \ PREFIX##_##NAME##_##FOUR = 4, \ PREFIX##_##NAME##_##FIVE = 5, \ PREFIX##_##NAME##_##SIX = 6, \ PREFIX##_##NAME##_##SEVEN = 7, \ PREFIX##_##NAME##_##EIGHT = 8, \ PREFIX##_##NAME##_##NINE = 9, \ PREFIX##_##NAME##_##TEN = 10, \ PREFIX##_##NAME##_##ELEVEN = 11, \ PREFIX##_##NAME##_##TWELVE = 12, \ PREFIX##_##NAME##_##THIRTEEN = 13, \ PREFIX##_##NAME##_##FOURTEEN = 14, \ PREFIX##_##NAME##_##FIFTEEN = 15, \ }; }