sdmmc: implement driver suitable for fs + bootloader

* sdmmc: begin skeletoning sdmmc driver

* sdmmc: add most of SdHostStandardController

* sdmmc: implement most of SdmmcController

* sdmmc: Sdmmc2Controller

* sdmmc: skeleton implementation of Sdmmc1Controller

* sdmmc: complete abstract logic for Sdmmc1 power controller

* sdmmc: implement gpio handling for sdmmc1-register-control

* sdmmc: implement pinmux handling for sdmmc1-register-control

* sdmmc: fix building for arm32 and in stratosphere context

* sdmmc: implement voltage enable/set for sdmmc1-register-control

* util: move T(V)SNPrintf from kernel to util

* sdmmc: implement BaseDeviceAccessor

* sdmmc: implement MmcDeviceAccessor

* sdmmc: implement clock reset controller for register api

* sdmmc: fix bug in WaitWhileCommandInhibit, add mmc accessors

* exo: add sdmmc test program

* sdmmc: fix speed mode extension, add CheckMmcConnection for debug

* sdmmc: add DeviceDetector, gpio: implement client api

* gpio: modernize client api instead of doing it the lazy way

* sdmmc: SdCardDeviceAccessor impl

* sdmmc: update test program to read first two sectors of sd card

* sdmmc: fix vref sel

* sdmmc: finish outward-facing api (untested)

* ams: changes for libvapours including tegra register defs

* sdmmc: remove hwinit
This commit is contained in:
SciresM
2020-10-30 11:54:30 -07:00
committed by GitHub
parent ac04e02a08
commit 166318ba77
143 changed files with 13696 additions and 1569 deletions

View File

@@ -20,12 +20,22 @@
#include <vapours/literals.hpp>
#include <vapours/allocator.hpp>
#include <vapours/device_code.hpp>
#include <vapours/timespan.hpp>
#include <vapours/span.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
#include <vapours/tegra.hpp>
#endif
#include <vapours/crypto.hpp>
#include <vapours/svc.hpp>
#include <vapours/ams/ams_fatal_error_context.hpp>
#include <vapours/dd.hpp>
#include <vapours/sdmmc.hpp>

View File

@@ -0,0 +1,24 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/results.hpp>
#include <vapours/dd/dd_common_types.hpp>
#include <vapours/dd/dd_io_mapping.hpp>
#include <vapours/dd/dd_cache.hpp>

View File

@@ -0,0 +1,25 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/dd/dd_common_types.hpp>
namespace ams::dd {
void InvalidateDataCache(void *addr, size_t size);
void StoreDataCache(void *addr, size_t size);
void FlushDataCache(void *addr, size_t size);
}

View File

@@ -0,0 +1,29 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/results.hpp>
namespace ams::dd {
using PhysicalAddress = u64;
using DeviceVirtualAddress = u64;
static_assert(std::same_as<PhysicalAddress, ams::svc::PhysicalAddress>);
}

View File

@@ -0,0 +1,26 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/dd/dd_common_types.hpp>
namespace ams::dd {
uintptr_t QueryIoMapping(dd::PhysicalAddress phys_addr, size_t size);
u32 ReadIoRegister(dd::PhysicalAddress phys_addr);
void WriteIoRegister(dd::PhysicalAddress phys_addr, u32 value);
}

View File

@@ -0,0 +1,49 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
namespace ams {
namespace impl {
using DeviceCodeType = u32;
}
/* TODO: Better understand device code components. */
class DeviceCode {
private:
impl::DeviceCodeType inner_value;
public:
constexpr DeviceCode(impl::DeviceCodeType v) : inner_value(v) { /* ... */ }
constexpr impl::DeviceCodeType GetInternalValue() const { return this->inner_value; }
constexpr bool operator==(const DeviceCode &rhs) const {
return this->GetInternalValue() == rhs.GetInternalValue();
}
constexpr bool operator!=(const DeviceCode &rhs) const {
return !(*this == rhs);
}
};
constexpr inline const DeviceCode InvalidDeviceCode(0);
}

View File

@@ -0,0 +1,255 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::reg {
template<typename T>
concept UnsignedNonConstIntegral = std::unsigned_integral<T> && !std::is_const<T>::value;
using BitsValue = std::tuple<u16, u16, u32>;
using BitsMask = std::tuple<u16, u16>;
constexpr ALWAYS_INLINE u32 GetOffset(const BitsMask v) { return static_cast<u32>(std::get<0>(v)); }
constexpr ALWAYS_INLINE u32 GetOffset(const BitsValue v) { return static_cast<u32>(std::get<0>(v)); }
constexpr ALWAYS_INLINE u32 GetWidth(const BitsMask v) { return static_cast<u32>(std::get<1>(v)); }
constexpr ALWAYS_INLINE u32 GetWidth(const BitsValue v) { return static_cast<u32>(std::get<1>(v)); }
constexpr ALWAYS_INLINE u32 GetValue(const BitsValue v) { return static_cast<u32>(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<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
constexpr ALWAYS_INLINE u32 Encode(const Values... values) {
return (EncodeValue(values) | ...);
}
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
constexpr ALWAYS_INLINE u32 EncodeMask(const Masks... masks) {
return (EncodeMask(masks) | ...);
}
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
ALWAYS_INLINE void Write(volatile IntType *reg, std::type_identity_t<IntType> val) { *reg = val; }
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
ALWAYS_INLINE void Write(volatile IntType &reg, std::type_identity_t<IntType> val) { reg = val; }
ALWAYS_INLINE void Write(uintptr_t reg, u32 val) { Write(reinterpret_cast<volatile u32 *>(reg), val); }
template<typename IntType, typename... Values> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
ALWAYS_INLINE void Write(volatile IntType *reg, const Values... values) { return Write(reg, static_cast<IntType>((EncodeValue(values) | ...))); }
template<typename IntType, typename... Values> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
ALWAYS_INLINE void Write(volatile IntType &reg, const Values... values) { return Write(reg, static_cast<IntType>((EncodeValue(values) | ...))); }
template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
ALWAYS_INLINE void Write(uintptr_t reg, const Values... values) { return Write(reg, (EncodeValue(values) | ...)); }
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
ALWAYS_INLINE IntType Read(volatile IntType *reg) { return *reg; }
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
ALWAYS_INLINE IntType Read(volatile IntType &reg) { return reg; }
ALWAYS_INLINE u32 Read(uintptr_t reg) { return Read(reinterpret_cast<volatile u32 *>(reg)); }
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
ALWAYS_INLINE IntType Read(volatile IntType *reg, std::type_identity_t<IntType> mask) { return *reg & mask; }
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
ALWAYS_INLINE IntType Read(volatile IntType &reg, std::type_identity_t<IntType> mask) { return reg & mask; }
ALWAYS_INLINE u32 Read(uintptr_t reg, u32 mask) { return Read(reinterpret_cast<volatile u32 *>(reg), mask); }
template<typename IntType, typename... Masks> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
ALWAYS_INLINE IntType Read(volatile IntType *reg, const Masks... masks) { return Read(reg, static_cast<IntType>((EncodeMask(masks) | ...))); }
template<typename IntType, typename... Masks> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
ALWAYS_INLINE IntType Read(volatile IntType &reg, const Masks... masks) { return Read(reg, static_cast<IntType>((EncodeMask(masks) | ...))); }
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
ALWAYS_INLINE u32 Read(uintptr_t reg, const Masks... masks) { return Read(reg, (EncodeMask(masks) | ...)); }
template<typename IntType, typename... Values> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
ALWAYS_INLINE bool HasValue(volatile IntType *reg, const Values... values) { return Read(reg, static_cast<IntType>((EncodeMask(values) | ...))) == static_cast<IntType>(Encode(values...)); }
template<typename IntType, typename... Values> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
ALWAYS_INLINE bool HasValue(volatile IntType &reg, const Values... values) { return Read(reg, static_cast<IntType>((EncodeMask(values) | ...))) == static_cast<IntType>(Encode(values...)); }
template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
ALWAYS_INLINE bool HasValue(uintptr_t reg, const Values... values) { return Read(reg, (EncodeMask(values) | ...)) == Encode(values...); }
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
ALWAYS_INLINE IntType GetValue(volatile IntType *reg, const BitsMask mask) { return Read(reg, mask) >> GetOffset(mask); }
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
ALWAYS_INLINE IntType GetValue(volatile IntType &reg, 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<typename IntType> requires UnsignedNonConstIntegral<IntType>
ALWAYS_INLINE void ReadWrite(volatile IntType *reg, std::type_identity_t<IntType> val, std::type_identity_t<IntType> mask) { *reg = (*reg & (~mask)) | (val & mask); }
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
ALWAYS_INLINE void ReadWrite(volatile IntType &reg, std::type_identity_t<IntType> val, std::type_identity_t<IntType> mask) { reg = ( reg & (~mask)) | (val & mask); }
ALWAYS_INLINE void ReadWrite(uintptr_t reg, u32 val, u32 mask) { ReadWrite(reinterpret_cast<volatile u32 *>(reg), val, mask); }
template<typename IntType, typename... Values> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
ALWAYS_INLINE void ReadWrite(volatile IntType *reg, const Values... values) { return ReadWrite(reg, static_cast<IntType>((EncodeValue(values) | ...)), static_cast<IntType>((EncodeMask(values) | ...))); }
template<typename IntType, typename... Values> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
ALWAYS_INLINE void ReadWrite(volatile IntType &reg, const Values... values) { return ReadWrite(reg, static_cast<IntType>((EncodeValue(values) | ...)), static_cast<IntType>((EncodeMask(values) | ...))); }
template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
ALWAYS_INLINE void ReadWrite(uintptr_t reg, const Values... values) { return ReadWrite(reg, (EncodeValue(values) | ...), (EncodeMask(values) | ...)); }
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
ALWAYS_INLINE void SetBits(volatile IntType *reg, std::type_identity_t<IntType> mask) { *reg = *reg | mask; }
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
ALWAYS_INLINE void SetBits(volatile IntType &reg, std::type_identity_t<IntType> mask) { reg = reg | mask; }
ALWAYS_INLINE void SetBits(uintptr_t reg, u32 mask) { SetBits(reinterpret_cast<volatile u32 *>(reg), mask); }
template<typename IntType, typename... Masks> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
ALWAYS_INLINE void SetBits(volatile IntType *reg, const Masks... masks) { return SetBits(reg, static_cast<IntType>((EncodeMask(masks) | ...))); }
template<typename IntType, typename... Masks> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
ALWAYS_INLINE void SetBits(volatile IntType &reg, const Masks... masks) { return SetBits(reg, static_cast<IntType>((EncodeMask(masks) | ...))); }
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
ALWAYS_INLINE void SetBits(uintptr_t reg, const Masks... masks) { return SetBits(reg, (EncodeMask(masks) | ...)); }
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
ALWAYS_INLINE void ClearBits(volatile IntType *reg, std::type_identity_t<IntType> mask) { *reg = *reg & ~mask; }
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
ALWAYS_INLINE void ClearBits(volatile IntType &reg, std::type_identity_t<IntType> mask) { reg = reg & ~mask; }
ALWAYS_INLINE void ClearBits(uintptr_t reg, u32 mask) { ClearBits(reinterpret_cast<volatile u32 *>(reg), mask); }
template<typename IntType, typename... Masks> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
ALWAYS_INLINE void ClearBits(volatile IntType *reg, const Masks... masks) { return ClearBits(reg, static_cast<IntType>((EncodeMask(masks) | ...))); }
template<typename IntType, typename... Masks> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
ALWAYS_INLINE void ClearBits(volatile IntType &reg, const Masks... masks) { return ClearBits(reg, static_cast<IntType>((EncodeMask(masks) | ...))); }
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
ALWAYS_INLINE void ClearBits(uintptr_t reg, const Masks... masks) { return ClearBits(reg, (EncodeMask(masks) | ...)); }
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
ALWAYS_INLINE void MaskBits(volatile IntType *reg, std::type_identity_t<IntType> mask) { *reg = *reg & mask; }
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
ALWAYS_INLINE void MaskBits(volatile IntType &reg, std::type_identity_t<IntType> mask) { reg = reg & mask; }
ALWAYS_INLINE void MaskBits(uintptr_t reg, u32 mask) { MaskBits(reinterpret_cast<volatile u32 *>(reg), mask); }
template<typename IntType, typename... Masks> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
ALWAYS_INLINE void MaskBits(volatile IntType *reg, const Masks... masks) { return MaskBits(reg, static_cast<IntType>((EncodeMask(masks) | ...))); }
template<typename IntType, typename... Masks> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
ALWAYS_INLINE void MaskBits(volatile IntType &reg, const Masks... masks) { return MaskBits(reg, static_cast<IntType>((EncodeMask(masks) | ...))); }
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::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, \
};
}

View File

@@ -41,10 +41,12 @@
#include <vapours/results/nim_results.hpp>
#include <vapours/results/ns_results.hpp>
#include <vapours/results/os_results.hpp>
#include <vapours/results/pcv_results.hpp>
#include <vapours/results/pgl_results.hpp>
#include <vapours/results/pm_results.hpp>
#include <vapours/results/psc_results.hpp>
#include <vapours/results/ro_results.hpp>
#include <vapours/results/sdmmc_results.hpp>
#include <vapours/results/settings_results.hpp>
#include <vapours/results/sf_results.hpp>
#include <vapours/results/sm_results.hpp>

View File

@@ -0,0 +1,26 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::pcv {
R_DEFINE_NAMESPACE_RESULT_MODULE(133);
R_DEFINE_ERROR_RESULT(IllegalRequest, 16);
}

View File

@@ -0,0 +1,105 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::sdmmc {
R_DEFINE_NAMESPACE_RESULT_MODULE(24);
R_DEFINE_ERROR_RESULT(NoDevice, 1);
R_DEFINE_ERROR_RESULT(NotActivated, 2);
R_DEFINE_ERROR_RESULT(DeviceRemoved, 3);
R_DEFINE_ERROR_RESULT(NotAwakened, 4);
R_DEFINE_ERROR_RANGE(CommunicationError, 32, 126);
R_DEFINE_ERROR_RANGE(CommunicationNotAttained, 33, 46);
R_DEFINE_ERROR_RESULT(ResponseIndexError, 34);
R_DEFINE_ERROR_RESULT(ResponseEndBitError, 35);
R_DEFINE_ERROR_RESULT(ResponseCrcError, 36);
R_DEFINE_ERROR_RESULT(ResponseTimeoutError, 37);
R_DEFINE_ERROR_RESULT(DataEndBitError, 38);
R_DEFINE_ERROR_RESULT(DataCrcError, 39);
R_DEFINE_ERROR_RESULT(DataTimeoutError, 40);
R_DEFINE_ERROR_RESULT(AutoCommandResponseIndexError, 41);
R_DEFINE_ERROR_RESULT(AutoCommandResponseEndBitError, 42);
R_DEFINE_ERROR_RESULT(AutoCommandResponseCrcError, 43);
R_DEFINE_ERROR_RESULT(AutoCommandResponseTimeoutError, 44);
R_DEFINE_ERROR_RESULT(CommandCompleteSoftwareTimeout, 45);
R_DEFINE_ERROR_RESULT(TransferCompleteSoftwareTimeout, 46);
R_DEFINE_ERROR_RANGE(DeviceStatusHasError, 48, 70);
R_DEFINE_ERROR_RESULT(DeviceStatusAddressOutOfRange, 49);
R_DEFINE_ERROR_RESULT(DeviceStatusAddressMisaligned, 50);
R_DEFINE_ERROR_RESULT(DeviceStatusBlockLenError, 51);
R_DEFINE_ERROR_RESULT(DeviceStatusEraseSeqError, 52);
R_DEFINE_ERROR_RESULT(DeviceStatusEraseParam, 53);
R_DEFINE_ERROR_RESULT(DeviceStatusWpViolation, 54);
R_DEFINE_ERROR_RESULT(DeviceStatusLockUnlockFailed, 55);
R_DEFINE_ERROR_RESULT(DeviceStatusComCrcError, 56);
R_DEFINE_ERROR_RESULT(DeviceStatusIllegalCommand, 57);
R_DEFINE_ERROR_RESULT(DeviceStatusDeviceEccFailed, 58);
R_DEFINE_ERROR_RESULT(DeviceStatusCcError, 59);
R_DEFINE_ERROR_RESULT(DeviceStatusError, 60);
R_DEFINE_ERROR_RESULT(DeviceStatusCidCsdOverwrite, 61);
R_DEFINE_ERROR_RESULT(DeviceStatusWpEraseSkip, 62);
R_DEFINE_ERROR_RESULT(DeviceStatusEraseReset, 63);
R_DEFINE_ERROR_RESULT(DeviceStatusSwitchError, 64);
R_DEFINE_ERROR_RESULT(UnexpectedDeviceState, 72);
R_DEFINE_ERROR_RESULT(UnexpectedDeviceCsdValue, 73);
R_DEFINE_ERROR_RESULT(AbortTransactionSoftwareTimeout, 74);
R_DEFINE_ERROR_RESULT(CommandInhibitCmdSoftwareTimeout, 75);
R_DEFINE_ERROR_RESULT(CommandInhibitDatSoftwareTimeout, 76);
R_DEFINE_ERROR_RESULT(BusySoftwareTimeout, 77);
R_DEFINE_ERROR_RESULT(IssueTuningCommandSoftwareTimeout, 78);
R_DEFINE_ERROR_RESULT(TuningFailed, 79);
R_DEFINE_ERROR_RESULT(MmcInitializationSoftwareTimeout, 80);
R_DEFINE_ERROR_RESULT(MmcNotSupportExtendedCsd, 81);
R_DEFINE_ERROR_RESULT(UnexpectedMmcExtendedCsdValue, 82);
R_DEFINE_ERROR_RESULT(MmcEraseSoftwareTimeout, 83);
R_DEFINE_ERROR_RESULT(SdCardValidationError, 84);
R_DEFINE_ERROR_RESULT(SdCardInitializationSoftwareTimeout, 85);
R_DEFINE_ERROR_RESULT(SdCardGetValidRcaSoftwareTimeout, 86);
R_DEFINE_ERROR_RESULT(UnexpectedSdCardAcmdDisabled, 87);
R_DEFINE_ERROR_RESULT(SdCardNotSupportSwitchFunctionStatus, 88);
R_DEFINE_ERROR_RESULT(UnexpectedSdCardSwitchFunctionStatus, 89);
R_DEFINE_ERROR_RESULT(SdCardNotSupportAccessMode, 90);
R_DEFINE_ERROR_RESULT(SdCardNot4BitBusWidthAtUhsIMode, 91);
R_DEFINE_ERROR_RESULT(SdCardNotSupportSdr104AndSdr50, 92);
R_DEFINE_ERROR_RESULT(SdCardCannotSwitchAccessMode, 93);
R_DEFINE_ERROR_RESULT(SdCardFailedSwitchAccessMode, 94);
R_DEFINE_ERROR_RESULT(SdCardUnacceptableCurrentConsumption, 95);
R_DEFINE_ERROR_RESULT(SdCardNotReadyToVoltageSwitch, 96);
R_DEFINE_ERROR_RESULT(SdCardNotCompleteVoltageSwitch, 97);
R_DEFINE_ERROR_RANGE(HostControllerUnexpected, 128, 158);
R_DEFINE_ERROR_RESULT(InternalClockStableSoftwareTimeout, 129);
R_DEFINE_ERROR_RESULT(SdHostStandardUnknownAutoCmdError, 130);
R_DEFINE_ERROR_RESULT(SdHostStandardUnknownError, 131);
R_DEFINE_ERROR_RESULT(SdmmcDllCalibrationSoftwareTimeout, 132);
R_DEFINE_ERROR_RESULT(SdmmcDllApplicationSoftwareTimeout, 133);
R_DEFINE_ERROR_RESULT(SdHostStandardFailSwitchTo1_8V, 134);
R_DEFINE_ERROR_RESULT(DriveStrengthCalibrationNotCompleted, 135);
R_DEFINE_ERROR_RESULT(DriveStrengthCalibrationSoftwareTimeout, 136);
R_DEFINE_ERROR_RESULT(SdmmcCompShortToGnd, 137);
R_DEFINE_ERROR_RESULT(SdmmcCompOpen, 138);
R_DEFINE_ERROR_RANGE(InternalError, 160, 190);
R_DEFINE_ERROR_RESULT(NoWaitedInterrupt, 161);
R_DEFINE_ERROR_RESULT(WaitInterruptSoftwareTimeout, 162);
R_DEFINE_ERROR_RESULT(NotImplemented, 201);
}

View File

@@ -0,0 +1,28 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/results.hpp>
#include <vapours/util.hpp>
#include <vapours/svc.hpp>
#include <vapours/sdmmc/sdmmc_build_config.hpp>
#include <vapours/sdmmc/sdmmc_common.hpp>
#include <vapours/sdmmc/sdmmc_mmc.hpp>
#include <vapours/sdmmc/sdmmc_sd_card.hpp>
#include <vapours/sdmmc/sdmmc_gc_asic.hpp>

View File

@@ -0,0 +1,72 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/results.hpp>
#include <vapours/util.hpp>
#include <vapours/svc.hpp>
#include <vapours/dd.hpp>
#if defined(ATMOSPHERE_IS_EXOSPHERE)
//#define AMS_SDMMC_THREAD_SAFE
//#define AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS
//#define AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL
//#define AMS_SDMMC_USE_DEVICE_DETECTOR
//#define AMS_SDMMC_USE_LOGGER
//#define AMS_SDMMC_USE_OS_EVENTS
//#define AMS_SDMMC_USE_OS_TIMER
#define AMS_SDMMC_USE_UTIL_TIMER
//#define AMS_SDMMC_ENABLE_MMC_HS400
//#define AMS_SDMMC_ENABLE_SD_UHS_I
//#define AMS_SDMMC_SET_PLLC4_BASE
//#define AMS_SDMMC_USE_SD_CARD_DETECTOR
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
//#define AMS_SDMMC_THREAD_SAFE
//#define AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS
//#define AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL
//#define AMS_SDMMC_USE_DEVICE_DETECTOR
//#define AMS_SDMMC_USE_LOGGER
//#define AMS_SDMMC_USE_OS_EVENTS
//#define AMS_SDMMC_USE_OS_TIMER
#define AMS_SDMMC_USE_UTIL_TIMER
//#define AMS_SDMMC_ENABLE_MMC_HS400
//#define AMS_SDMMC_ENABLE_SD_UHS_I
//#define AMS_SDMMC_SET_PLLC4_BASE
//#define AMS_SDMMC_USE_SD_CARD_DETECTOR
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
#define AMS_SDMMC_THREAD_SAFE
#define AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS
#define AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL
#define AMS_SDMMC_USE_DEVICE_DETECTOR
#define AMS_SDMMC_USE_LOGGER
#define AMS_SDMMC_USE_OS_EVENTS
#define AMS_SDMMC_USE_OS_TIMER
//#define AMS_SDMMC_USE_UTIL_TIMER
#define AMS_SDMMC_ENABLE_MMC_HS400
#define AMS_SDMMC_ENABLE_SD_UHS_I
#define AMS_SDMMC_SET_PLLC4_BASE
#define AMS_SDMMC_USE_SD_CARD_DETECTOR
#else
#error "Unknown execution context for ams::sdmmc!"
#endif

View File

@@ -0,0 +1,114 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/sdmmc/sdmmc_build_config.hpp>
namespace ams::sdmmc {
enum BusPower {
BusPower_Off = 0,
BusPower_1_8V = 1,
BusPower_3_3V = 2,
};
enum BusWidth {
BusWidth_1Bit = 0,
BusWidth_4Bit = 1,
BusWidth_8Bit = 2,
};
enum SpeedMode {
SpeedMode_MmcIdentification = 0,
SpeedMode_MmcLegacySpeed = 1,
SpeedMode_MmcHighSpeed = 2,
SpeedMode_MmcHs200 = 3,
SpeedMode_MmcHs400 = 4,
SpeedMode_SdCardIdentification = 5,
SpeedMode_SdCardDefaultSpeed = 6,
SpeedMode_SdCardHighSpeed = 7,
SpeedMode_SdCardSdr12 = 8,
SpeedMode_SdCardSdr25 = 9,
SpeedMode_SdCardSdr50 = 10,
SpeedMode_SdCardSdr104 = 11,
SpeedMode_SdCardDdr50 = 12,
SpeedMode_GcAsicFpgaSpeed = 13,
SpeedMode_GcAsicSpeed = 14,
};
enum Port {
Port_Mmc0 = 0,
Port_SdCard0 = 1,
Port_GcAsic0 = 2,
};
struct ErrorInfo {
u32 num_activation_failures;
u32 num_activation_error_corrections;
u32 num_read_write_failures;
u32 num_read_write_error_corrections;
};
struct DataTransfer {
void *buffer;
size_t buffer_size;
size_t block_size;
u32 num_blocks;
bool is_read;
};
using DeviceDetectionEventCallback = void (*)(void *);
constexpr inline size_t SectorSize = 0x200;
constexpr inline size_t DeviceCidSize = 0x10;
constexpr inline size_t DeviceCsdSize = 0x10;
constexpr inline size_t BufferDeviceVirtualAddressAlignment = alignof(ams::dd::DeviceVirtualAddress);
static_assert(BufferDeviceVirtualAddressAlignment >= 8);
void Initialize(Port port);
void Finalize(Port port);
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
void SwitchToPcvClockResetControl();
#endif
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
void RegisterDeviceVirtualAddress(Port port, uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address);
void UnregisterDeviceVirtualAddress(Port port, uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address);
#endif
void ChangeCheckTransferInterval(Port port, u32 ms);
void SetDefaultCheckTransferInterval(Port port);
Result Activate(Port port);
void Deactivate(Port port);
Result Read(void *dst, size_t dst_size, Port port, u32 sector_index, u32 num_sectors);
Result Write(Port port, u32 sector_index, u32 num_sectors, const void *src, size_t src_size);
Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port);
Result GetDeviceSpeedMode(SpeedMode *out, Port port);
Result GetDeviceMemoryCapacity(u32 *out_num_sectors, Port port);
Result GetDeviceStatus(u32 *out_device_status, Port port);
Result GetDeviceCid(void *out, size_t out_size, Port port);
Result GetDeviceCsd(void *out, size_t out_size, Port port);
void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size, Port port);
}

View File

@@ -0,0 +1,34 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/sdmmc/sdmmc_build_config.hpp>
namespace ams::sdmmc {
constexpr inline size_t GcAsicOperationSize = 0x40;
void PutGcAsicToSleep(Port port);
Result AwakenGcAsic(Port port);
Result WriteGcAsicOperation(Port port, const void *op_buf, size_t op_buf_size);
Result FinishGcAsicOperation(Port port);
Result AbortGcAsicOperation(Port port);
Result SleepGcAsic(Port port);
Result UpdateGcAsicKey(Port port);
void SignalGcRemovedEvent(Port port);
void ClearGcRemovedEvent(Port port);
}

View File

@@ -0,0 +1,43 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/sdmmc/sdmmc_build_config.hpp>
namespace ams::sdmmc {
enum MmcPartition {
MmcPartition_UserData = 0,
MmcPartition_BootPartition1 = 1,
MmcPartition_BootPartition2 = 2,
MmcPartition_Unknown = 3,
};
constexpr inline size_t MmcExtendedCsdSize = 0x200;
constexpr inline size_t MmcWorkBufferSize = MmcExtendedCsdSize;
void SetMmcWorkBuffer(Port port, void *buffer, size_t buffer_size);
void PutMmcToSleep(Port port);
void AwakenMmc(Port port);
Result SelectMmcPartition(Port port, MmcPartition mmc_partition);
Result EraseMmc(Port port);
Result GetMmcBootPartitionCapacity(u32 *out_num_sectors, Port port);
Result GetMmcExtendedCsd(void *out_buffer, size_t buffer_size, Port port);
Result CheckMmcConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port);
}

View File

@@ -0,0 +1,53 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/sdmmc/sdmmc_build_config.hpp>
namespace ams::sdmmc {
enum SdCardSwitchFunction {
SdCardSwitchFunction_CheckSupportedFunction = 0,
SdCardSwitchFunction_CheckDefault = 1,
SdCardSwitchFunction_CheckHighSpeed = 2,
SdCardSwitchFunction_CheckSdr50 = 3,
SdCardSwitchFunction_CheckSdr104 = 4,
SdCardSwitchFunction_CheckDdr50 = 5,
};
constexpr inline size_t SdCardScrSize = 0x08;
constexpr inline size_t SdCardSwitchFunctionStatusSize = 0x40;
constexpr inline size_t SdCardSdStatusSize = 0x40;
constexpr inline size_t SdCardWorkBufferSize = SdCardSdStatusSize;
void SetSdCardWorkBuffer(Port port, void *buffer, size_t buffer_size);
void PutSdCardToSleep(Port port);
void AwakenSdCard(Port port);
Result GetSdCardProtectedAreaCapacity(u32 *out_num_sectors, Port port);
Result GetSdCardScr(void *dst, size_t dst_size, Port port);
Result GetSdCardSwitchFunctionStatus(void *dst, size_t dst_size, Port port, SdCardSwitchFunction switch_function);
Result GetSdCardCurrentConsumption(u16 *out_current_consumption, Port port, SpeedMode speed_mode);
Result GetSdCardSdStatus(void *dst, size_t dst_size, Port port);
Result CheckSdCardConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port);
bool IsSdCardInserted(Port port);
bool IsSdCardRemoved(Port port);
void RegisterSdCardDetectionEventCallback(Port port, DeviceDetectionEventCallback callback, void *arg);
void UnregisterSdCardDetectionEventCallback(Port port);
}

View File

@@ -0,0 +1,39 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#include <vapours/tegra/tegra_ahb_arbc.hpp>
#include <vapours/tegra/tegra_apb_misc.hpp>
#include <vapours/tegra/tegra_avp_cache.hpp>
#include <vapours/tegra/tegra_clkrst.hpp>
#include <vapours/tegra/tegra_emc.hpp>
#include <vapours/tegra/tegra_evp.hpp>
#include <vapours/tegra/tegra_flow_ctlr.hpp>
#include <vapours/tegra/tegra_ictlr.hpp>
#include <vapours/tegra/tegra_mc.hpp>
#include <vapours/tegra/tegra_mselect.hpp>
#include <vapours/tegra/tegra_pinmux.hpp>
#include <vapours/tegra/tegra_pg_up.hpp>
#include <vapours/tegra/tegra_pmc.hpp>
#include <vapours/tegra/tegra_sb.hpp>
#include <vapours/tegra/tegra_sysctr0.hpp>
#include <vapours/tegra/tegra_timer.hpp>

View File

@@ -0,0 +1,52 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#define AHB_ARBC(x) (0x6000c000 + x)
#define AHB_ARBITRATION_DISABLE (0x004)
#define AHB_ARBITRATION_PRIORITY_CTRL (0x008)
#define AHB_MASTER_SWID (0x018)
#define AHB_MASTER_SWID_1 (0x038)
#define AHB_GIZMO_TZRAM (0x054)
#define AHB_AHB_SPARE_REG (0x110)
#define AHB_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (AHB_, NAME)
#define AHB_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (AHB_, NAME, VALUE)
#define AHB_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (AHB_, NAME, ENUM)
#define AHB_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(AHB_, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
#define DEFINE_AHB_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (AHB_, NAME, __OFFSET__, __WIDTH__)
#define DEFINE_AHB_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (AHB_, NAME, __OFFSET__, ZERO, ONE)
#define DEFINE_AHB_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (AHB_, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
#define DEFINE_AHB_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(AHB_, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_AHB_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (AHB_, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
DEFINE_AHB_REG_BIT_ENUM(ARBITRATION_DISABLE_COP, 1, ENABLE, DISABLE);
DEFINE_AHB_REG_BIT_ENUM(ARBITRATION_DISABLE_AHBDMA, 5, ENABLE, DISABLE);
DEFINE_AHB_REG_BIT_ENUM(ARBITRATION_DISABLE_USB, 6, ENABLE, DISABLE);
DEFINE_AHB_REG_BIT_ENUM(ARBITRATION_DISABLE_USB2, 18, ENABLE, DISABLE);
DEFINE_AHB_REG(AHB_SPARE_REG_CSITE_PADMACRO3_TRIM_SEL, 0, 5);
DEFINE_AHB_REG_BIT_ENUM(AHB_SPARE_REG_OBS_OVERRIDE_EN, 5, DISABLE, ENABLE);
DEFINE_AHB_REG_BIT_ENUM(AHB_SPARE_REG_APB2JTAG_OVERRIDE_EN, 6, DISABLE, ENABLE);
DEFINE_AHB_REG(AHB_SPARE_REG_AHB_SPARE_REG, 12, 32-12);

View File

@@ -0,0 +1,145 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#define APB_MISC_PP_CONFIG_CTL (0x024)
#define APB_MISC_GP_ASDBGREG (0x810)
#define APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL (0xA98)
#define APB_MISC_GP_EMMC2_PAD_CFGPADCTRL (0xA9C)
#define APB_MISC_GP_SDMMC2_PAD_CFGPADCTRL (0xA9C)
#define APB_MISC_GP_EMMC4_PAD_CFGPADCTRL (0xAB4)
#define APB_MISC_GP_EMMC4_PAD_PUPD_CFGPADCTRL (0xABC)
#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 (0xc00)
#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 (0xc00)
#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0 (0xc04)
#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG2_0 (0xc08)
#define APB_MISC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (APB_MISC, NAME)
#define APB_MISC_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (APB_MISC, NAME, VALUE)
#define APB_MISC_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (APB_MISC, NAME, ENUM)
#define APB_MISC_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(APB_MISC, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
#define DEFINE_APB_MISC_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (APB_MISC, NAME, __OFFSET__, __WIDTH__)
#define DEFINE_APB_MISC_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (APB_MISC, NAME, __OFFSET__, ZERO, ONE)
#define DEFINE_APB_MISC_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (APB_MISC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
#define DEFINE_APB_MISC_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(APB_MISC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_APB_MISC_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (APB_MISC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
DEFINE_APB_MISC_REG_BIT_ENUM(PP_CONFIG_CTL_JTAG, 6, DISABLE, ENABLE);
DEFINE_APB_MISC_REG_BIT_ENUM(PP_CONFIG_CTL_TBE, 7, DISABLE, ENABLE);
DEFINE_APB_MISC_REG(GP_ASDBGREG_CFG2TMC_RAM_SVOP_PDP, 24, 2);
DEFINE_APB_MISC_REG (GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_PAD_CAL_DRVDN, 12, 7);
DEFINE_APB_MISC_REG (GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_PAD_CAL_DRVUP, 20, 7);
DEFINE_APB_MISC_REG (GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_CLK_CFG_CAL_DRVDN_SLWR, 28, 2);
DEFINE_APB_MISC_REG (GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_CLK_CFG_CAL_DRVDN_SLWF, 30, 2);
DEFINE_APB_MISC_REG_BIT_ENUM(GP_EMMC2_PAD_CFGPADCTRL_CFG2TMC_EMMC2_PAD_E_SCH, 0, DISABLE, ENABLE);
DEFINE_APB_MISC_REG (GP_EMMC2_PAD_CFGPADCTRL_CFG2TMC_EMMC2_PAD_DRVDN_COMP, 2, 6);
DEFINE_APB_MISC_REG (GP_EMMC2_PAD_CFGPADCTRL_CFG2TMC_EMMC2_PAD_DRVUP_COMP, 8, 6);
DEFINE_APB_MISC_REG (GP_EMMC2_PAD_CFGPADCTRL_MISC2PMC_EMMC2_ALL_PARK, 14, 13);
DEFINE_APB_MISC_REG (GP_SDMMC2_PAD_CFGPADCTRL_CFG2TMC_SDMMC2_PAD_CAL_DRVDN, 12, 7);
DEFINE_APB_MISC_REG (GP_SDMMC2_PAD_CFGPADCTRL_CFG2TMC_SDMMC2_PAD_CAL_DRVUP, 20, 7);
DEFINE_APB_MISC_REG_BIT_ENUM(GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_E_SCH, 0, DISABLE, ENABLE);
DEFINE_APB_MISC_REG (GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DRVDN_COMP, 2, 6);
DEFINE_APB_MISC_REG (GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DRVUP_COMP, 8, 6);
DEFINE_APB_MISC_REG (GP_EMMC4_PAD_CFGPADCTRL_MISC2PMC_EMMC4_ALL_PARK, 14, 13);
DEFINE_APB_MISC_REG(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_CMD_PUPD_PULLU, 1, 1);
DEFINE_APB_MISC_REG(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_CLK_PUPD_PULLD, 2, 1);
DEFINE_APB_MISC_REG(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DQS_PUPD_PULLD, 22, 1);
#define DEFINE_SLAVE_SECURITY_REG(RINDEX, INDEX, NAME) DEFINE_APB_MISC_REG_BIT_ENUM(SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG##RINDEX##_##NAME##_SECURITY_EN, INDEX, DISABLE, ENABLE)
DEFINE_SLAVE_SECURITY_REG(0, 29, STM);
DEFINE_SLAVE_SECURITY_REG(0, 24, CEC);
DEFINE_SLAVE_SECURITY_REG(0, 23, ATOMICS);
DEFINE_SLAVE_SECURITY_REG(0, 22, LA);
DEFINE_SLAVE_SECURITY_REG(0, 21, HDA);
DEFINE_SLAVE_SECURITY_REG(0, 20, SATA);
DEFINE_SLAVE_SECURITY_REG(0, 16, KFUSE);
DEFINE_SLAVE_SECURITY_REG(0, 15, FUSE);
DEFINE_SLAVE_SECURITY_REG(0, 14, SE);
DEFINE_SLAVE_SECURITY_REG(0, 13, PMC);
DEFINE_SLAVE_SECURITY_REG(0, 11, RTC);
DEFINE_SLAVE_SECURITY_REG(0, 10, CSITE);
DEFINE_SLAVE_SECURITY_REG(0, 9, QSPI);
DEFINE_SLAVE_SECURITY_REG(0, 8, PWM);
DEFINE_SLAVE_SECURITY_REG(0, 6, DTV);
DEFINE_SLAVE_SECURITY_REG(0, 4, APE);
DEFINE_SLAVE_SECURITY_REG(0, 3, PINMUX_AUX);
DEFINE_SLAVE_SECURITY_REG(0, 2, SATA_AUX);
DEFINE_SLAVE_SECURITY_REG(0, 1, MISC_REGS);
DEFINE_SLAVE_SECURITY_REG(1, 31, I2C6);
DEFINE_SLAVE_SECURITY_REG(1, 30, DVC);
DEFINE_SLAVE_SECURITY_REG(1, 29, I2C4);
DEFINE_SLAVE_SECURITY_REG(1, 28, I2C3);
DEFINE_SLAVE_SECURITY_REG(1, 27, I2C2);
DEFINE_SLAVE_SECURITY_REG(1, 26, I2C1);
DEFINE_SLAVE_SECURITY_REG(1, 25, SPI6);
DEFINE_SLAVE_SECURITY_REG(1, 24, SPI5);
DEFINE_SLAVE_SECURITY_REG(1, 23, SPI4);
DEFINE_SLAVE_SECURITY_REG(1, 22, SPI3);
DEFINE_SLAVE_SECURITY_REG(1, 21, SPI2);
DEFINE_SLAVE_SECURITY_REG(1, 20, SPI1);
DEFINE_SLAVE_SECURITY_REG(1, 15, UART_D);
DEFINE_SLAVE_SECURITY_REG(1, 14, UART_C);
DEFINE_SLAVE_SECURITY_REG(1, 13, UART_B);
DEFINE_SLAVE_SECURITY_REG(1, 12, UART_A);
DEFINE_SLAVE_SECURITY_REG(1, 11, EMCB);
DEFINE_SLAVE_SECURITY_REG(1, 10, MCB);
DEFINE_SLAVE_SECURITY_REG(1, 9, EMC1);
DEFINE_SLAVE_SECURITY_REG(1, 8, MC1);
DEFINE_SLAVE_SECURITY_REG(1, 5, EMC0);
DEFINE_SLAVE_SECURITY_REG(1, 4, MC0);
DEFINE_SLAVE_SECURITY_REG(2, 21, FEK);
DEFINE_SLAVE_SECURITY_REG(2, 20, PKA1);
DEFINE_SLAVE_SECURITY_REG(2, 19, SE2);
DEFINE_SLAVE_SECURITY_REG(2, 16, DVFS);
DEFINE_SLAVE_SECURITY_REG(2, 15, MIPI_CAL);
DEFINE_SLAVE_SECURITY_REG(2, 14, XUSB_PADCTL);
DEFINE_SLAVE_SECURITY_REG(2, 13, XUSB_DEV);
DEFINE_SLAVE_SECURITY_REG(2, 12, XUSB_HOST);
DEFINE_SLAVE_SECURITY_REG(2, 11, APB2JTAG);
DEFINE_SLAVE_SECURITY_REG(2, 10, SOC_THERM);
DEFINE_SLAVE_SECURITY_REG(2, 9, DP2);
DEFINE_SLAVE_SECURITY_REG(2, 8, DDS);
DEFINE_SLAVE_SECURITY_REG(2, 7, MIPIBIF);
DEFINE_SLAVE_SECURITY_REG(2, 3, SDMMC4);
DEFINE_SLAVE_SECURITY_REG(2, 2, SDMMC3);
DEFINE_SLAVE_SECURITY_REG(2, 1, SDMMC2);
DEFINE_SLAVE_SECURITY_REG(2, 0, SDMMC1);
#undef DEFINE_SLAVE_SECURITY_REG
#define SLAVE_SECURITY_REG_BITS_ENUM(RINDEX, NAME, ENUM) APB_MISC_REG_BITS_ENUM(SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG##RINDEX##_##NAME##_SECURITY_EN, ENUM)

View File

@@ -0,0 +1,40 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#define AVP_CACHE_ADDRESS(x) (0x50040000 + x)
#define AVP_CACHE_CONFIG (0x000)
#define AVP_CACHE_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (AVP_CACHE, NAME)
#define AVP_CACHE_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (AVP_CACHE, NAME, VALUE)
#define AVP_CACHE_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (AVP_CACHE, NAME, ENUM)
#define AVP_CACHE_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(AVP_CACHE, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
#define DEFINE_AVP_CACHE_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (AVP_CACHE, NAME, __OFFSET__, __WIDTH__)
#define DEFINE_AVP_CACHE_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (AVP_CACHE, NAME, __OFFSET__, ZERO, ONE)
#define DEFINE_AVP_CACHE_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (AVP_CACHE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
#define DEFINE_AVP_CACHE_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(AVP_CACHE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_AVP_CACHE_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (AVP_CACHE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
DEFINE_AVP_CACHE_REG_BIT_ENUM(DISABLE_WB, 10, FALSE, TRUE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(DISABLE_RB, 11, FALSE, TRUE);

View File

@@ -0,0 +1,302 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
/* Clock source enums. */
#define CLK_RST_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (CLK_RST_CONTROLLER, NAME)
#define CLK_RST_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (CLK_RST_CONTROLLER, NAME, VALUE)
#define CLK_RST_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (CLK_RST_CONTROLLER, NAME, ENUM)
#define CLK_RST_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(CLK_RST_CONTROLLER, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
#define DEFINE_CLK_RST_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (CLK_RST_CONTROLLER, NAME, __OFFSET__, __WIDTH__)
#define DEFINE_CLK_RST_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE)
#define DEFINE_CLK_RST_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
#define DEFINE_CLK_RST_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_CLK_RST_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
#define CLK_RST_CONTROLLER_RST_SOURCE (0x000)
#define CLK_RST_CONTROLLER_MISC_CLK_ENB (0x048)
#define CLK_RST_CONTROLLER_OSC_CTRL (0x050)
#define CLK_RST_CONTROLLER_PLLD_BASE (0x0D0)
#define CLK_RST_CONTROLLER_PLLD_MISC1 (0x0D8)
#define CLK_RST_CONTROLLER_PLLD_MISC (0x0DC)
#define CLK_RST_CONTROLLER_PLLX_BASE (0x0E0)
#define CLK_RST_CONTROLLER_PLLX_MISC (0x0E4)
#define CLK_RST_CONTROLLER_CCLKG_BURST_POLICY (0x368)
#define CLK_RST_CONTROLLER_SUPER_CCLKG_DIVIDER (0x36C)
#define CLK_RST_CONTROLLER_CCLKLP_BURST_POLICY (0x370)
#define CLK_RST_CONTROLLER_SUPER_CCLKLP_DIVIDER (0x374)
#define CLK_RST_CONTROLLER_CPU_SOFTRST_CTRL2 (0x388)
#define CLK_RST_CONTROLLER_SPARE_REG0 (0x55C)
#define CLK_RST_CONTROLLER_PLLC4_BASE (0x5A4)
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRA (0x0F8)
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRB (0x0FC)
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRC (0x3A0)
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD (0x3A4)
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRE (0x554)
DEFINE_CLK_RST_REG(MISC_CLK_ENB_CFG_ALL_VISIBLE, 28, 1);
DEFINE_CLK_RST_REG_BIT_ENUM(OSC_CTRL_XOE, 0, DISABLE, ENABLE);
DEFINE_CLK_RST_REG(OSC_CTRL_XOFS, 4, 6);
DEFINE_CLK_RST_REG_BIT_ENUM(PLLX_BASE_PLLX_ENABLE, 30, DISABLE, ENABLE);
DEFINE_CLK_RST_REG(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVISOR, 0, 8);
DEFINE_CLK_RST_REG(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVIDEND, 8, 8);
DEFINE_CLK_RST_REG_BIT_ENUM(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_IRQ, 24, NO_IMPACT, DISABLE);
DEFINE_CLK_RST_REG_BIT_ENUM(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_IRQ, 25, NO_IMPACT, DISABLE);
DEFINE_CLK_RST_REG_BIT_ENUM(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_FIQ, 26, NO_IMPACT, DISABLE);
DEFINE_CLK_RST_REG_BIT_ENUM(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_FIQ, 27, NO_IMPACT, DISABLE);
DEFINE_CLK_RST_REG_BIT_ENUM(SUPER_CCLKG_DIVIDER_SUPER_CDIV_ENB, 31, DISABLE, ENABLE);
DEFINE_CLK_RST_REG_BIT_ENUM(SUPER_CCLKLP_DIVIDER_SUPER_CDIV_ENB, 31, DISABLE, ENABLE);
DEFINE_CLK_RST_REG_FOUR_BIT_ENUM(CCLK_BURST_POLICY_CWAKEUP_IDLE_SOURCE, 0, CLKM, RSVD1, CLKS, RSVD3, PLLP_OUT0, PLLP_OUT4, RSVD6, RSVD7, PLLX_OUT0_LJ, DVFS_CPU_CLK, RSVD10, RSVD11, RSVD12, RSVD13, PLLX_OUT0, DVFS_CPU_CLK_LJ);
DEFINE_CLK_RST_REG_FOUR_BIT_ENUM(CCLK_BURST_POLICY_CWAKEUP_RUN_SOURCE, 4, CLKM, RSVD1, CLKS, RSVD3, PLLP_OUT0, PLLP_OUT4, RSVD6, RSVD7, PLLX_OUT0_LJ, DVFS_CPU_CLK, RSVD10, RSVD11, RSVD12, RSVD13, PLLX_OUT0, DVFS_CPU_CLK_LJ);
DEFINE_CLK_RST_REG_FOUR_BIT_ENUM(CCLK_BURST_POLICY_CWAKEUP_IRQ_SOURCE, 8, CLKM, RSVD1, CLKS, RSVD3, PLLP_OUT0, PLLP_OUT4, RSVD6, RSVD7, PLLX_OUT0_LJ, DVFS_CPU_CLK, RSVD10, RSVD11, RSVD12, RSVD13, PLLX_OUT0, DVFS_CPU_CLK_LJ);
DEFINE_CLK_RST_REG_FOUR_BIT_ENUM(CCLK_BURST_POLICY_CWAKEUP_FIQ_SOURCE, 12, CLKM, RSVD1, CLKS, RSVD3, PLLP_OUT0, PLLP_OUT4, RSVD6, RSVD7, PLLX_OUT0_LJ, DVFS_CPU_CLK, RSVD10, RSVD11, RSVD12, RSVD13, PLLX_OUT0, DVFS_CPU_CLK_LJ);
DEFINE_CLK_RST_REG_FOUR_BIT_ENUM(CCLK_BURST_POLICY_CPU_STATE, 28, STDBY, IDLE, RUN, RSVD3, IRQ, RSVD5, RSVD6, RSVD7, FIQ, RSVD9, RSVD10, RSVD11, RSVD12, RSVD13, RSVD14, RSVD15);
DEFINE_CLK_RST_REG(CPU_SOFTRST_CTRL2_CAR2PMC_CPU_ACK_WIDTH, 0, 12);
DEFINE_CLK_RST_REG_TWO_BIT_ENUM(SPARE_REG0_CLK_M_DIVISOR, 2, CLK_M_DIVISOR1, CLK_M_DIVISOR2, CLK_M_DIVISOR3, CLK_M_DIVISOR4);
DEFINE_CLK_RST_REG_BIT_ENUM(PLLC4_BASE_PLLC4_IDDQ, 18, OFF, ON);
DEFINE_CLK_RST_REG_BIT_ENUM(PLLC4_BASE_PLLC4_LOCK, 27, NOT_LOCK, LOCK_FEQ_AND_PHASE);
DEFINE_CLK_RST_REG_BIT_ENUM(PLLC4_BASE_PLLC4_ENABLE, 30, DISABLE, ENABLE);
/* RST_DEVICES */
#define CLK_RST_CONTROLLER_RST_DEVICES_L (0x004)
#define CLK_RST_CONTROLLER_RST_DEVICES_H (0x008)
#define CLK_RST_CONTROLLER_RST_DEVICES_U (0x00C)
#define CLK_RST_CONTROLLER_RST_DEVICES_X (0x28C)
#define CLK_RST_CONTROLLER_RST_DEVICES_Y (0x2A4)
#define CLK_RST_CONTROLLER_RST_DEVICES_V (0x358)
#define CLK_RST_CONTROLLER_RST_DEVICES_W (0x35C)
/* CLK_OUT_ENB */
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_L (0x010)
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_H (0x014)
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_U (0x018)
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_X (0x280)
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_Y (0x298)
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_V (0x360)
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_W (0x364)
/* CLK_SOURCE */
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C1 (0x124)
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C5 (0x128)
#define CLK_RST_CONTROLLER_CLK_SOURCE_DISP1 (0x138)
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1 (0x150)
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2 (0x154)
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4 (0x164)
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA (0x178)
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB (0x17C)
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0)
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3 (0x1BC)
#define CLK_RST_CONTROLLER_CLK_SOURCE_CSITE (0x1D4)
#define CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT (0x3B4)
#define CLK_RST_CONTROLLER_CLK_SOURCE_ACTMON (0x3E8)
#define CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP (0x620)
#define CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_REF (0x62C)
#define CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_SOC (0x630)
#define CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL (0x66C)
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM (0x694)
/* RST_DEV_*_SET */
#define CLK_RST_CONTROLLER_RST_DEV_L_SET (0x300)
#define CLK_RST_CONTROLLER_RST_DEV_H_SET (0x308)
#define CLK_RST_CONTROLLER_RST_DEV_U_SET (0x310)
#define CLK_RST_CONTROLLER_RST_DEV_V_SET (0x430)
/* RST_DEV_*_CLR */
#define CLK_RST_CONTROLLER_RST_DEV_L_CLR (0x304)
#define CLK_RST_CONTROLLER_RST_DEV_H_CLR (0x30C)
#define CLK_RST_CONTROLLER_RST_DEV_U_CLR (0x314)
#define CLK_RST_CONTROLLER_RST_DEV_V_CLR (0x434)
/* CLK_ENB_*_SET */
#define CLK_RST_CONTROLLER_CLK_ENB_L_SET (0x320)
#define CLK_RST_CONTROLLER_CLK_ENB_H_SET (0x328)
#define CLK_RST_CONTROLLER_CLK_ENB_U_SET (0x330)
#define CLK_RST_CONTROLLER_CLK_ENB_V_SET (0x440)
#define CLK_RST_CONTROLLER_CLK_ENB_W_SET (0x448)
#define CLK_RST_CONTROLLER_CLK_ENB_X_SET (0x284)
#define CLK_RST_CONTROLLER_CLK_ENB_Y_SET (0x29C)
/* CLK_ENB_*_CLR */
#define CLK_RST_CONTROLLER_CLK_ENB_L_CLR (0x324)
#define CLK_RST_CONTROLLER_CLK_ENB_H_CLR (0x32C)
#define CLK_RST_CONTROLLER_CLK_ENB_U_CLR (0x334)
#define CLK_RST_CONTROLLER_CLK_ENB_X_CLR (0x288)
#define CLK_RST_CONTROLLER_CLK_ENB_Y_CLR (0x2A0)
#define CLK_RST_CONTROLLER_CLK_ENB_V_CLR (0x444)
#define CLK_RST_CONTROLLER_CLK_ENB_W_CLR (0x44C)
/* CLK_ENB_*_INDEX */
#define CLK_RST_CONTROLLER_CLK_ENB_I2C1_INDEX (0x0C)
#define CLK_RST_CONTROLLER_CLK_ENB_I2C5_INDEX (0x0F)
#define CLK_RST_CONTROLLER_CLK_ENB_UARTA_INDEX (0x06)
#define CLK_RST_CONTROLLER_CLK_ENB_UARTB_INDEX (0x07)
#define CLK_RST_CONTROLLER_CLK_ENB_UARTC_INDEX (0x17)
#define CLK_RST_CONTROLLER_CLK_ENB_ACTMON_INDEX (0x17)
/* RST_CPUG_CMPLX_* */
#define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET (0x450)
#define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR (0x454)
DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_ARC_CLK_OVR_ON, 19, OFF, ON);
DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_TSEC_CLK_OVR_ON, 20, OFF, ON);
DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_TSECB_CLK_OVR_ON, 21, OFF, ON);
DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_ISPB_CLK_OVR_ON, 22, OFF, ON);
DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_TZRAM_CLK_OVR_ON, 23, OFF, ON);
DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_QSPI_CLK_OVR_ON, 24, OFF, ON);
DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_A9AVP_CLK_OVR_ON, 26, OFF, ON);
DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_MPCORE_MSELECT_CLK_OVR_ON, 27, OFF, ON);
DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC1_LEGACY_TMCLK_OVR_ON, 28, OFF, ON);
DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC2_LEGACY_TMCLK_OVR_ON, 29, OFF, ON);
DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC3_LEGACY_TMCLK_OVR_ON, 30, OFF, ON);
DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC4_LEGACY_TMCLK_OVR_ON, 31, OFF, ON);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_I2C1_I2C1_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
DEFINE_CLK_RST_REG(CLK_SOURCE_I2C1_I2C1_CLK_DIVISOR, 0, 8);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_I2C5_I2C5_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
DEFINE_CLK_RST_REG(CLK_SOURCE_I2C5_I2C5_CLK_DIVISOR, 0, 8);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMC1_SDMMC1_CLK_SRC, 29, PLLP_OUT0, PLLA_OUT, PLLC_OUT0, PLLC4_OUT2, PLLM_OUT0, PLLE_OUT0, CLK_M, PLLC4_OUT0);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMC2_SDMMC2_CLK_SRC, 29, PLLP_OUT0, PLLC4_OUT2_LJ, PLLC4_OUT0_LJ, PLLC4_OUT2, PLLC4_OUT1, PLLC4_OUT1_LJ, CLK_M, PLLC4_OUT0);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMC4_SDMMC4_CLK_SRC, 29, PLLP_OUT0, PLLC4_OUT2_LJ, PLLC4_OUT0_LJ, PLLC4_OUT2, PLLC4_OUT1, PLLC4_OUT1_LJ, CLK_M, PLLC4_OUT0);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMC3_SDMMC3_CLK_SRC, 29, PLLP_OUT0, PLLA_OUT, PLLC_OUT0, PLLC4_OUT2, PLLC4_OUT1, PLLE_OUT0, CLK_M, PLLC4_OUT0);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, 29, PLLP_OUT0, _RSVD1_, _RSVD2_, PLLC4_OUT2, _RSVD4_, _RSVD5_, CLK_M, PLLC4_OUT0);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMC24_SDMMC24_CLK_SRC, 29, PLLP_OUT0, PLLC4_OUT2_LJ, PLLC4_OUT0_LJ, PLLC4_OUT2, PLLC4_OUT1, PLLC4_OUT1_LJ, CLK_M, PLLC4_OUT0);
DEFINE_CLK_RST_REG(CLK_SOURCE_SDMMC1_SDMMC1_CLK_DIVISOR, 0, 8);
DEFINE_CLK_RST_REG(CLK_SOURCE_SDMMC2_SDMMC2_CLK_DIVISOR, 0, 8);
DEFINE_CLK_RST_REG(CLK_SOURCE_SDMMC4_SDMMC4_CLK_DIVISOR, 0, 8);
DEFINE_CLK_RST_REG(CLK_SOURCE_SDMMC3_SDMMC3_CLK_DIVISOR, 0, 8);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTA_UARTA_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTB_UARTB_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTC_UARTC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_MSELECT_MSELECT_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT2, PLLC4_OUT1, CLK_S, CLK_M, PLLC4_OUT0);
DEFINE_CLK_RST_REG(CLK_SOURCE_MSELECT_MSELECT_CLK_DIVISOR, 0, 8);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_ACTMON_ACTMON_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, CLK_S, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
DEFINE_CLK_RST_REG(CLK_SOURCE_DVFS_REF_DVFS_REF_DIVISOR, 0, 8);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_DVFS_REF_DVFS_REF_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
DEFINE_CLK_RST_REG(CLK_SOURCE_DVFS_SOC_DVFS_SOC_DIVISOR, 0, 8);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_DVFS_SOC_DVFS_SOC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
DEFINE_CLK_RST_REG(CLK_SOURCE_LEGACY_TM_CLK_DIVISOR, 0, 8);
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_LEGACY_TM_CLK_SRC, 29, PLLP_OUT3, PLLC_OUT0, PLLC2_OUT0, CLK_M, PLLP_OUT0, PLLC4_OUT0, PLLC4_OUT1, PLLC4_OUT2);
DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_L_SET_SET_COP_RST, 1, DISABLE, ENABLE);
DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_L_CLR_CLR_COP_RST, 1, DISABLE, ENABLE);
DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_CPURESET0, 0, DISABLE, ENABLE);
DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_CPURESET1, 1, DISABLE, ENABLE);
DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_CPURESET2, 2, DISABLE, ENABLE);
DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_CPURESET3, 3, DISABLE, ENABLE);
DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_CORERESET0, 16, DISABLE, ENABLE);
DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_CORERESET1, 17, DISABLE, ENABLE);
DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_CORERESET2, 18, DISABLE, ENABLE);
DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_CORERESET3, 19, DISABLE, ENABLE);
DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_NONCPURESET, 29, DISABLE, ENABLE);
/* TODO: Actually include all devices. */
#define CLK_RST_FOREACH_DEVICE(HANDLER) \
HANDLER(L, CPU, 0, 0) \
HANDLER(L, RTC, 0, 4) \
HANDLER(L, TMR, 0, 5) \
HANDLER(L, GPIO, 0, 8) \
HANDLER(L, SDMMC2, 0, 9) \
HANDLER(L, SDMMC1, 0, 14) \
HANDLER(L, SDMMC4, 0, 15) \
HANDLER(L, USBD, 0, 22) \
HANDLER(L, CACHE2, 0, 31) \
HANDLER(H, MEM, 1, 0) \
HANDLER(H, AHBDMA, 1, 1) \
HANDLER(H, APBDMA, 1, 2) \
HANDLER(H, PMC, 1, 6) \
HANDLER(H, FUSE, 1, 7) \
HANDLER(H, KFUSE, 1, 8) \
HANDLER(H, I2C5, 1, 15) \
HANDLER(H, EMC, 1, 25) \
HANDLER(H, USB2, 1, 26) \
HANDLER(U, SDMMC3, 2, 5) \
HANDLER(U, CSITE, 2, 9) \
HANDLER(U, IRAMA, 2, 20) \
HANDLER(U, IRAMB, 2, 21) \
HANDLER(U, IRAMC, 2, 22) \
HANDLER(U, IRAMD, 2, 23) \
HANDLER(U, CRAM2, 2, 24) \
HANDLER(V, CPUG, 3, 0) \
HANDLER(V, MSELECT, 3, 3) \
HANDLER(V, SPDIF_DOUBLER, 3, 22) \
HANDLER(V, ACTMON, 3, 23) \
HANDLER(V, TZRAM, 3, 30) \
HANDLER(V, SE, 3, 31) \
HANDLER(W, PCIERX0, 4, 2) \
HANDLER(W, PCIERX1, 4, 3) \
HANDLER(W, PCIERX2, 4, 4) \
HANDLER(W, PCIERX3, 4, 5) \
HANDLER(W, PCIERX4, 4, 6) \
HANDLER(W, PCIERX5, 4, 7) \
HANDLER(W, ENTROPY, 4, 21) \
HANDLER(W, DVFS, 4, 27) \
HANDLER(W, MC1, 4, 30) \
HANDLER(X, MC_CAPA, 5, 7) \
HANDLER(X, MC_CBPA, 5, 8) \
HANDLER(X, MC_CPU, 5, 9) \
HANDLER(X, MC_BBC, 5, 10) \
HANDLER(X, EMC_DLL, 5, 14) \
HANDLER(X, GPU, 5, 24) \
HANDLER(X, DBGAPB, 5, 25) \
HANDLER(X, PLLG_REF, 5, 29) \
HANDLER(Y, LEGACY_TM, 6, 1) \
HANDLER(Y, MC_CCPA, 6, 8) \
HANDLER(Y, MC_CDPA, 6, 9) \
HANDLER(Y, PLLP_OUT_CPU, 6, 31)
#define CLK_RST_DEFINE_SET_CLR_REG(REGISTER, DEVICE, REGISTER_INDEX, DEVICE_INDEX) \
DEFINE_CLK_RST_REG_BIT_ENUM(CLK_ENB_##REGISTER##_SET_SET_CLK_ENB_##DEVICE, DEVICE_INDEX, DISABLE, ENABLE); \
DEFINE_CLK_RST_REG_BIT_ENUM(CLK_ENB_##REGISTER##_CLR_CLR_CLK_ENB_##DEVICE, DEVICE_INDEX, DISABLE, ENABLE); \
DEFINE_CLK_RST_REG_BIT_ENUM(CLK_OUT_ENB_##REGISTER##_CLK_ENB_##DEVICE, DEVICE_INDEX, DISABLE, ENABLE); \
DEFINE_CLK_RST_REG_BIT_ENUM(CLK_ENB_##REGISTER##_CLK_ENB_##DEVICE, DEVICE_INDEX, DISABLE, ENABLE); \
DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_##REGISTER##_SET_SET_##DEVICE##_RST, DEVICE_INDEX, DISABLE, ENABLE); \
DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_##REGISTER##_CLR_CLR_##DEVICE##_RST, DEVICE_INDEX, DISABLE, ENABLE); \
DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_##REGISTER##_##DEVICE##_RST, DEVICE_INDEX, DISABLE, ENABLE); \
DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEVICES_##REGISTER##_SWR_##DEVICE##_RST, DEVICE_INDEX, DISABLE, ENABLE);
CLK_RST_FOREACH_DEVICE(CLK_RST_DEFINE_SET_CLR_REG)
#undef CLK_RST_DEFINE_SET_CLR_REG

View File

@@ -0,0 +1,122 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#define EMC_ADDRESS(x) (0x7001B000 + x)
#define EMC0_ADDRESS(x) (0x7001E000 + x)
#define EMC1_ADDRESS(x) (0x7001F000 + x)
#define EMC_CFG (0x00C)
#define EMC_ADR_CFG (0x010)
#define EMC_TIMING_CONTROL (0x028)
#define EMC_SELF_REF (0x0E0)
#define EMC_MRW (0x0E8)
#define EMC_FBIO_CFG5 (0x104)
#define EMC_MRW3 (0x138)
#define EMC_AUTO_CAL_CONFIG (0x2A4)
#define EMC_REQ_CTRL (0x2B0)
#define EMC_EMC_STATUS (0x2B4)
#define EMC_CFG_DIG_DLL (0x2BC)
#define EMC_ZCAL_INTERVAL (0x2E0)
#define EMC_PMC_SCRATCH3 (0x448)
#define EMC_FBIO_CFG7 (0x584)
#define EMC_PMACRO_CFG_PM_GLOBAL_0 (0xC30)
#define EMC_PMACRO_TRAINING_CTRL_0 (0xCF8)
#define EMC_PMACRO_TRAINING_CTRL_1 (0xCFC)
#define EMC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (EMC, NAME)
#define EMC_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (EMC, NAME, VALUE)
#define EMC_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (EMC, NAME, ENUM)
#define EMC_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(EMC, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
#define DEFINE_EMC_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (EMC, NAME, __OFFSET__, __WIDTH__)
#define DEFINE_EMC_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (EMC, NAME, __OFFSET__, ZERO, ONE)
#define DEFINE_EMC_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (EMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
#define DEFINE_EMC_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(EMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_EMC_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (EMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
DEFINE_EMC_REG_BIT_ENUM(CFG_DYN_SELF_REF, 28, DISABLED, ENABLED);
DEFINE_EMC_REG_BIT_ENUM(CFG_DRAM_ACPD, 29, NO_POWERDOWN, ACTIVE_POWERDOWN);
DEFINE_EMC_REG_BIT_ENUM(ADR_CFG_EMEM_NUMDEV, 0, N1, N2);
DEFINE_EMC_REG_BIT_ENUM(TIMING_CONTROL_TIMING_UPDATE, 0, DISABLED, ENABLED);
DEFINE_EMC_REG_BIT_ENUM(SELF_REF_SELF_REF_CMD, 0, DISABLED, ENABLED);
DEFINE_EMC_REG_BIT_ENUM(SELF_REF_ACTIVE_SELF_REF, 8, DISABLED, ENABLED);
DEFINE_EMC_REG_TWO_BIT_ENUM(SELF_REF_SREF_DEV_SELECTN, 30, BOTH, DEV1, DEV0, RESERVED);
DEFINE_EMC_REG(MRW_OP, 0, 8);
DEFINE_EMC_REG(MRW_MA, 16, 8);
DEFINE_EMC_REG_TWO_BIT_ENUM(MRW_CNT, 26, SHORT, LONG, EXT1, EXT2);
DEFINE_EMC_REG_TWO_BIT_ENUM(MRW_DEV_SELECTN, 30, BOTH, DEV1, DEV0, RESERVED);
DEFINE_EMC_REG_TWO_BIT_ENUM(FBIO_CFG5_DRAM_TYPE, 0, DDR4, LPDDR4, LPDDR2, DDR2);
DEFINE_EMC_REG_BIT_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_MEASURE_STALL, 9, DISABLE, ENABLE);
DEFINE_EMC_REG_BIT_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_UPDATE_STALL, 10, DISABLE, ENABLE);
DEFINE_EMC_REG_BIT_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_START, 31, DISABLE, ENABLE);
DEFINE_EMC_REG(REQ_CTRL_STALL_ALL_READS, 0, 1);
DEFINE_EMC_REG(REQ_CTRL_STALL_ALL_WRITES, 1, 1);
DEFINE_EMC_REG_TWO_BIT_ENUM(EMC_STATUS_DRAM_IN_SELF_REFRESH, 8, DISABLED, DEV0_ENABLED, DEV1_ENABLED, BOTH_ENABLED);
DEFINE_EMC_REG_BIT_ENUM(EMC_STATUS_DRAM_DEV0_IN_SELF_REFRESH, 8, DISABLED, ENABLED);
DEFINE_EMC_REG_BIT_ENUM(EMC_STATUS_NO_OUTSTANDING_TRANSACTIONS, 2, WAITING, COMPLETED);
DEFINE_EMC_REG_BIT_ENUM(EMC_STATUS_TIMING_UPDATE_STALLED, 23, DONE, BUSY);
DEFINE_EMC_REG_BIT_ENUM(CFG_DIG_DLL_CFG_DLL_EN, 0, DISABLED, ENABLED);
DEFINE_EMC_REG(ZCAL_INTERVAL_LO, 0, 10);
DEFINE_EMC_REG(ZCAL_INTERVAL_HI, 10, 14);
DEFINE_EMC_REG(PMC_SCRATCH3_DDR_CNTRL, 0, 19);
DEFINE_EMC_REG_BIT_ENUM(PMC_SCRATCH3_WEAK_BIAS, 30, DISABLED, ENABLED);
DEFINE_EMC_REG_BIT_ENUM(FBIO_CFG7_CH1_ENABLE, 2, DISABLE, ENABLE);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE0, 16, DISABLE, ENABLE);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE1, 17, DISABLE, ENABLE);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE2, 18, DISABLE, ENABLE);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE3, 19, DISABLE, ENABLE);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE4, 20, DISABLE, ENABLE);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE5, 21, DISABLE, ENABLE);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE6, 22, DISABLE, ENABLE);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE7, 23, DISABLE, ENABLE);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD0, 24, DISABLE, ENABLE);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD1, 25, DISABLE, ENABLE);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD2, 26, DISABLE, ENABLE);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD3, 27, DISABLE, ENABLE);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_ENABLED, 0, DISABLED, ENABLED);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_TRAIN_QPOP, 1, DISABLED, ENABLED);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_RX_E_DIRECT_ZI, 2, DISABLED, ENABLED);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_E_WRPTR, 3, DISABLED, ENABLED);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_DRV_DQS, 4, DISABLED, ENABLED);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_ENABLED, 0, DISABLED, ENABLED);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_TRAIN_QPOP, 1, DISABLED, ENABLED);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_RX_E_DIRECT_ZI, 2, DISABLED, ENABLED);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_E_WRPTR, 3, DISABLED, ENABLED);
DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_DRV_DQS, 4, DISABLED, ENABLED);

View File

@@ -0,0 +1,33 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#define EVP_CPU_RESET_VECTOR (0x100)
#define EVP_COP_RESET_VECTOR (0x200)
#define EVP_COP_UNDEF_VECTOR (0x204)
#define EVP_COP_SWI_VECTOR (0x208)
#define EVP_COP_PREFETCH_ABORT_VECTOR (0x20C)
#define EVP_COP_DATA_ABORT_VECTOR (0x210)
#define EVP_COP_RSVD_VECTOR (0x214)
#define EVP_COP_IRQ_VECTOR (0x218)
#define EVP_COP_FIQ_VECTOR (0x21C)

View File

@@ -0,0 +1,83 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#define FLOW_CTLR_RAM_REPAIR (0x040)
#define FLOW_CTLR_FLOW_DBG_QUAL (0x050)
#define FLOW_CTLR_CC4_HVC_CONTROL (0x060)
#define FLOW_CTLR_CC4_RETENTION_CONTROL (0x064)
#define FLOW_CTLR_CC4_HVC_RETRY (0x08C)
#define FLOW_CTLR_L2FLUSH_CONTROL (0x094)
#define FLOW_CTLR_BPMP_CLUSTER_CONTROL (0x098)
#define FLOW_CTLR_CPU0_CSR (0x008)
#define FLOW_CTLR_CPU1_CSR (0x018)
#define FLOW_CTLR_CPU2_CSR (0x020)
#define FLOW_CTLR_CPU3_CSR (0x028)
#define FLOW_CTLR_HALT_CPU0_EVENTS (0x000)
#define FLOW_CTLR_HALT_CPU1_EVENTS (0x014)
#define FLOW_CTLR_HALT_CPU2_EVENTS (0x01C)
#define FLOW_CTLR_HALT_CPU3_EVENTS (0x024)
#define FLOW_CTLR_HALT_COP_EVENTS (0x004)
#define FLOW_CTLR_CC4_CORE0_CTRL (0x06C)
#define FLOW_CTLR_CC4_CORE1_CTRL (0x070)
#define FLOW_CTLR_CC4_CORE2_CTRL (0x074)
#define FLOW_CTLR_CC4_CORE3_CTRL (0x078)
#define FLOW_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (FLOW_CTLR, NAME)
#define FLOW_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (FLOW_CTLR, NAME, VALUE)
#define FLOW_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (FLOW_CTLR, NAME, ENUM)
#define FLOW_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(FLOW_CTLR, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
#define DEFINE_FLOW_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (FLOW_CTLR, NAME, __OFFSET__, __WIDTH__)
#define DEFINE_FLOW_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (FLOW_CTLR, NAME, __OFFSET__, ZERO, ONE)
#define DEFINE_FLOW_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (FLOW_CTLR, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
#define DEFINE_FLOW_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(FLOW_CTLR, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_FLOW_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (FLOW_CTLR, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
DEFINE_FLOW_REG_BIT_ENUM(CPUN_CSR_ENABLE, 0, DISABLE, ENABLE);
DEFINE_FLOW_REG(CPUN_CSR_WAIT_WFI_BITMAP, 8, 4);
DEFINE_FLOW_REG_TWO_BIT_ENUM(CPUN_CSR_ENABLE_EXT, 12, POWERGATE_CPU_ONLY, POWERGATE_BOTH_CPU_NONCPU, POWERGATE_CPU_TURNOFF_CPURAIL, PG_EMULATION);
DEFINE_FLOW_REG_BIT_ENUM(CPUN_CSR_EVENT_FLAG, 14, FALSE, TRUE);
DEFINE_FLOW_REG_BIT_ENUM(CPUN_CSR_INTR_FLAG, 15, FALSE, TRUE);
DEFINE_FLOW_REG_BIT_ENUM(HALT_CPUN_EVENTS_GIC_FIQN, 8, DISABLE, ENABLE);
DEFINE_FLOW_REG_BIT_ENUM(HALT_CPUN_EVENTS_GIC_IRQN, 9, DISABLE, ENABLE);
DEFINE_FLOW_REG_BIT_ENUM(HALT_CPUN_EVENTS_LIC_FIQN, 10, DISABLE, ENABLE);
DEFINE_FLOW_REG_BIT_ENUM(HALT_CPUN_EVENTS_LIC_IRQN, 11, DISABLE, ENABLE);
DEFINE_FLOW_REG_THREE_BIT_ENUM(HALT_CPUN_EVENTS_FLOW_MODE, 29, NONE, RUN_AND_INT, WAITEVENT, WAITEVENT_AND_INT, STOP_UNTIL_IRQ, STOP_UNTIL_EVENT_AND_IRQ, RESERVED6, RESERVED7);
DEFINE_FLOW_REG_BIT_ENUM(HALT_COP_EVENTS_JTAG, 28, DISABLED, ENABLED);
DEFINE_FLOW_REG_THREE_BIT_ENUM(HALT_COP_EVENTS_MODE, 29, FLOW_MODE_NONE, FLOW_MODE_RUN_AND_INT, FLOW_MODE_STOP, FLOW_MODE_STOP_AND_INT, FLOW_MODE_STOP_UNTIL_IRQ, FLOW_MODE_STOP_UNTIL_IRQ_AND_INT, FLOW_MODE_STOP_UNTIL_EVENT_AND_IRQ, RESERVED7);
DEFINE_FLOW_REG_BIT_ENUM(FLOW_DBG_QUAL_FIQ2CCPLEX_ENABLE, 28, DISABLE, ENABLE);
DEFINE_FLOW_REG_BIT_ENUM(RAM_REPAIR_REQ, 0, DISABLE, ENABLE);
DEFINE_FLOW_REG_BIT_ENUM(RAM_REPAIR_STS, 1, REQUESTED, DONE);
DEFINE_FLOW_REG_BIT_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER, 0, FAST, SLOW);
DEFINE_FLOW_REG_BIT_ENUM(BPMP_CLUSTER_CONTROL_CLUSTER_SWITCH_ENABLE, 1, DISABLE, ENABLE);
DEFINE_FLOW_REG_BIT_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER_LOCK, 2, DISABLE, ENABLE);

View File

@@ -0,0 +1,32 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#define PRI_ICTLR(n) (0x60004000 + n)
#define SEC_ICTLR(n) (0x60004100 + n)
#define TRI_ICTLR(n) (0x60004200 + n)
#define QUAD_ICTLR(n) (0x60004300 + n)
#define PENTA_ICTLR(n) (0x60004400 + n)
#define HEXA_ICTLR(n) (0x60004500 + n)
#define ICTLR_COP_IER_CLR (0x038)

View File

@@ -0,0 +1,555 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#define MC_INTSTATUS (0x000)
#define MC_INTMASK (0x004)
#define MC_ERR_STATUS (0x008)
#define MC_ERR_ADR (0x00C)
#define MC_SMMU_CONFIG (0x010)
#define MC_SMMU_PTB_ASID (0x01C)
#define MC_SMMU_PTB_DATA (0x020)
#define MC_SMMU_TLB_FLUSH (0x030)
#define MC_SMMU_PTC_FLUSH_0 (0x034)
#define MC_EMEM_CFG (0x050)
#define MC_EMEM_ADR_CFG (0x054)
#define MC_EMEM_ARB_CFG (0x090)
#define MC_EMEM_ARB_OUTSTANDING_REQ (0x094)
#define MC_EMEM_ARB_TIMING_RCD (0x098)
#define MC_EMEM_ARB_TIMING_RP (0x09C)
#define MC_EMEM_ARB_TIMING_RC (0x0A0)
#define MC_EMEM_ARB_TIMING_RAS (0x0A4)
#define MC_EMEM_ARB_TIMING_FAW (0x0A8)
#define MC_EMEM_ARB_TIMING_RRD (0x0AC)
#define MC_EMEM_ARB_TIMING_RAP2PRE (0x0B0)
#define MC_EMEM_ARB_TIMING_WAP2PRE (0x0B4)
#define MC_EMEM_ARB_TIMING_R2R (0x0B8)
#define MC_EMEM_ARB_TIMING_W2W (0x0BC)
#define MC_EMEM_ARB_TIMING_R2W (0x0C0)
#define MC_EMEM_ARB_TIMING_W2R (0x0C4)
#define MC_EMEM_ARB_MISC2 (0x0C8)
#define MC_EMEM_ARB_DA_TURNS (0x0D0)
#define MC_EMEM_ARB_DA_COVERS (0x0D4)
#define MC_EMEM_ARB_MISC0 (0x0D8)
#define MC_EMEM_ARB_MISC1 (0x0DC)
#define MC_EMEM_ARB_RING1_THROTTLE (0x0E0)
#define MC_CLIENT_HOTRESET_CTRL (0x200)
#define MC_CLIENT_HOTRESET_STATUS (0x204)
#define MC_SMMU_AFI_ASID (0x238)
#define MC_SMMU_DC_ASID (0x240)
#define MC_SMMU_DCB_ASID (0x244)
#define MC_SMMU_HC_ASID (0x250)
#define MC_SMMU_HDA_ASID (0x254)
#define MC_SMMU_ISP2_ASID (0x258)
#define MC_SMMU_MSENC_NVENC_ASID (0x264)
#define MC_SMMU_NV_ASID (0x268)
#define MC_SMMU_NV2_ASID (0x26C)
#define MC_SMMU_PPCS_ASID (0x270)
#define MC_SMMU_SATA_ASID (0x274)
#define MC_SMMU_VI_ASID (0x280)
#define MC_SMMU_VIC_ASID (0x284)
#define MC_SMMU_XUSB_HOST_ASID (0x288)
#define MC_SMMU_XUSB_DEV_ASID (0x28C)
#define MC_SMMU_TSEC_ASID (0x294)
#define MC_LATENCY_ALLOWANCE_AVPC_0 (0x2E4)
#define MC_LATENCY_ALLOWANCE_DC_0 (0x2E8)
#define MC_LATENCY_ALLOWANCE_DC_1 (0x2EC)
#define MC_LATENCY_ALLOWANCE_DCB_0 (0x2F4)
#define MC_LATENCY_ALLOWANCE_DCB_1 (0x2F8)
#define MC_LATENCY_ALLOWANCE_HC_0 (0x310)
#define MC_LATENCY_ALLOWANCE_HC_1 (0x314)
#define MC_LATENCY_ALLOWANCE_MPCORE_0 (0x320)
#define MC_LATENCY_ALLOWANCE_NVENC_0 (0x328)
#define MC_LATENCY_ALLOWANCE_PPCS_0 (0x344)
#define MC_LATENCY_ALLOWANCE_PPCS_1 (0x348)
#define MC_LATENCY_ALLOWANCE_ISP2_0 (0x370)
#define MC_LATENCY_ALLOWANCE_ISP2_1 (0x374)
#define MC_LATENCY_ALLOWANCE_XUSB_0 (0x37C)
#define MC_LATENCY_ALLOWANCE_XUSB_1 (0x380)
#define MC_LATENCY_ALLOWANCE_TSEC_0 (0x390)
#define MC_LATENCY_ALLOWANCE_VIC_0 (0x394)
#define MC_LATENCY_ALLOWANCE_VI2_0 (0x398)
#define MC_LATENCY_ALLOWANCE_GPU_0 (0x3AC)
#define MC_LATENCY_ALLOWANCE_SDMMCA_0 (0x3B8)
#define MC_LATENCY_ALLOWANCE_SDMMCAA_0 (0x3BC)
#define MC_LATENCY_ALLOWANCE_SDMMC_0 (0x3C0)
#define MC_LATENCY_ALLOWANCE_SDMMCAB_0 (0x3C4)
#define MC_LATENCY_ALLOWANCE_NVDEC_0 (0x3D8)
#define MC_LATENCY_ALLOWANCE_GPU2_0 (0x3E8)
#define MC_DIS_PTSA_RATE (0x41C)
#define MC_DIS_PTSA_MIN (0x420)
#define MC_DIS_PTSA_MAX (0x424)
#define MC_DISB_PTSA_RATE (0x428)
#define MC_DISB_PTSA_MIN (0x42C)
#define MC_DISB_PTSA_MAX (0x430)
#define MC_VE_PTSA_RATE (0x434)
#define MC_VE_PTSA_MIN (0x438)
#define MC_VE_PTSA_MAX (0x43C)
#define MC_MLL_MPCORER_PTSA_RATE (0x44C)
#define MC_RING1_PTSA_RATE (0x47C)
#define MC_RING1_PTSA_MIN (0x480)
#define MC_RING1_PTSA_MAX (0x484)
#define MC_PCX_PTSA_RATE (0x4AC)
#define MC_PCX_PTSA_MIN (0x4B0)
#define MC_PCX_PTSA_MAX (0x4B4)
#define MC_MSE_PTSA_RATE (0x4C4)
#define MC_MSE_PTSA_MIN (0x4C8)
#define MC_MSE_PTSA_MAX (0x4CC)
#define MC_AHB_PTSA_RATE (0x4DC)
#define MC_AHB_PTSA_MIN (0x4E0)
#define MC_AHB_PTSA_MAX (0x4E4)
#define MC_APB_PTSA_RATE (0x4E8)
#define MC_APB_PTSA_MIN (0x4EC)
#define MC_APB_PTSA_MAX (0x4F0)
#define MC_FTOP_PTSA_RATE (0x50C)
#define MC_HOST_PTSA_RATE (0x518)
#define MC_HOST_PTSA_MIN (0x51C)
#define MC_HOST_PTSA_MAX (0x520)
#define MC_USBX_PTSA_RATE (0x524)
#define MC_USBX_PTSA_MIN (0x528)
#define MC_USBX_PTSA_MAX (0x52C)
#define MC_USBD_PTSA_RATE (0x530)
#define MC_USBD_PTSA_MIN (0x534)
#define MC_USBD_PTSA_MAX (0x538)
#define MC_GK_PTSA_RATE (0x53C)
#define MC_GK_PTSA_MIN (0x540)
#define MC_GK_PTSA_MAX (0x544)
#define MC_AUD_PTSA_RATE (0x548)
#define MC_AUD_PTSA_MIN (0x54C)
#define MC_AUD_PTSA_MAX (0x550)
#define MC_VICPC_PTSA_RATE (0x554)
#define MC_VICPC_PTSA_MIN (0x558)
#define MC_VICPC_PTSA_MAX (0x55C)
#define MC_JPG_PTSA_RATE (0x584)
#define MC_JPG_PTSA_MIN (0x588)
#define MC_JPG_PTSA_MAX (0x58C)
#define MC_GK2_PTSA_RATE (0x610)
#define MC_GK2_PTSA_MIN (0x614)
#define MC_GK2_PTSA_MAX (0x618)
#define MC_SDM_PTSA_RATE (0x61C)
#define MC_SDM_PTSA_MIN (0x620)
#define MC_SDM_PTSA_MAX (0x624)
#define MC_HDAPC_PTSA_RATE (0x628)
#define MC_HDAPC_PTSA_MIN (0x62C)
#define MC_HDAPC_PTSA_MAX (0x630)
#define MC_SEC_CARVEOUT_BOM (0x670)
#define MC_SEC_CARVEOUT_SIZE_MB (0x674)
#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0A (0x690)
#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0AB (0x694)
#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0B (0x698)
#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0BB (0x69C)
#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0C (0x6A0)
#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0CB (0x6A4)
#define MC_EMEM_ARB_TIMING_RFCPB (0x6C0)
#define MC_EMEM_ARB_TIMING_CCDMW (0x6C4)
#define MC_EMEM_ARB_REFPB_HP_CTRL (0x6F0)
#define MC_EMEM_ARB_REFPB_BANK_CTRL (0x6F4)
#define MC_PTSA_GRANT_DECREMENT (0x960)
#define MC_CLIENT_HOTRESET_CTRL_1 (0x970)
#define MC_CLIENT_HOTRESET_STATUS_1 (0x974)
#define MC_SMMU_PTC_FLUSH_1 (0x9B8)
#define MC_SMMU_DC1_ASID (0xA88)
#define MC_SMMU_SDMMC1A_ASID (0xA94)
#define MC_SMMU_SDMMC2A_ASID (0xA98)
#define MC_SMMU_SDMMC3A_ASID (0xA9C)
#define MC_SMMU_SDMMC4A_ASID (0xAA0)
#define MC_SMMU_ISP2B_ASID (0xAA4)
#define MC_SMMU_GPU_ASID (0xAA8)
#define MC_SMMU_GPUB_ASID (0xAAC)
#define MC_SMMU_PPCS2_ASID (0xAB0)
#define MC_SMMU_NVDEC_ASID (0xAB4)
#define MC_SMMU_APE_ASID (0xAB8)
#define MC_SMMU_SE_ASID (0xABC)
#define MC_SMMU_NVJPG_ASID (0xAC0)
#define MC_SMMU_HC1_ASID (0xAC4)
#define MC_SMMU_SE1_ASID (0xAC8)
#define MC_SMMU_AXIAP_ASID (0xACC)
#define MC_SMMU_ETR_ASID (0xAD0)
#define MC_SMMU_TSECB_ASID (0xAD4)
#define MC_SMMU_TSEC1_ASID (0xAD8)
#define MC_SMMU_TSECB1_ASID (0xADC)
#define MC_SMMU_NVDEC1_ASID (0xAE0)
#define MC_EMEM_ARB_DHYST_CTRL (0xBCC)
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0 (0xBD0)
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1 (0xBD4)
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2 (0xBD8)
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3 (0xBDC)
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4 (0xBE0)
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5 (0xBE4)
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6 (0xBE8)
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7 (0xBEC)
#define MC_ERR_GENERALIZED_CARVEOUT_STATUS (0xC00)
#define MC_SMMU_TLB_CONFIG (0x014)
#define MC_SMMU_PTC_CONFIG (0x018)
#define MC_SMMU_AVPC_ASID (0x23C)
#define MC_SMMU_PPCS1_ASID (0x298)
#define MC_SECURITY_CFG0 (0x070)
#define MC_SECURITY_CFG1 (0x074)
#define MC_SECURITY_CFG3 (0x9BC)
#define MC_SMMU_TRANSLATION_ENABLE_0 (0x228)
#define MC_SMMU_TRANSLATION_ENABLE_1 (0x22C)
#define MC_SMMU_TRANSLATION_ENABLE_2 (0x230)
#define MC_SMMU_TRANSLATION_ENABLE_3 (0x234)
#define MC_SMMU_TRANSLATION_ENABLE_4 (0xB98)
#define MC_SMMU_ASID_SECURITY (0x038)
#define MC_SMMU_ASID_SECURITY_1 (0x03c)
#define MC_SMMU_ASID_SECURITY_2 (0x9e0)
#define MC_SMMU_ASID_SECURITY_3 (0x9e4)
#define MC_SMMU_ASID_SECURITY_4 (0x9e8)
#define MC_SMMU_ASID_SECURITY_5 (0x9ec)
#define MC_SMMU_ASID_SECURITY_6 (0x9f0)
#define MC_SMMU_ASID_SECURITY_7 (0x9f4)
#define MC_IRAM_BOM (0x65c)
#define MC_IRAM_TOM (0x660)
#define MC_IRAM_REG_CTRL (0x964)
#define MC_SEC_CARVEOUT_BOM (0x670)
#define MC_SEC_CARVEOUT_SIZE_MB (0x674)
#define MC_SEC_CARVEOUT_REG_CTRL (0x678)
#define MC_VIDEO_PROTECT_BOM (0x648)
#define MC_VIDEO_PROTECT_SIZE_MB (0x64c)
#define MC_VIDEO_PROTECT_REG_CTRL (0x650)
#define MC_VIDEO_PROTECT_GPU_OVERRIDE_0 (0x984)
#define MC_VIDEO_PROTECT_GPU_OVERRIDE_1 (0x988)
#define MC_MTS_CARVEOUT_BOM (0x9a0)
#define MC_MTS_CARVEOUT_SIZE_MB (0x9a4)
#define MC_MTS_CARVEOUT_ADR_HI (0x9a8)
#define MC_MTS_CARVEOUT_REG_CTRL (0x9ac)
#define MC_SECURITY_CARVEOUT1_CFG0 (0xc08)
#define MC_SECURITY_CARVEOUT1_BOM (0xc0c)
#define MC_SECURITY_CARVEOUT1_BOM_HI (0xc10)
#define MC_SECURITY_CARVEOUT1_SIZE_128KB (0xc14)
#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS0 (0xc18)
#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS1 (0xc1c)
#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS2 (0xc20)
#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS3 (0xc24)
#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS4 (0xc28)
#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS0 (0xc2c)
#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS1 (0xc30)
#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS2 (0xc34)
#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS3 (0xc38)
#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS4 (0xc3c)
#define MC_SECURITY_CARVEOUT2_CFG0 (0xc58)
#define MC_SECURITY_CARVEOUT2_BOM (0xc5c)
#define MC_SECURITY_CARVEOUT2_BOM_HI (0xc60)
#define MC_SECURITY_CARVEOUT2_SIZE_128KB (0xc64)
#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS0 (0xc68)
#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS1 (0xc6c)
#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS2 (0xc70)
#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS3 (0xc74)
#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS4 (0xc78)
#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS0 (0xc7c)
#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS1 (0xc80)
#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS2 (0xc84)
#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS3 (0xc88)
#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS4 (0xc8c)
#define MC_SECURITY_CARVEOUT3_CFG0 (0xca8)
#define MC_SECURITY_CARVEOUT3_BOM (0xcac)
#define MC_SECURITY_CARVEOUT3_BOM_HI (0xcb0)
#define MC_SECURITY_CARVEOUT3_SIZE_128KB (0xcb4)
#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS0 (0xcb8)
#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS1 (0xcbc)
#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS2 (0xcc0)
#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS3 (0xcc4)
#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS4 (0xcc8)
#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS0 (0xccc)
#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS1 (0xcd0)
#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS2 (0xcd4)
#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS3 (0xcd8)
#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS4 (0xcdc)
#define MC_SECURITY_CARVEOUT4_CFG0 (0xcf8)
#define MC_SECURITY_CARVEOUT4_BOM (0xcfc)
#define MC_SECURITY_CARVEOUT4_BOM_HI (0xd00)
#define MC_SECURITY_CARVEOUT4_SIZE_128KB (0xd04)
#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS0 (0xd08)
#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS1 (0xd0c)
#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS2 (0xd10)
#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS3 (0xd14)
#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS4 (0xd18)
#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS0 (0xd1c)
#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS1 (0xd20)
#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS2 (0xd24)
#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS3 (0xd28)
#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS4 (0xd2c)
#define MC_SECURITY_CARVEOUT5_CFG0 (0xd48)
#define MC_SECURITY_CARVEOUT5_BOM (0xd4c)
#define MC_SECURITY_CARVEOUT5_BOM_HI (0xd50)
#define MC_SECURITY_CARVEOUT5_SIZE_128KB (0xd54)
#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS0 (0xd58)
#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS1 (0xd5c)
#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS2 (0xd60)
#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS3 (0xd64)
#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS4 (0xd68)
#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS0 (0xd6c)
#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS1 (0xd70)
#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS2 (0xd74)
#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS3 (0xd78)
#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS4 (0xd7c)
#define MC_STAT_CONTROL (0x100)
#define MC_STAT_EMC_CLOCK_LIMIT (0x108)
#define MC_STAT_EMC_CLOCK_LIMIT_MSBS (0x10c)
#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_LO (0x118)
#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_HI (0x11c)
#define MC_STAT_EMC_FILTER_SET0_SPARE (0x124)
#define MC_STAT_EMC_FILTER_SET0_CLIENT_0 (0x128)
#define MC_STAT_EMC_FILTER_SET0_CLIENT_1 (0x12c)
#define MC_STAT_EMC_FILTER_SET0_CLIENT_2 (0x130)
#define MC_STAT_EMC_FILTER_SET0_CLIENT_3 (0x134)
#define MC_STAT_EMC_SET0_COUNT (0x138)
#define MC_STAT_EMC_SET0_COUNT_MSBS (0x13c)
#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_LO (0x158)
#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_HI (0x15c)
#define MC_STAT_EMC_FILTER_SET1_SPARE (0x164)
#define MC_STAT_EMC_FILTER_SET1_CLIENT_0 (0x168)
#define MC_STAT_EMC_FILTER_SET1_CLIENT_1 (0x16c)
#define MC_STAT_EMC_FILTER_SET1_CLIENT_2 (0x170)
#define MC_STAT_EMC_FILTER_SET1_CLIENT_3 (0x174)
#define MC_STAT_EMC_SET1_COUNT (0x178)
#define MC_STAT_EMC_SET1_COUNT_MSBS (0x17c)
#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_UPPER (0xa20)
#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_UPPER (0xa24)
#define MC_STAT_EMC_FILTER_SET0_CLIENT_4 (0xb88)
#define MC_STAT_EMC_FILTER_SET1_CLIENT_4 (0xb8c)
#define MC_STAT_EMC_FILTER_SET0_CLIENT_5 (0xbc4)
#define MC_STAT_EMC_FILTER_SET1_CLIENT_5 (0xbc8)
#define MC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (MC, NAME)
#define MC_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (MC, NAME, VALUE)
#define MC_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (MC, NAME, ENUM)
#define MC_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(MC, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
#define DEFINE_MC_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (MC, NAME, __OFFSET__, __WIDTH__)
#define DEFINE_MC_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (MC, NAME, __OFFSET__, ZERO, ONE)
#define DEFINE_MC_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (MC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
#define DEFINE_MC_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(MC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_MC_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (MC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
DEFINE_MC_REG_BIT_ENUM(SMMU_CONFIG_SMMU_ENABLE, 0, DISABLE, ENABLE);
DEFINE_MC_REG(SMMU_TLB_CONFIG_TLB_ACTIVE_LINES, 0, 6);
DEFINE_MC_REG_BIT_ENUM(SMMU_TLB_CONFIG_TLB_ROUND_ROBIN_ARBITRATION, 28, DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(SMMU_TLB_CONFIG_TLB_HIT_UNDER_MISS, 29, DISABLE, ENABLE);
DEFINE_MC_REG(SMMU_PTC_CONFIG_PTC_INDEX_MAP, 0, 7);
DEFINE_MC_REG(SMMU_PTC_CONFIG_PTC_REQ_LIMIT, 24, 4);
DEFINE_MC_REG_BIT_ENUM(SMMU_PTC_CONFIG_PTC_CACHE_ENABLE, 29, DISABLE, ENABLE);
DEFINE_MC_REG(SMMU_PTB_ASID_CURRENT_ASID, 0, 7);
DEFINE_MC_REG(SMMU_PTB_DATA_ASID_PDE_BASE, 0, 22);
DEFINE_MC_REG_BIT_ENUM(SMMU_PTB_DATA_ASID_NONSECURE, 29, DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(SMMU_PTB_DATA_ASID_WRITABLE, 30, DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(SMMU_PTB_DATA_ASID_READABLE, 31, DISABLE, ENABLE);
DEFINE_MC_REG(SMMU_AVPC_ASID_AVPC_ASID, 0, 7);
DEFINE_MC_REG_BIT_ENUM(SMMU_AVPC_ASID_AVPC_SMMU_ENABLE, 31, DISABLE, ENABLE);
DEFINE_MC_REG(SMMU_PPCS1_ASID_PPCS1_ASID, 0, 7);
DEFINE_MC_REG_BIT_ENUM(SMMU_PPCS1_ASID_PPCS1_SMMU_ENABLE, 31, DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_0, 0, NONSECURE, SECURE);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_1, 1, NONSECURE, SECURE);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_2, 2, NONSECURE, SECURE);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_3, 3, NONSECURE, SECURE);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_4, 4, NONSECURE, SECURE);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_5, 5, NONSECURE, SECURE);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_6, 6, NONSECURE, SECURE);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_7, 7, NONSECURE, SECURE);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_8, 8, NONSECURE, SECURE);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_9, 9, NONSECURE, SECURE);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_10, 10, NONSECURE, SECURE);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_11, 11, NONSECURE, SECURE);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_12, 12, NONSECURE, SECURE);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_13, 13, NONSECURE, SECURE);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_14, 14, NONSECURE, SECURE);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_15, 15, NONSECURE, SECURE);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_0, 16, NONPROMOTING, PROMOTING);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_1, 17, NONPROMOTING, PROMOTING);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_2, 18, NONPROMOTING, PROMOTING);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_3, 19, NONPROMOTING, PROMOTING);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_4, 20, NONPROMOTING, PROMOTING);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_5, 21, NONPROMOTING, PROMOTING);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_6, 22, NONPROMOTING, PROMOTING);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_7, 23, NONPROMOTING, PROMOTING);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_8, 24, NONPROMOTING, PROMOTING);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_9, 25, NONPROMOTING, PROMOTING);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_10, 26, NONPROMOTING, PROMOTING);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_11, 27, NONPROMOTING, PROMOTING);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_12, 28, NONPROMOTING, PROMOTING);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_13, 29, NONPROMOTING, PROMOTING);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_14, 30, NONPROMOTING, PROMOTING);
DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_15, 31, NONPROMOTING, PROMOTING);
DEFINE_MC_REG(SECURITY_CFG0_SECURITY_BOM, 20, 12);
DEFINE_MC_REG(SECURITY_CFG1_SECURITY_SIZE, 0, 13);
DEFINE_MC_REG(SECURITY_CFG3_SECURITY_BOM_HI, 0, 2);
DEFINE_MC_REG_BIT_ENUM(SEC_CARVEOUT_REG_CTRL_SEC_CARVEOUT_WRITE_ACCESS, 0, ENABLED, DISABLED);
DEFINE_MC_REG_BIT_ENUM(VIDEO_PROTECT_REG_CTRL_VIDEO_PROTECT_WRITE_ACCESS, 0, ENABLED, DISABLED);
DEFINE_MC_REG_BIT_ENUM(VIDEO_PROTECT_REG_CTRL_VIDEO_PROTECT_ALLOW_TZ_WRITE, 1, DISABLED, ENABLED);
DEFINE_MC_REG_BIT_ENUM(MTS_CARVEOUT_REG_CTRL_MTS_CARVEOUT_WRITE_ACCESS, 0, ENABLED, DISABLED);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_PROTECT_MODE, 0, LOCKBIT_SECURE, TZ_SECURE);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_LOCK_MODE, 1, UNLOCKED, LOCKED);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, 2, ANY_ADDRESS, UNTRANSLATED_ONLY);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, 3, DISABLED, ENABLED);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, 4, DISABLED, ENABLED);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, 5, DISABLED, ENABLED);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, 6, DISABLED, ENABLED);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, 7, DISABLED, ENABLED);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, 8, DISABLED, ENABLED);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, 9, DISABLED, ENABLED);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, 10, DISABLED, ENABLED);
DEFINE_MC_REG(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 11, 3);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, 14, ENABLE_CHECKS, DISABLE_CHECKS);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, 15, ENABLE_CHECKS, DISABLE_CHECKS);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, 16, ENABLE_CHECKS, DISABLE_CHECKS);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, 17, ENABLE_CHECKS, DISABLE_CHECKS);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, 18, ENABLE_CHECKS, DISABLE_CHECKS);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, 19, ENABLE_CHECKS, DISABLE_CHECKS);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, 20, ENABLE_CHECKS, DISABLE_CHECKS);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, 21, ENABLE_CHECKS, DISABLE_CHECKS);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, 22, DISABLED, ENABLED);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, 23, DISABLED, BYPASS_CHECK);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, 24, DISABLED, BYPASS_CHECK);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, 25, DISABLED, ENABLED);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, 26, DISABLED, ENABLED);
DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_IS_WPR, 27, DISABLED, ENABLED);
#define MC_CLIENT_ACCESS_NUM_CLIENTS 32
/* _ACCESS0 */
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_PTCR, ( 0 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0A, ( 1 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0AB, ( 2 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0B, ( 3 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0BB, ( 4 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0C, ( 5 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0CB, ( 6 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_AFIR, ( 14 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_AVPCARM7R, ( 15 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAYHC, ( 16 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAYHCB, ( 17 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_HDAR, ( 21 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_HOST1XDMAR, ( 22 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_HOST1XR, ( 23 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_NVENCSRD, ( 28 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_PPCSAHBDMAR, ( 29 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_PPCSAHBSLVR, ( 30 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_SATAR, ( 31 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE);
/* _ACCESS1 */
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDEBSEVR, ( 34 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDEMBER, ( 35 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDEMCER, ( 36 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDETPER, ( 37 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_MPCORELPR, ( 38 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_MPCORER, ( 39 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_NVENCSWR, ( 43 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_AFIW, ( 49 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_AVPCARM7W, ( 50 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_HDAW, ( 53 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_HOST1XW, ( 54 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_MPCORELPW, ( 56 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_MPCOREW, ( 57 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_PPCSAHBDMAW, ( 59 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_PPCSAHBSLVW, ( 60 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_SATAW, ( 61 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDEBSEVW, ( 62 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDEDBGW, ( 63 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE);
/* _ACCESS2 */
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_VDEMBEW, ( 64 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_VDETPMW, ( 65 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPRA, ( 68 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPWA, ( 70 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPWB, ( 71 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_XUSB_HOSTR, ( 74 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_XUSB_HOSTW, ( 75 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_XUSB_DEVR, ( 76 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_XUSB_DEVW, ( 77 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPRAB, ( 78 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPWAB, ( 80 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPWBB, ( 81 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_TSECSRD, ( 84 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_TSECSWR, ( 85 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_A9AVPSCR, ( 86 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_A9AVPSCW, ( 87 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_GPUSRD, ( 88 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_GPUSWR, ( 89 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_DISPLAYT, ( 90 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE);
/* _ACCESS3 */
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCRA, ( 96 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCRAA, ( 97 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCR, ( 98 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCRAB, ( 99 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCWA, (100 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCWAA, (101 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCW, (102 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCWAB, (103 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_VICSRD, (108 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_VICSWR, (109 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_VIW, (114 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_DISPLAYD, (115 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_NVDECSRD, (120 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_NVDECSWR, (121 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_APER, (122 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_APEW, (123 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_NVJPGSRD, (126 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_NVJPGSWR, (127 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE);
/* _ACCESS4 */
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_SESRD, (128 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_SESWR, (129 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_AXIAPR, (130 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_AXIAPW, (131 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_ETRR, (132 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_ETRW, (133 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_TSECRDB, (134 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_TSECWRB, (135 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_GPUSRD2, (136 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE);
DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_GPUSWR2, (137 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE);
DEFINE_MC_REG(IRAM_BOM_IRAM_BOM, 12, BITSIZEOF(u32) - 12);
DEFINE_MC_REG(IRAM_TOM_IRAM_TOM, 12, BITSIZEOF(u32) - 12);
DEFINE_MC_REG_BIT_ENUM(IRAM_REG_CTRL_IRAM_CFG_WRITE_ACCESS, 0, ENABLED, DISABLED);

View File

@@ -0,0 +1,44 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#define MSELECT(x) (0x50060000 + x)
#define MSELECT_CONFIG (0x000)
#define MSELECT_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (MSELECT, NAME)
#define MSELECT_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (MSELECT, NAME, VALUE)
#define MSELECT_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (MSELECT, NAME, ENUM)
#define MSELECT_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(MSELECT, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
#define DEFINE_MSELECT_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (MSELECT, NAME, __OFFSET__, __WIDTH__)
#define DEFINE_MSELECT_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (MSELECT, NAME, __OFFSET__, ZERO, ONE)
#define DEFINE_MSELECT_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (MSELECT, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
#define DEFINE_MSELECT_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(MSELECT, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_MSELECT_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (MSELECT, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
DEFINE_MSELECT_REG_BIT_ENUM(CONFIG_ERR_RESP_EN_SLAVE1, 24, DISABLE, ENABLE);
DEFINE_MSELECT_REG_BIT_ENUM(CONFIG_ERR_RESP_EN_SLAVE2, 25, DISABLE, ENABLE);
DEFINE_MSELECT_REG_BIT_ENUM(CONFIG_WRAP_TO_INCR_SLAVE0, 27, DISABLE, ENABLE);
DEFINE_MSELECT_REG_BIT_ENUM(CONFIG_WRAP_TO_INCR_SLAVE1, 28, DISABLE, ENABLE);
DEFINE_MSELECT_REG_BIT_ENUM(CONFIG_WRAP_TO_INCR_SLAVE2, 29, DISABLE, ENABLE);
DEFINE_MSELECT_REG_BIT_ENUM(CONFIG_WRAP_TO_INCR_SLAVE3, 30, DISABLE, ENABLE);

View File

@@ -0,0 +1,28 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#define PG_UP(x) (0x60000000 + x)
#define PG_UP_TAG (0x000)
#define PG_UP_TAG_PID_COP 0xAAAAAAAA

View File

@@ -0,0 +1,97 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#define PINMUX_AUX_SDMMC1_CLK (0x3000)
#define PINMUX_AUX_SDMMC1_CMD (0x3004)
#define PINMUX_AUX_SDMMC1_DAT3 (0x3008)
#define PINMUX_AUX_SDMMC1_DAT2 (0x300C)
#define PINMUX_AUX_SDMMC1_DAT1 (0x3010)
#define PINMUX_AUX_SDMMC1_DAT0 (0x3014)
#define PINMUX_AUX_DMIC3_CLK (0x30B4)
#define PINMUX_AUX_GEN1_I2C_SCL (0x30BC)
#define PINMUX_AUX_GEN1_I2C_SDA (0x30C0)
#define PINMUX_AUX_PWR_I2C_SCL (0x30DC)
#define PINMUX_AUX_PWR_I2C_SDA (0x30E0)
#define PINMUX_AUX_UART1_TX (0x30E4)
#define PINMUX_AUX_UART1_RX (0x30E8)
#define PINMUX_AUX_UART1_RTS (0x30EC)
#define PINMUX_AUX_UART1_CTS (0x30F0)
#define PINMUX_AUX_UART2_TX (0x30F4)
#define PINMUX_AUX_UART2_RX (0x30F8)
#define PINMUX_AUX_UART2_RTS (0x30FC)
#define PINMUX_AUX_UART2_CTS (0x3100)
#define PINMUX_AUX_UART3_TX (0x3104)
#define PINMUX_AUX_UART3_RX (0x3108)
#define PINMUX_AUX_UART3_RTS (0x310C)
#define PINMUX_AUX_UART3_CTS (0x3110)
#define PINMUX_AUX_DVFS_PWM (0x3184)
#define PINMUX_AUX_NFC_EN (0x31D0)
#define PINMUX_AUX_NFC_INT (0x31D4)
#define PINMUX_AUX_LCD_BL_PWM (0x31FC)
#define PINMUX_AUX_LCD_BL_EN (0x3200)
#define PINMUX_AUX_LCD_RST (0x3204)
#define PINMUX_AUX_GPIO_PA6 (0x3244)
#define PINMUX_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (PINMUX, NAME)
#define PINMUX_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (PINMUX, NAME, VALUE)
#define PINMUX_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (PINMUX, NAME, ENUM)
#define PINMUX_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(PINMUX, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
#define DEFINE_PINMUX_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (PINMUX, NAME, __OFFSET__, __WIDTH__)
#define DEFINE_PINMUX_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE)
#define DEFINE_PINMUX_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
#define DEFINE_PINMUX_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_PINMUX_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_PUPD, 2, NONE, PULL_DOWN, PULL_UP, RSVD);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_TRISTATE, 4, PASSTHROUGH, TRISTATE);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_PARK, 5, NORMAL, PARKED);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_INPUT, 6, DISABLE, ENABLE);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_LOCK, 7, DISABLE, ENABLE);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_LPDR, 8, DISABLE, ENABLE);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_OD, 11, DISABLE, ENABLE);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_SCHMT, 12, DISABLE, ENABLE);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_SDMMC1_CLK_PM, 0, SDMMC1, RSVD1, RSVD2, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_SDMMC1_CMD_PM, 0, SDMMC1, RSVD1, RSVD2, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_SDMMC1_DAT3_PM, 0, SDMMC1, RSVD1, RSVD2, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_SDMMC1_DAT2_PM, 0, SDMMC1, RSVD1, RSVD2, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_SDMMC1_DAT1_PM, 0, SDMMC1, RSVD1, RSVD2, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_SDMMC1_DAT0_PM, 0, SDMMC1, RSVD1, RSVD2, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_DMIC3_CLK_PM, 0, DMIC3, I2S5A, RSVD2, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_GEN1_I2C_PM, 0, I2C1, RSVD1, RSVD2, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_PWR_I2C_PM, 0, I2CPMU, RSVD1, RSVD2, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART1_PM, 0, UARTA, RSVD1, RSVD2, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART2_PM, 0, UARTB, I2S4A, RSVD2, UART);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART3_PM, 0, UARTC, SPI4, RSVD2, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_DVFS_PWM_PM, 0, RSVD0, CLDVFS, SPI3, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_GPIO_PA6_PM, 0, SATA, RSVD1, RSVD2, RSVD3);

View File

@@ -0,0 +1,246 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#define APBDEV_PMC_CNTRL (0x000)
#define APBDEV_PMC_WAKE_MASK (0x00C)
#define APBDEV_PMC_WAKE_LVL (0x010)
#define APBDEV_PMC_WAKE_STATUS (0x014)
#define APBDEV_PMC_DPD_PADS_ORIDE (0x01C)
#define APBDEV_PMC_DPD_SAMPLE (0x020)
#define APBDEV_PMC_DPD_ENABLE (0x024)
#define APBDEV_PMC_CLAMP_STATUS (0x02C)
#define APBDEV_PMC_PWRGATE_TOGGLE (0x030)
#define APBDEV_PMC_REMOVE_CLAMPING_CMD (0x034)
#define APBDEV_PMC_PWRGATE_STATUS (0x038)
#define APBDEV_PMC_PWRGOOD_TIMER (0x03C)
#define APBDEV_PMC_BLINK_TIMER (0x040)
#define APBDEV_PMC_NO_IOPOWER (0x044)
#define APBDEV_PMC_PWR_DET (0x048)
#define APBDEV_PMC_SCRATCH0 (0x050)
#define APBDEV_PMC_SCRATCH1 (0x054)
#define APBDEV_PMC_SCRATCH4 (0x060)
#define APBDEV_PMC_SCRATCH12 (0x080)
#define APBDEV_PMC_SCRATCH13 (0x084)
#define APBDEV_PMC_SCRATCH18 (0x098)
#define APBDEV_PMC_SCRATCH20 (0x0A0)
#define APBDEV_PMC_AUTO_WAKE_LVL_MASK (0x0DC)
#define APBDEV_PMC_WAKE_DELAY (0x0E0)
#define APBDEV_PMC_PWR_DET_VAL (0x0E4)
#define APBDEV_PMC_CRYPTO_OP (0x0F4)
#define APBDEV_PMC_SCRATCH31 (0x118)
#define APBDEV_PMC_SCRATCH32 (0x11C)
#define APBDEV_PMC_SCRATCH33 (0x120)
#define APBDEV_PMC_SCRATCH39 (0x138)
#define APBDEV_PMC_SCRATCH40 (0x13C)
#define APBDEV_PMC_WAKE2_MASK (0x160)
#define APBDEV_PMC_WAKE2_LVL (0x164)
#define APBDEV_PMC_WAKE2_STATUS (0x168)
#define APBDEV_PMC_AUTO_WAKE2_LVL_MASK (0x170)
#define APBDEV_PMC_OSC_EDPD_OVER (0x1A4)
#define APBDEV_PMC_CLK_OUT_CNTRL (0x1A8)
#define APBDEV_PMC_IO_DPD_REQ (0x1B8)
#define APBDEV_PMC_IO_DPD_STATUS (0x1BC)
#define APBDEV_PMC_IO_DPD2_REQ (0x1C0)
#define APBDEV_PMC_IO_DPD2_STATUS (0x1C4)
#define APBDEV_PMC_SEL_DPD_TIM (0x1C8)
#define APBDEV_PMC_SCRATCH45 (0x234)
#define APBDEV_PMC_SCRATCH46 (0x238)
#define APBDEV_PMC_TSC_MULT (0x2B4)
#define APBDEV_PMC_STICKY_BITS (0x2C0)
#define APBDEV_PMC_WEAK_BIAS (0x2C8)
#define APBDEV_PMC_GPU_RG_CNTRL (0x2D4)
#define APBDEV_PMC_CNTRL2 (0x440)
#define APBDEV_PMC_FUSE_CTRL (0x450)
#define APBDEV_PMC_IO_DPD3_REQ (0x45C)
#define APBDEV_PMC_IO_DPD3_STATUS (0x460)
#define APBDEV_PMC_IO_DPD4_REQ (0x464)
#define APBDEV_PMC_IO_DPD4_STATUS (0x468)
#define APBDEV_PMC_SET_SW_CLAMP (0x47C)
#define APBDEV_PMC_WAKE_DEBOUNCE_EN (0x4D8)
#define APBDEV_PMC_DDR_CNTRL (0x4E4)
#define APBDEV_PMC_SEC_DISABLE (0x004)
#define APBDEV_PMC_SEC_DISABLE2 (0x2C4)
#define APBDEV_PMC_SEC_DISABLE3 (0x2D8)
#define APBDEV_PMC_SEC_DISABLE4 (0x5B0)
#define APBDEV_PMC_SEC_DISABLE5 (0x5B4)
#define APBDEV_PMC_SEC_DISABLE6 (0x5B8)
#define APBDEV_PMC_SEC_DISABLE7 (0x5BC)
#define APBDEV_PMC_SEC_DISABLE8 (0x5C0)
#define APBDEV_PMC_SCRATCH43 (0x22C)
#define APBDEV_PMC_SCRATCH190 (0x818)
#define APBDEV_PMC_SCRATCH200 (0x840)
#define APBDEV_PMC_SEC_DISABLE3 (0x2D8)
#define APBDEV_PMC_SECURE_SCRATCH4 (0x0C0)
#define APBDEV_PMC_SECURE_SCRATCH5 (0x0C4)
#define APBDEV_PMC_SECURE_SCRATCH6 (0x224)
#define APBDEV_PMC_SECURE_SCRATCH7 (0x228)
#define APBDEV_PMC_SECURE_SCRATCH16 (0x320)
#define APBDEV_PMC_SECURE_SCRATCH21 (0x334)
#define APBDEV_PMC_SECURE_SCRATCH24 (0x340)
#define APBDEV_PMC_SECURE_SCRATCH25 (0x344)
#define APBDEV_PMC_SECURE_SCRATCH26 (0x348)
#define APBDEV_PMC_SECURE_SCRATCH27 (0x34C)
#define APBDEV_PMC_SECURE_SCRATCH32 (0x360)
#define APBDEV_PMC_SECURE_SCRATCH34 (0x368)
#define APBDEV_PMC_SECURE_SCRATCH35 (0x36C)
#define APBDEV_PMC_SECURE_SCRATCH39 (0x37C)
#define APBDEV_PMC_SECURE_SCRATCH51 (0x3AC)
#define APBDEV_PMC_SECURE_SCRATCH55 (0x3BC)
#define APBDEV_PMC_SECURE_SCRATCH74 (0x408)
#define APBDEV_PMC_SECURE_SCRATCH75 (0x40C)
#define APBDEV_PMC_SECURE_SCRATCH76 (0x410)
#define APBDEV_PMC_SECURE_SCRATCH77 (0x414)
#define APBDEV_PMC_SECURE_SCRATCH78 (0x418)
#define APBDEV_PMC_SECURE_SCRATCH99 (0xAE4)
#define APBDEV_PMC_SECURE_SCRATCH100 (0xAE8)
#define APBDEV_PMC_SECURE_SCRATCH101 (0xAEC)
#define APBDEV_PMC_SECURE_SCRATCH102 (0xAF0)
#define APBDEV_PMC_SECURE_SCRATCH103 (0xAF4)
#define APBDEV_PMC_SECURE_SCRATCH112 (0xB18)
#define APBDEV_PMC_SECURE_SCRATCH113 (0xB1C)
#define APBDEV_PMC_SECURE_SCRATCH114 (0xB20)
#define APBDEV_PMC_SECURE_SCRATCH115 (0xB24)
#define APBDEV_PMC_SECURE_SCRATCH119 (0xB34)
#define PMC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (APBDEV_PMC, NAME)
#define PMC_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (APBDEV_PMC, NAME, VALUE)
#define PMC_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (APBDEV_PMC, NAME, ENUM)
#define PMC_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(APBDEV_PMC, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
#define DEFINE_PMC_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (APBDEV_PMC, NAME, __OFFSET__, __WIDTH__)
#define DEFINE_PMC_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (APBDEV_PMC, NAME, __OFFSET__, ZERO, ONE)
#define DEFINE_PMC_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (APBDEV_PMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
#define DEFINE_PMC_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(APBDEV_PMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_PMC_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (APBDEV_PMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
DEFINE_PMC_REG_BIT_ENUM(CNTRL_MAIN_RESET, 4, DISABLE, ENABLE)
DEFINE_PMC_REG_BIT_ENUM(DPD_SAMPLE_ON, 0, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(DPD_ENABLE_ON, 0, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(DPD_ENABLE_TSC_MULT_EN, 1, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_TOGGLE_START, 8, DISABLE, ENABLE);
DEFINE_PMC_REG(PWRGATE_TOGGLE_PARTID, 0, 5);
enum APBDEV_PMC_PWRGATE_TOGGLE_PARTID : u8 {
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CRAIL = 0,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_VE = 2,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_PCX = 3,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_MPE = 6,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_SAX = 8,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE1 = 9,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE2 = 10,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE3 = 11,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE0 = 14,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_C0NC = 15,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_SOR = 17,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_DIS = 18,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_DISB = 19,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_XUSBA = 20,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_XUSBB = 21,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_XUSBC = 22,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_VIC = 23,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_IRAM = 24,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_NVDEC = 25,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_NVJPG = 26,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_AUD = 27,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_DFD = 28,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_VE2 = 29,
};
DEFINE_PMC_REG_BIT_ENUM(REMOVE_CLAMPING_COMMAND_CRAIL, 0, DISABLE, ENABLE);
enum APBDEV_PMC_PWRGATE_STATUS_STATUS {
APBDEV_PMC_PWRGATE_STATUS_STATUS_OFF = 0,
APBDEV_PMC_PWRGATE_STATUS_STATUS_ON = 1,
};
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_CRAIL, 0, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_VE, 2, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_PCX, 3, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_MPE, 6, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_SAX, 8, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_CE1, 9, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_CE2, 10, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_CE3, 11, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_CE0, 14, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_C0NC, 15, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_SOR, 17, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_DIS, 18, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_DISB, 19, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_XUSBA, 20, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_XUSBB, 21, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_XUSBC, 22, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_VIC, 23, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_IRAM, 24, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_NVDEC, 25, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_NVJPG, 26, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_AUD, 27, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_DFD, 28, OFF, ON);
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_VE2, 29, OFF, ON);
DEFINE_PMC_REG(PWRGATE_STATUS_CE123, 9, 3);
DEFINE_PMC_REG_BIT_ENUM(NO_IOPOWER_SDMMC1, 12, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(PWR_DET_SDMMC1, 12, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(PWR_DET_VAL_SDMMC1, 12, DISABLE, ENABLE);
DEFINE_PMC_REG(SET_SW_CLAMP_CRAIL, 0, 1);
DEFINE_PMC_REG_TWO_BIT_ENUM(IO_DPD_REQ_CODE, 30, IDLE, DPD_OFF, DPD_ON, RESERVED3);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CRAIL, 0, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_TE, 1, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_VE, 2, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_PCX, 3, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_VDE, 4, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_MPE, 6, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_HEG, 7, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_SAX, 8, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CE1, 9, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CE2, 10, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CE3, 11, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CELP, 12, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CE0, 14, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_C0NC, 15, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_SOR, 17, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_C1NC, 16, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_DIS, 18, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_DISB, 19, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_XUSBA, 20, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_XUSBB, 21, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_XUSBC, 22, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_VIC, 23, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_IRAM, 24, DISABLE, ENABLE);
DEFINE_PMC_REG(OSC_EDPD_OVER_XOFS, 1, 6);
DEFINE_PMC_REG_BIT_ENUM(STICKY_BITS_HDA_LPBK_DIS, 0, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(STICKY_BITS_JTAG_STS, 6, ENABLE, DISABLE);
DEFINE_PMC_REG_BIT_ENUM(CNTRL2_WAKE_DET_EN, 9, DISABLE, ENABLE);
DEFINE_PMC_REG_BIT_ENUM(SEC_DISABLE2_WRITE21, 26, OFF, ON);

View File

@@ -0,0 +1,52 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#define SB_CSR (0x200)
#define SB_PFCFG (0x208)
#define SB_AA64_RESET_LOW (0x230)
#define SB_AA64_RESET_HIGH (0x234)
#define SB_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (SB, NAME)
#define SB_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (SB, NAME, VALUE)
#define SB_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (SB, NAME, ENUM)
#define SB_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(SB, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
#define DEFINE_SB_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (SB, NAME, __OFFSET__, __WIDTH__)
#define DEFINE_SB_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (SB, NAME, __OFFSET__, ZERO, ONE)
#define DEFINE_SB_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (SB, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
#define DEFINE_SB_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(SB, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_SB_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (SB, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
DEFINE_SB_REG_BIT_ENUM(CSR_SECURE_BOOT_FLAG, 0, DISABLE, ENABLE);
DEFINE_SB_REG_BIT_ENUM(CSR_NS_RST_VEC_WR_DIS, 1, ENABLE, DISABLE);
DEFINE_SB_REG_BIT_ENUM(CSR_PIROM_DISABLE, 4, ENABLE, DISABLE);
DEFINE_SB_REG_BIT_ENUM(CSR_HANG, 6, DISABLE, ENABLE);
DEFINE_SB_REG_BIT_ENUM(CSR_SWDM_ENABLE, 7, DISABLE, ENABLE);
DEFINE_SB_REG(CSR_SWDM_FAIL_COUNT, 8, 4);
DEFINE_SB_REG(CSR_COT_FAIL_COUNT, 12, 4);
DEFINE_SB_REG_BIT_ENUM(PFCFG_SPNIDEN, 0, DISABLE, ENABLE);
DEFINE_SB_REG_BIT_ENUM(PFCFG_SPIDEN, 1, DISABLE, ENABLE);
DEFINE_SB_REG_BIT_ENUM(PFCFG_NIDEN, 2, DISABLE, ENABLE);
DEFINE_SB_REG_BIT_ENUM(PFCFG_DBGEN, 3, DISABLE, ENABLE);

View File

@@ -0,0 +1,58 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#define SYSCTR0_CNTCR (0x000)
#define SYSCTR0_CNTCV0 (0x008)
#define SYSCTR0_CNTCV1 (0x00C)
#define SYSCTR0_CNTFID0 (0x020)
#define SYSCTR0_CNTFID1 (0x024)
#define SYSCTR0_COUNTERID4 (0xFD0)
#define SYSCTR0_COUNTERID5 (0xFD4)
#define SYSCTR0_COUNTERID6 (0xFD8)
#define SYSCTR0_COUNTERID7 (0xFDC)
#define SYSCTR0_COUNTERID0 (0xFE0)
#define SYSCTR0_COUNTERID1 (0xFE4)
#define SYSCTR0_COUNTERID2 (0xFE8)
#define SYSCTR0_COUNTERID3 (0xFEC)
#define SYSCTR0_COUNTERID8 (0xFF0)
#define SYSCTR0_COUNTERID9 (0xFF4)
#define SYSCTR0_COUNTERID10 (0xFF8)
#define SYSCTR0_COUNTERID11 (0xFFC)
#define SYSCTR0_COUNTERID(n) SYSCTR0_COUNTERID##n
#define SYSCTR0_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (SYSCTR0, NAME)
#define SYSCTR0_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (SYSCTR0, NAME, VALUE)
#define SYSCTR0_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (SYSCTR0, NAME, ENUM)
#define SYSCTR0_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(SYSCTR0, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
#define DEFINE_SYSCTR0_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (SYSCTR0, NAME, __OFFSET__, __WIDTH__)
#define DEFINE_SYSCTR0_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (SYSCTR0, NAME, __OFFSET__, ZERO, ONE)
#define DEFINE_SYSCTR0_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (SYSCTR0, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
#define DEFINE_SYSCTR0_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(SYSCTR0, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_SYSCTR0_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (SYSCTR0, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
DEFINE_SYSCTR0_REG_BIT_ENUM(CNTCR_EN, 0, DISABLE, ENABLE);
DEFINE_SYSCTR0_REG_BIT_ENUM(CNTCR_HDBG, 1, DISABLE, ENABLE);

View File

@@ -0,0 +1,49 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/util.hpp>
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#define TIMERUS_USEC_CFG (0x014)
#define TIMER_SHARED_TIMER_SECURE_CFG (0x1A4)
#define TIMER_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (TIMER, NAME)
#define TIMER_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (TIMER, NAME, VALUE)
#define TIMER_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (TIMER, NAME, ENUM)
#define TIMER_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(TIMER, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
#define DEFINE_TIMER_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (TIMER, NAME, __OFFSET__, __WIDTH__)
#define DEFINE_TIMER_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (TIMER, NAME, __OFFSET__, ZERO, ONE)
#define DEFINE_TIMER_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (TIMER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
#define DEFINE_TIMER_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(TIMER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_TIMER_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (TIMER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
DEFINE_TIMER_REG(USEC_CFG_USEC_DIVISOR, 0, 8);
DEFINE_TIMER_REG(USEC_CFG_USEC_DIVIDEND, 8, 8);
DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_TMR5, 5, DISABLE, ENABLE);
DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_TMR6, 6, DISABLE, ENABLE);
DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_TMR7, 7, DISABLE, ENABLE);
DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_TMR8, 8, DISABLE, ENABLE);
DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_WDT0, 12, DISABLE, ENABLE);
DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_WDT1, 13, DISABLE, ENABLE);
DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_WDT2, 14, DISABLE, ENABLE);
DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_WDT3, 15, DISABLE, ENABLE);

View File

@@ -34,8 +34,10 @@
#include <vapours/util/util_intrusive_list.hpp>
#include <vapours/util/util_intrusive_red_black_tree.hpp>
#include <vapours/util/util_tinymt.hpp>
#include <vapours/util/util_timer.hpp>
#include <vapours/util/util_uuid.hpp>
#include <vapours/util/util_bounded_map.hpp>
#include <vapours/util/util_overlap.hpp>
#include <vapours/util/util_string_util.hpp>
#include <vapours/util/util_variadic.hpp>
#include <vapours/util/util_format_string.hpp>

View File

@@ -73,4 +73,9 @@ namespace ams::util {
return IsAligned(reinterpret_cast<uintptr_t>(value), alignment);
}
}
template<typename T, typename U> requires std::integral<T> && std::integral<U>
constexpr ALWAYS_INLINE T DivideUp(T x, U y) {
return (x + (y - 1)) / y;
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
namespace ams::util {
int SNPrintf(char *dst, size_t dst_size, const char *fmt, ...);
int VSNPrintf(char *dst, size_t dst_size, const char *fmt, std::va_list vl);
int TSNPrintf(char *dst, size_t dst_size, const char *fmt, ...);
int TVSNPrintf(char *dst, size_t dst_size, const char *fmt, std::va_list vl);
}

View File

@@ -0,0 +1,26 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
namespace ams::util {
u32 GetMicroSeconds();
void WaitMicroSeconds(int us);
}

View File

@@ -0,0 +1,41 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#include "impl/dd_select_cache_impl.hpp"
namespace ams::dd {
void InvalidateDataCache(void *addr, size_t size) {
return impl::InvalidateDataCacheImpl(addr, size);
}
void StoreDataCache(void *addr, size_t size) {
return impl::StoreDataCacheImpl(addr, size);
}
void FlushDataCache(void *addr, size_t size) {
return impl::FlushDataCacheImpl(addr, size);
}
}

View File

@@ -0,0 +1,101 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
namespace ams::dd {
uintptr_t QueryIoMapping(dd::PhysicalAddress phys_addr, size_t size) {
#if defined(ATMOSPHERE_IS_EXOSPHERE)
#if defined(ATMOSPHERE_ARCH_ARM64)
/* TODO: Secure Monitor translation? */
AMS_UNUSED(size);
return static_cast<uintptr_t>(phys_addr);
#elif defined(ATMOSPHERE_ARCH_ARM)
/* TODO: BPMP translation? */
AMS_UNUSED(size);
return static_cast<uintptr_t>(phys_addr);
#else
#error "Unknown architecture for ams::dd::QueryIoMapping (EXOSPHERE)!"
#endif
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
/* TODO: Kernel address translation? */
AMS_UNUSED(size);
return static_cast<uintptr_t>(phys_addr);
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
svc::Address virt_addr = 0;
const dd::PhysicalAddress aligned_addr = util::AlignDown(phys_addr, os::MemoryPageSize);
const size_t offset = phys_addr - aligned_addr;
const size_t aligned_size = size + offset;
if (hos::GetVersion() >= hos::Version_10_0_0) {
svc::Size region_size = 0;
R_TRY_CATCH(svc::QueryIoMapping(&virt_addr, &region_size, aligned_addr, aligned_size)) {
/* Official software handles this by returning 0. */
R_CATCH(svc::ResultNotFound) { return 0; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
AMS_ASSERT(region_size >= aligned_size);
} else {
R_TRY_CATCH(svc::LegacyQueryIoMapping(&virt_addr, aligned_addr, aligned_size)) {
/* Official software handles this by returning 0. */
R_CATCH(svc::ResultNotFound) { return 0; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return static_cast<uintptr_t>(virt_addr) + offset;
#else
#error "Unknown execution context for ams::dd::QueryIoMapping!"
#endif
}
u32 ReadIoRegister(dd::PhysicalAddress phys_addr) {
#if defined(ATMOSPHERE_IS_EXOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE)
return reg::Read(dd::QueryIoMapping(phys_addr, sizeof(u32)));
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
u32 val;
R_ABORT_UNLESS(svc::ReadWriteRegister(std::addressof(val), phys_addr, 0, 0));
return val;
#else
#error "Unknown execution context for ams::dd::ReadIoRegister!"
#endif
}
void WriteIoRegister(dd::PhysicalAddress phys_addr, u32 value) {
#if defined(ATMOSPHERE_IS_EXOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE)
reg::Write(dd::QueryIoMapping(phys_addr, sizeof(u32)), value);
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
u32 out_val;
R_ABORT_UNLESS(svc::ReadWriteRegister(std::addressof(out_val), phys_addr, 0xFFFFFFFF, value));
AMS_UNUSED(out_val);
#else
#error "Unknown execution context for ams::dd::WriteIoRegister!"
#endif
}
}

View File

@@ -0,0 +1,94 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#else
#include <vapours.hpp>
#endif
namespace ams::dd::impl {
void StoreDataCacheImpl(void *addr, size_t size) {
#if defined(ATMOSPHERE_ARCH_ARM64)
/* On aarch64, we can use cache maintenance instructions. */
/* Get cache line size. */
uintptr_t ctr_el0 = 0;
__asm__ __volatile__("mrs %[ctr_el0], ctr_el0" : [ctr_el0]"=r"(ctr_el0));
const uintptr_t cache_line_size = 4 << ((ctr_el0 >> 16) & 0xF);
/* Invalidate the cache. */
const uintptr_t start_addr = reinterpret_cast<uintptr_t>(addr) & ~(cache_line_size - 1);
const uintptr_t end_addr = reinterpret_cast<uintptr_t>(addr) + size;
for (uintptr_t cur = start_addr; cur < end_addr; cur += cache_line_size) {
__asm__ __volatile__("dc cvac, %[cur]" : : [cur]"r"(cur));
}
/* Add a memory barrier. */
__asm__ __volatile__("dsb sy" ::: "memory");
#else
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
/* Invoke the relevant svc. */
const auto result = svc::StoreProcessDataCache(svc::PseudoHandle::CurrentProcess, reinterpret_cast<uintptr_t>(addr), size);
R_ASSERT(result);
#elif defined(ATMOSPHERE_IS_EXOSPHERE) && defined(__BPMP__)
/* Do nothing. */
AMS_UNUSED(addr, size);
#else
#error "Unknown execution context for ams::dd::impl::StoreDataCacheImpl"
#endif
#endif
}
void FlushDataCacheImpl(void *addr, size_t size) {
#if defined(ATMOSPHERE_ARCH_ARM64)
/* On aarch64, we can use cache maintenance instructions. */
/* Get cache line size. */
uintptr_t ctr_el0 = 0;
__asm__ __volatile__("mrs %[ctr_el0], ctr_el0" : [ctr_el0]"=r"(ctr_el0));
const uintptr_t cache_line_size = 4 << ((ctr_el0 >> 16) & 0xF);
/* Invalidate the cache. */
const uintptr_t start_addr = reinterpret_cast<uintptr_t>(addr) & ~(cache_line_size - 1);
const uintptr_t end_addr = reinterpret_cast<uintptr_t>(addr) + size;
for (uintptr_t cur = start_addr; cur < end_addr; cur += cache_line_size) {
__asm__ __volatile__("dc civac, %[cur]" : : [cur]"r"(cur));
}
/* Add a memory barrier. */
__asm__ __volatile__("dsb sy" ::: "memory");
#else
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
/* Invoke the relevant svc. */
const auto result = svc::FlushProcessDataCache(svc::PseudoHandle::CurrentProcess, reinterpret_cast<uintptr_t>(addr), size);
R_ASSERT(result);
#elif defined(ATMOSPHERE_IS_EXOSPHERE) && defined(__BPMP__)
/* Do nothing. */
AMS_UNUSED(addr, size);
#else
#error "Unknown execution context for ams::dd::impl::FlushDataCacheImpl"
#endif
#endif
}
void InvalidateDataCacheImpl(void *addr, size_t size) {
/* Just perform a flush, which is clean + invalidate. */
return FlushDataCacheImpl(addr, size);
}
}

View File

@@ -0,0 +1,22 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#if defined(ATMOSPHERE_OS_HORIZON)
#include "dd_cache_impl.os.horizon.hpp"
#else
#error "Unknown OS for ams::dd::CacheImpl"
#endif

View File

@@ -0,0 +1,581 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#include "sdmmc_base_device_accessor.hpp"
namespace ams::sdmmc::impl {
#if defined(AMS_SDMMC_THREAD_SAFE)
#define AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX() std::scoped_lock lk(this->base_device->device_mutex)
#else
#define AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX()
#endif
void BaseDevice::GetLegacyCapacityParameters(u8 *out_c_size_mult, u8 *out_read_bl_len) const {
AMS_ABORT_UNLESS(out_c_size_mult != nullptr);
AMS_ABORT_UNLESS(out_read_bl_len != nullptr);
/* Extract C_SIZE_MULT and READ_BL_LEN from the CSD. */
*out_c_size_mult = static_cast<u8>((this->csd[2] >> 7) & 0x7);
*out_read_bl_len = static_cast<u8>((this->csd[4] >> 8) & 0xF);
}
Result BaseDevice::SetLegacyMemoryCapacity() {
/* Get csize from the csd. */
const u32 c_size = ((this->csd[3] >> 6) & 0x3FF) | ((this->csd[4] & 0x3) << 10);
/* Get c_size_mult and read_bl_len. */
u8 c_size_mult, read_bl_len;
this->GetLegacyCapacityParameters(std::addressof(c_size_mult), std::addressof(read_bl_len));
/* Validate the parameters. */
R_UNLESS((read_bl_len + c_size_mult + 2) >= 9, sdmmc::ResultUnexpectedDeviceCsdValue());
/* Set memory capacity. */
this->memory_capacity = (c_size + 1) << ((read_bl_len + c_size_mult + 2) - 9);
this->is_valid_memory_capacity = true;
return ResultSuccess();
}
Result BaseDevice::CheckDeviceStatus(u32 r1_resp) const {
/* Check if there are any errors at all. */
R_SUCCEED_IF((r1_resp & DeviceStatus_ErrorMask) == 0);
/* Check errors individually. */
#define AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(__ERROR__) R_UNLESS((r1_resp & DeviceStatus_##__ERROR__) == 0, sdmmc::ResultDeviceStatus##__ERROR__())
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(ComCrcError);
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(DeviceEccFailed);
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(CcError);
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(Error);
if (this->GetDeviceType() == DeviceType_Mmc) {
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(SwitchError);
}
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(AddressMisaligned);
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(BlockLenError);
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseSeqError);
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseParam);
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpViolation);
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(LockUnlockFailed);
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(CidCsdOverwrite);
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpEraseSkip);
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpEraseSkip);
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseReset);
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(IllegalCommand);
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(AddressOutOfRange);
#undef AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR
return ResultSuccess();
}
DeviceState BaseDevice::GetDeviceState(u32 r1_resp) const {
return static_cast<DeviceState>((r1_resp & DeviceStatus_CurrentStateMask) >> DeviceStatus_CurrentStateShift);
}
Result BaseDeviceAccessor::IssueCommandAndCheckR1(u32 *out_response, u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const {
/* Issue the command. */
constexpr ResponseType CommandResponseType = ResponseType_R1;
Command command(command_index, command_arg, CommandResponseType, is_busy);
R_TRY(this->host_controller->IssueCommand(std::addressof(command)));
/* Get the response. */
AMS_ABORT_UNLESS(out_response != nullptr);
this->host_controller->GetLastResponse(out_response, sizeof(u32), CommandResponseType);
/* Mask out the ignored status bits. */
if (status_ignore_mask != 0) {
*out_response &= ~status_ignore_mask;
}
/* Check the r1 response for errors. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
R_TRY(this->base_device->CheckDeviceStatus(*out_response));
/* Check the device state. */
if (expected_state != DeviceState_Unknown) {
R_UNLESS(this->base_device->GetDeviceState(*out_response) == expected_state, sdmmc::ResultUnexpectedDeviceState());
}
return ResultSuccess();
}
Result BaseDeviceAccessor::IssueCommandGoIdleState() const {
/* Issue the command. */
Command command(CommandIndex_GoIdleState, 0, ResponseType_R0, false);
return this->host_controller->IssueCommand(std::addressof(command));
}
Result BaseDeviceAccessor::IssueCommandAllSendCid(void *dst, size_t dst_size) const {
/* Issue the command. */
constexpr ResponseType CommandResponseType = ResponseType_R2;
Command command(CommandIndex_AllSendCid, 0, CommandResponseType, false);
R_TRY(this->host_controller->IssueCommand(std::addressof(command)));
/* Copy the data out. */
AMS_ABORT_UNLESS(dst != nullptr);
AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast<uintptr_t>(dst), alignof(u32)));
AMS_ABORT_UNLESS(dst_size >= DeviceCidSize);
this->host_controller->GetLastResponse(static_cast<u32 *>(dst), DeviceCidSize, CommandResponseType);
return ResultSuccess();
}
Result BaseDeviceAccessor::IssueCommandSelectCard() const {
/* Get the command argument. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
const u32 arg = static_cast<u32>(this->base_device->GetRca()) << 16;
/* Issue the command. */
return this->IssueCommandAndCheckR1(CommandIndex_SelectCard, arg, true, DeviceState_Unknown);
}
Result BaseDeviceAccessor::IssueCommandSendCsd(void *dst, size_t dst_size) const {
/* Get the command argument. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
const u32 arg = static_cast<u32>(this->base_device->GetRca()) << 16;
/* Issue the command. */
constexpr ResponseType CommandResponseType = ResponseType_R2;
Command command(CommandIndex_SendCsd, arg, CommandResponseType, false);
R_TRY(this->host_controller->IssueCommand(std::addressof(command)));
/* Copy the data out. */
AMS_ABORT_UNLESS(dst != nullptr);
AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast<uintptr_t>(dst), alignof(u32)));
AMS_ABORT_UNLESS(dst_size >= DeviceCsdSize);
this->host_controller->GetLastResponse(static_cast<u32 *>(dst), DeviceCsdSize, CommandResponseType);
return ResultSuccess();
}
Result BaseDeviceAccessor::IssueCommandSendStatus(u32 *out_device_status, u32 status_ignore_mask) const {
/* Get the command argument. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
const u32 arg = static_cast<u32>(this->base_device->GetRca()) << 16;
/* Issue the command. */
return this->IssueCommandAndCheckR1(out_device_status, CommandIndex_SendStatus, arg, false, DeviceState_Tran, status_ignore_mask);
}
Result BaseDeviceAccessor::IssueCommandSetBlockLenToSectorSize() const {
/* Issue the command. */
return this->IssueCommandAndCheckR1(CommandIndex_SetBlockLen, SectorSize, false, DeviceState_Tran);
}
Result BaseDeviceAccessor::IssueCommandMultipleBlock(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const {
/* Get the argument. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
const u32 arg = this->base_device->IsHighCapacity() ? sector_index : sector_index * SectorSize;
/* Get the command index and transfer direction. */
const u32 command_index = is_read ? CommandIndex_ReadMultipleBlock : CommandIndex_WriteMultipleBlock;
const auto xfer_direction = is_read ? TransferDirection_ReadFromDevice : TransferDirection_WriteToDevice;
/* Issue the command. */
constexpr ResponseType CommandResponseType = ResponseType_R1;
Command command(command_index, arg, CommandResponseType, false);
TransferData xfer_data(buf, SectorSize, num_sectors, xfer_direction, true, true);
Result result = this->host_controller->IssueCommand(std::addressof(command), std::addressof(xfer_data), out_num_transferred_blocks);
/* Handle the failure case. */
if (R_FAILED(result)) {
/* Check if we were removed. */
R_TRY(this->CheckRemoved());
/* By default, we'll want to return the result we just got. */
Result result_to_return = result;
/* Stop transmission. */
u32 resp = 0;
result = this->host_controller->IssueStopTransmissionCommand(std::addressof(resp));
if (R_SUCCEEDED(result)) {
result = this->base_device->CheckDeviceStatus(resp & (~DeviceStatus_IllegalCommand));
if (R_FAILED(result)) {
result_to_return = result;
}
}
/* Check if we were removed. */
R_TRY(this->CheckRemoved());
/* Get the device status. */
u32 device_status = 0;
result = this->IssueCommandSendStatus(std::addressof(device_status), DeviceStatus_IllegalCommand);
/* If there's a device status error we don't already have, we prefer to return it. */
if (!sdmmc::ResultDeviceStatusHasError::Includes(result_to_return) && sdmmc::ResultDeviceStatusHasError::Includes(result)) {
result_to_return = result;
}
/* Return the result we chose. */
return result_to_return;
}
/* Get the responses. */
u32 resp, st_resp;
this->host_controller->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType);
this->host_controller->GetLastStopTransmissionResponse(std::addressof(st_resp), sizeof(st_resp));
/* Check the device status. */
R_TRY(this->base_device->CheckDeviceStatus(resp));
/* Decide on what errors to ignore. */
u32 status_ignore_mask = 0;
if (is_read) {
AMS_ABORT_UNLESS(out_num_transferred_blocks != nullptr);
if ((*out_num_transferred_blocks + sector_index) == this->base_device->GetMemoryCapacity()) {
status_ignore_mask = DeviceStatus_AddressOutOfRange;
}
}
/* Check the device status. */
R_TRY(this->base_device->CheckDeviceStatus(st_resp & ~status_ignore_mask));
return ResultSuccess();
}
Result BaseDeviceAccessor::ReadWriteSingle(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const {
/* Issue the read/write command. */
AMS_ABORT_UNLESS(out_num_transferred_blocks != nullptr);
R_TRY(this->IssueCommandMultipleBlock(out_num_transferred_blocks, sector_index, num_sectors, buf, is_read));
/* Decide on what errors to ignore. */
u32 status_ignore_mask = 0;
if (is_read) {
AMS_ABORT_UNLESS(this->base_device != nullptr);
if ((*out_num_transferred_blocks + sector_index) == this->base_device->GetMemoryCapacity()) {
status_ignore_mask = DeviceStatus_AddressOutOfRange;
}
}
/* Get and check the status. */
u32 device_status;
R_TRY(this->IssueCommandSendStatus(std::addressof(device_status), status_ignore_mask));
return ResultSuccess();
}
Result BaseDeviceAccessor::ReadWriteMultiple(u32 sector_index, u32 num_sectors, u32 sector_index_alignment, void *buf, size_t buf_size, bool is_read) {
/* Verify that we can send the command. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
/* If we want to read zero sectors, there's no work for us to do. */
R_SUCCEED_IF(num_sectors == 0);
/* Check that the buffer is big enough for the sectors we're reading. */
AMS_ABORT_UNLESS((buf_size / SectorSize) >= num_sectors);
/* Read sectors repeatedly until we've read all the ones we want. */
u32 cur_sector_index = sector_index;
u32 remaining_sectors = num_sectors;
u8 *cur_buf = static_cast<u8 *>(buf);
while (remaining_sectors > 0) {
/* Determine how many sectors we can read in this iteration. */
u32 cur_sectors = remaining_sectors;
if (sector_index_alignment > 0) {
AMS_ABORT_UNLESS((cur_sector_index % sector_index_alignment) == 0);
const u32 max_sectors = this->host_controller->GetMaxTransferNumBlocks();
if (remaining_sectors > max_sectors) {
cur_sectors = max_sectors - (max_sectors % sector_index_alignment);
}
}
/* Try to perform the read/write. */
u32 num_transferred_blocks = 0;
Result result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read);
if (R_FAILED(result)) {
/* Check if we were removed. */
R_TRY(this->CheckRemoved());
/* Log that we failed to read/write. */
this->PushErrorLog(false, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue());
/* Retry the read/write. */
num_transferred_blocks = 0;
result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read);
if (R_FAILED(result)) {
/* Check if we were removed. */
R_TRY(this->CheckRemoved());
/* Log that we failed to read/write. */
this->PushErrorLog(false, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue());
/* Re-startup the connection, to see if that helps. */
R_TRY(this->ReStartup());
/* Retry the read/write a third time. */
num_transferred_blocks = 0;
result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read);
if (R_FAILED(result)) {
/* Log that we failed after a re-startup. */
this->PushErrorLog(true, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue());
return result;
}
/* Log that we succeeded after a retry. */
this->PushErrorLog(true, "%s %X %X:0", is_read ? "R" : "W", cur_sector_index, cur_sectors);
/* Increment the number of error corrections we've done. */
++this->num_read_write_error_corrections;
}
}
/* Update our tracking variables. */
AMS_ABORT_UNLESS(remaining_sectors >= num_transferred_blocks);
remaining_sectors -= num_transferred_blocks;
cur_sector_index += num_transferred_blocks;
cur_buf += num_transferred_blocks * SectorSize;
}
return ResultSuccess();
}
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
void BaseDeviceAccessor::RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) {
/* Lock exclusive access of the base device. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
/* Register the address. */
return this->host_controller->RegisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address);
}
void BaseDeviceAccessor::UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) {
/* Lock exclusive access of the base device. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
/* Register the address. */
return this->host_controller->UnregisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address);
}
#endif
Result BaseDeviceAccessor::Activate() {
/* Lock exclusive access of the base device. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
/* Check that the device is awake. */
R_UNLESS(this->base_device->IsAwake(), sdmmc::ResultNotAwakened());
/* If the device is already active, we don't need to do anything. */
R_SUCCEED_IF(this->base_device->IsActive());
/* Activate the base device. */
auto activate_guard = SCOPE_GUARD { ++this->num_activation_failures; };
R_TRY(this->OnActivate());
/* We successfully activated the device. */
activate_guard.Cancel();
this->base_device->SetActive();
return ResultSuccess();
}
void BaseDeviceAccessor::Deactivate() {
/* Lock exclusive access of the base device. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
/* Deactivate the base device. */
if (this->base_device->IsActive()) {
this->host_controller->Shutdown();
this->base_device->Deactivate();
}
}
Result BaseDeviceAccessor::ReadWrite(u32 sector_index, u32 num_sectors, void *buffer, size_t buffer_size, bool is_read) {
/* Lock exclusive access of the base device. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
/* Check that the device is accessible. */
R_TRY(this->base_device->CheckAccessible());
/* Perform the read/write. */
auto rw_guard = SCOPE_GUARD { ++this->num_read_write_failures; };
R_TRY(this->OnReadWrite(sector_index, num_sectors, buffer, buffer_size, is_read));
/* We successfully performed the read/write. */
rw_guard.Cancel();
return ResultSuccess();
}
Result BaseDeviceAccessor::CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width) {
/* Lock exclusive access of the base device. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
/* Check that the device is accessible. */
R_TRY(this->base_device->CheckAccessible());
/* Get the current speed mode/bus width. */
*out_speed_mode = this->host_controller->GetSpeedMode();
*out_bus_width = this->host_controller->GetBusWidth();
/* Verify that we can get the status. */
R_TRY(this->host_controller->GetInternalStatus());
return ResultSuccess();
}
Result BaseDeviceAccessor::GetMemoryCapacity(u32 *out_sectors) const {
/* Lock exclusive access of the base device. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
/* Check that the device is accessible. */
R_TRY(this->base_device->CheckAccessible());
/* Get the capacity. */
AMS_ABORT_UNLESS(out_sectors != nullptr);
*out_sectors = this->base_device->GetMemoryCapacity();
return ResultSuccess();
}
Result BaseDeviceAccessor::GetDeviceStatus(u32 *out) const {
/* Lock exclusive access of the base device. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
/* Check that the device is accessible. */
R_TRY(this->base_device->CheckAccessible());
/* Get the status. */
R_TRY(this->IssueCommandSendStatus(out, 0));
return ResultSuccess();
}
Result BaseDeviceAccessor::GetOcr(u32 *out) const {
/* Lock exclusive access of the base device. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
/* Check that the device is accessible. */
R_TRY(this->base_device->CheckAccessible());
/* Get the ocr. */
AMS_ABORT_UNLESS(out != nullptr);
*out = this->base_device->GetOcr();
return ResultSuccess();
}
Result BaseDeviceAccessor::GetRca(u16 *out) const {
/* Lock exclusive access of the base device. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
/* Check that the device is accessible. */
R_TRY(this->base_device->CheckAccessible());
/* Get the rca. */
AMS_ABORT_UNLESS(out != nullptr);
*out = this->base_device->GetRca();
return ResultSuccess();
}
Result BaseDeviceAccessor::GetCid(void *out, size_t size) const {
/* Lock exclusive access of the base device. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
/* Check that the device is accessible. */
R_TRY(this->base_device->CheckAccessible());
/* Get the cid. */
this->base_device->GetCid(out, size);
return ResultSuccess();
}
Result BaseDeviceAccessor::GetCsd(void *out, size_t size) const {
/* Lock exclusive access of the base device. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
/* Check that the device is accessible. */
R_TRY(this->base_device->CheckAccessible());
/* Get the csd. */
this->base_device->GetCsd(out, size);
return ResultSuccess();
}
void BaseDeviceAccessor::GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) {
/* Lock exclusive access of the base device. */
AMS_ABORT_UNLESS(this->base_device != nullptr);
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
/* Set the output error info. */
AMS_ABORT_UNLESS(out_error_info != nullptr);
out_error_info->num_activation_failures = this->num_activation_failures;
out_error_info->num_activation_error_corrections = this->num_activation_error_corrections;
out_error_info->num_read_write_failures = this->num_read_write_failures;
out_error_info->num_read_write_error_corrections = this->num_read_write_error_corrections;
this->ClearErrorInfo();
/* Check if we should write logs. */
if (out_log_size == nullptr) {
return;
}
/* Check if we can write logs. */
if (out_log_buffer == nullptr || log_buffer_size == 0) {
*out_log_size = 0;
return;
}
/* Get and clear our logs. */
#if defined(AMS_SDMMC_USE_LOGGER)
{
if (this->error_logger.HasLog()) {
this->PushErrorTimeStamp();
*out_log_size = this->error_logger.GetAndClearLogs(out_log_buffer, log_buffer_size);
} else {
*out_log_size = 0;
}
}
#else
{
*out_log_size = 0;
}
#endif
}
}

View File

@@ -0,0 +1,512 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include "sdmmc_i_host_controller.hpp"
#include "sdmmc_i_device_accessor.hpp"
namespace ams::sdmmc::impl {
#if defined(AMS_SDMMC_USE_LOGGER)
class Logger {
private:
static constexpr size_t LogLengthMax = 0x20;
static constexpr size_t LogCountMax = 0x10;
private:
char logs[LogCountMax][LogLengthMax];
int log_index;
private:
void Clear() {
for (size_t i = 0; i < LogCountMax; ++i) {
this->logs[i][0] = '\0';
}
this->log_index = 0;
}
size_t Pop(char *dst, size_t dst_size) {
/* Decrease log index. */
if ((--this->log_index) < 0) {
this->log_index = LogCountMax - 1;
}
/* Check if we have a log. */
if (this->logs[this->log_index][0] == '\0') {
return 0;
}
/* Copy log to output. */
const int len = ::ams::util::Strlcpy(dst, this->logs[this->log_index], dst_size);
/* Clear the log we copied. */
this->logs[this->log_index][0] = '\0';
return static_cast<size_t>(len);
}
public:
Logger() {
this->Clear();
}
void Push(const char *fmt, std::va_list vl) {
/* Format the log into the current buffer. */
::ams::util::TVSNPrintf(this->logs[this->log_index], LogLengthMax, fmt, vl);
/* Update our log index. */
if ((++this->log_index) >= static_cast<int>(LogCountMax)) {
this->log_index = 0;
}
}
void Push(const char *fmt, ...) {
std::va_list vl;
va_start(vl, fmt);
this->Push(fmt, vl);
va_end(vl);
}
bool HasLog() const {
const int index = this->log_index > 0 ? this->log_index - 1 : static_cast<int>(LogCountMax - 1);
return this->logs[index][0] != '\0';
}
size_t GetAndClearLogs(char *dst, size_t dst_size) {
AMS_ABORT_UNLESS(dst != nullptr);
AMS_ABORT_UNLESS(dst_size > 0);
/* Pop logs until we run out of them. */
size_t total_len = 0;
while (true) {
/* Pop the current log. */
const size_t cur_len = this->Pop(dst + total_len, dst_size - total_len);
if (cur_len == 0) {
break;
}
/* Check if the log exceeded the buffer size. */
if (total_len + cur_len + 1 >= dst_size) {
break;
}
/* Advance the total length. */
total_len += cur_len;
/* Check if there's space for our separator. */
if (total_len + 3 >= dst_size) {
break;
}
dst[total_len + 0] = ',';
dst[total_len + 1] = ' ';
total_len += 2;
}
/* Ensure that the complete log fits in the buffer. */
if (total_len >= dst_size) {
total_len = dst_size - 1;
}
/* Ensure null termination. */
dst[total_len] = '\0';
/* Clear any remaining logs. */
this->Clear();
/* Return the length of the logs, including null terminator. */
return total_len + 1;
}
};
#endif
enum DeviceType {
DeviceType_Mmc = 0,
DeviceType_SdCard = 1,
DeviceType_GcAsic = 2,
};
enum DeviceState {
DeviceState_Idle = 0,
DeviceState_Ready = 1,
DeviceState_Ident = 2,
DeviceState_Stby = 3,
DeviceState_Tran = 4,
DeviceState_Data = 5,
DeviceState_Rcv = 6,
DeviceState_Prg = 7,
DeviceState_Dis = 8,
DeviceState_Rsvd0 = 9,
DeviceState_Rsvd1 = 10,
DeviceState_Rsvd2 = 11,
DeviceState_Rsvd3 = 12,
DeviceState_Rsvd4 = 13,
DeviceState_Rsvd5 = 14,
DeviceState_RsvdIoMode = 15,
DeviceState_Unknown = 16,
};
enum DeviceStatus : u32 {
DeviceStatus_AkeSeqError = (1u << 3),
DeviceStatus_AppCmd = (1u << 5),
DeviceStatus_SwitchError = (1u << 7),
DeviceStatus_EraseReset = (1u << 13),
DeviceStatus_WpEraseSkip = (1u << 15),
DeviceStatus_CidCsdOverwrite = (1u << 16),
DeviceStatus_Error = (1u << 19),
DeviceStatus_CcError = (1u << 20),
DeviceStatus_DeviceEccFailed = (1u << 21),
DeviceStatus_IllegalCommand = (1u << 22),
DeviceStatus_ComCrcError = (1u << 23),
DeviceStatus_LockUnlockFailed = (1u << 24),
DeviceStatus_WpViolation = (1u << 26),
DeviceStatus_EraseParam = (1u << 27),
DeviceStatus_EraseSeqError = (1u << 28),
DeviceStatus_BlockLenError = (1u << 29),
DeviceStatus_AddressMisaligned = (1u << 30),
DeviceStatus_AddressOutOfRange = (1u << 31),
DeviceStatus_CurrentStateShift = 9,
DeviceStatus_CurrentStateMask = (0b1111u << DeviceStatus_CurrentStateShift),
DeviceStatus_ErrorMask = (DeviceStatus_SwitchError |
DeviceStatus_EraseReset |
DeviceStatus_WpEraseSkip |
DeviceStatus_CidCsdOverwrite |
DeviceStatus_Error |
DeviceStatus_CcError |
DeviceStatus_DeviceEccFailed |
DeviceStatus_IllegalCommand |
DeviceStatus_ComCrcError |
DeviceStatus_LockUnlockFailed |
DeviceStatus_WpViolation |
DeviceStatus_EraseParam |
DeviceStatus_EraseSeqError |
DeviceStatus_BlockLenError |
DeviceStatus_AddressMisaligned |
DeviceStatus_AddressOutOfRange),
};
class BaseDevice {
private:
u32 ocr;
u8 cid[DeviceCidSize];
u16 csd[DeviceCsdSize / sizeof(u16)];
u32 memory_capacity;
bool is_high_capacity;
bool is_valid_ocr;
bool is_valid_cid;
bool is_valid_csd;
bool is_valid_high_capacity;
bool is_valid_memory_capacity;
bool is_active;
bool is_awake;
public:
#if defined(AMS_SDMMC_THREAD_SAFE)
mutable os::Mutex device_mutex;
public:
BaseDevice() : device_mutex(true)
#else
BaseDevice()
#endif
{
this->is_awake = true;
this->ocr = 0;
this->memory_capacity = 0;
this->is_high_capacity = false;
this->OnDeactivate();
}
void OnDeactivate() {
this->is_active = false;
this->is_valid_ocr = false;
this->is_valid_cid = false;
this->is_valid_csd = false;
this->is_valid_high_capacity = false;
this->is_valid_memory_capacity = false;
}
bool IsAwake() const {
return this->is_awake;
}
void Awaken() {
this->is_awake = true;
}
void PutToSleep() {
this->is_awake = false;
}
bool IsActive() const {
return this->is_active;
}
void SetActive() {
this->is_active = true;
}
virtual void Deactivate() {
this->OnDeactivate();
}
#if defined(AMS_SDMMC_USE_OS_EVENTS)
virtual os::EventType *GetRemovedEvent() const = 0;
#endif
virtual DeviceType GetDeviceType() const = 0;
virtual u16 GetRca() const = 0;
#if defined(AMS_SDMMC_USE_OS_EVENTS)
void InitializeRemovedEvent() {
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
os::InitializeEvent(removed_event, false, os::EventClearMode_ManualClear);
}
}
void FinalizeRemovedEvent() {
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
os::FinalizeEvent(removed_event);
}
}
void SignalRemovedEvent() {
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
os::SignalEvent(removed_event);
}
}
void ClearRemovedEvent() {
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
os::ClearEvent(removed_event);
}
}
bool IsRemoved() const {
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
return os::TryWaitEvent(removed_event);
}
return false;
}
#endif
Result CheckRemoved() const {
#if defined(AMS_SDMMC_USE_OS_EVENTS)
R_UNLESS(!this->IsRemoved(), sdmmc::ResultDeviceRemoved());
#endif
return ResultSuccess();
}
Result CheckAccessible() const {
/* Check awake. */
R_UNLESS(this->IsAwake(), sdmmc::ResultNotAwakened());
/* Check active. */
R_UNLESS(this->IsActive(), sdmmc::ResultNotActivated());
/* Check removed. */
R_TRY(this->CheckRemoved());
return ResultSuccess();
}
void SetHighCapacity(bool en) {
this->is_high_capacity = en;
this->is_valid_high_capacity = true;
}
bool IsHighCapacity() const {
AMS_ABORT_UNLESS(this->is_valid_high_capacity);
return this->is_high_capacity;
}
void SetOcr(u32 o) {
this->ocr = o;
this->is_valid_ocr = true;
}
u32 GetOcr() const {
AMS_ABORT_UNLESS(this->is_valid_ocr);
return this->ocr;
}
void SetCid(const void *src, size_t src_size) {
AMS_ABORT_UNLESS(src != nullptr);
AMS_ABORT_UNLESS(src_size >= DeviceCidSize);
std::memcpy(this->cid, src, DeviceCidSize);
this->is_valid_cid = true;
}
void GetCid(void *dst, size_t dst_size) const {
AMS_ABORT_UNLESS(this->is_valid_cid);
AMS_ABORT_UNLESS(dst != nullptr);
AMS_ABORT_UNLESS(dst_size >= DeviceCidSize);
std::memcpy(dst, this->cid, DeviceCidSize);
}
void SetCsd(const void *src, size_t src_size) {
AMS_ABORT_UNLESS(src != nullptr);
AMS_ABORT_UNLESS(src_size >= DeviceCsdSize);
std::memcpy(this->csd, src, DeviceCsdSize);
this->is_valid_csd = true;
}
void GetCsd(void *dst, size_t dst_size) const {
AMS_ABORT_UNLESS(this->is_valid_csd);
AMS_ABORT_UNLESS(dst != nullptr);
AMS_ABORT_UNLESS(dst_size >= DeviceCsdSize);
std::memcpy(dst, this->csd, DeviceCsdSize);
}
void SetMemoryCapacity(u32 num_sectors) {
this->memory_capacity = num_sectors;
this->is_valid_memory_capacity = true;
}
u32 GetMemoryCapacity() const {
AMS_ABORT_UNLESS(this->is_valid_memory_capacity);
return this->memory_capacity;
}
void GetLegacyCapacityParameters(u8 *out_c_size_mult, u8 *out_read_bl_len) const;
Result SetLegacyMemoryCapacity();
Result CheckDeviceStatus(u32 r1_resp) const;
DeviceState GetDeviceState(u32 r1_resp) const;
};
class BaseDeviceAccessor : public IDeviceAccessor {
private:
IHostController *host_controller;
BaseDevice *base_device;
u32 num_activation_failures;
u32 num_activation_error_corrections;
u32 num_read_write_failures;
u32 num_read_write_error_corrections;
#if defined(AMS_SDMMC_USE_LOGGER)
Logger error_logger;
#endif
private:
void ClearErrorInfo() {
this->num_activation_failures = 0;
this->num_activation_error_corrections = 0;
this->num_read_write_failures = 0;
this->num_read_write_error_corrections = 0;
}
protected:
explicit BaseDeviceAccessor(IHostController *hc) : host_controller(hc), base_device(nullptr) {
this->ClearErrorInfo();
}
IHostController *GetHostController() const {
return this->host_controller;
}
void SetDevice(BaseDevice *bd) {
this->base_device = bd;
}
Result CheckRemoved() const {
return this->base_device->CheckRemoved();
}
Result IssueCommandAndCheckR1(u32 *out_response, u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const;
Result IssueCommandAndCheckR1(u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const {
u32 dummy;
return this->IssueCommandAndCheckR1(std::addressof(dummy), command_index, command_arg, is_busy, expected_state, status_ignore_mask);
}
Result IssueCommandAndCheckR1(u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state) const {
return this->IssueCommandAndCheckR1(command_index, command_arg, is_busy, expected_state, 0);
}
Result IssueCommandGoIdleState() const;
Result IssueCommandAllSendCid(void *dst, size_t dst_size) const;
Result IssueCommandSelectCard() const;
Result IssueCommandSendCsd(void *dst, size_t dst_size) const;
Result IssueCommandSendStatus(u32 *out_device_status, u32 status_ignore_mask) const;
Result IssueCommandSendStatus(u32 status_ignore_mask) const {
u32 dummy;
return this->IssueCommandSendStatus(std::addressof(dummy), status_ignore_mask);
}
Result IssueCommandSendStatus() const {
return this->IssueCommandSendStatus(0);
}
Result IssueCommandSetBlockLenToSectorSize() const;
Result IssueCommandMultipleBlock(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const;
Result ReadWriteSingle(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const;
Result ReadWriteMultiple(u32 sector_index, u32 num_sectors, u32 sector_index_alignment, void *buf, size_t buf_size, bool is_read);
void IncrementNumActivationErrorCorrections() {
++this->num_activation_error_corrections;
}
void PushErrorTimeStamp() {
#if defined(AMS_SDMMC_USE_LOGGER)
{
this->error_logger.Push("%u", static_cast<u32>(os::ConvertToTimeSpan(os::GetSystemTick()).GetSeconds()));
}
#endif
}
void PushErrorLog(bool with_timestamp, const char *fmt, ...) {
#if defined(AMS_SDMMC_USE_LOGGER)
{
std::va_list vl;
va_start(vl, fmt);
this->error_logger.Push(fmt, vl);
va_end(vl);
if (with_timestamp) {
this->PushErrorTimeStamp();
}
}
#else
{
AMS_UNUSED(with_timestamp, fmt);
}
#endif
}
virtual Result OnActivate() = 0;
virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) = 0;
virtual Result ReStartup() = 0;
public:
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override;
virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override;
#endif
virtual Result Activate() override;
virtual void Deactivate() override;
virtual Result ReadWrite(u32 sector_index, u32 num_sectors, void *buffer, size_t buffer_size, bool is_read) override;
virtual Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width) override;
virtual Result GetMemoryCapacity(u32 *out_sectors) const override;
virtual Result GetDeviceStatus(u32 *out) const override;
virtual Result GetOcr(u32 *out) const override;
virtual Result GetRca(u16 *out) const override;
virtual Result GetCid(void *out, size_t size) const override;
virtual Result GetCsd(void *out, size_t size) const override;
virtual void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) override;
};
}

View File

@@ -0,0 +1,151 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#include "sdmmc_clock_reset_controller.hpp"
#include "sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp"
#include "sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp"
namespace ams::sdmmc::impl::ClockResetController {
namespace {
constinit bool g_is_module_initialized[Module_Count] = {};
#if defined(AMS_SDMMC_THREAD_SAFE)
constinit os::SdkMutex g_module_mutex;
#define AMS_SDMMC_LOCK_MODULE_MUTEX() std::scoped_lock lk(g_module_mutex)
#else
#define AMS_SDMMC_LOCK_MODULE_MUTEX()
#endif
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
constinit bool g_is_pcv_control = false;
#define AMS_SDMMC_IF_IS_PCV_CONTROL() if (g_is_pcv_control)
#else
#define AMS_SDMMC_IF_IS_PCV_CONTROL() if constexpr (false)
#endif
}
void Initialize(Module module) {
/* Acquire exclusive access to module state. */
AMS_SDMMC_LOCK_MODULE_MUTEX();
/* Mark the module as initialized. */
g_is_module_initialized[module] = true;
/* Initialize the module. */
AMS_SDMMC_IF_IS_PCV_CONTROL() {
ClockResetController::pcv::Initialize(module);
} else {
ClockResetController::reg::Initialize(module);
}
}
void Finalize(Module module) {
/* Acquire exclusive access to module state. */
AMS_SDMMC_LOCK_MODULE_MUTEX();
/* Finalize the module. */
AMS_SDMMC_IF_IS_PCV_CONTROL() {
ClockResetController::pcv::Finalize(module);
} else {
ClockResetController::reg::Finalize(module);
}
/* Mark the module as finalized. */
g_is_module_initialized[module] = false;
}
bool IsAvailable(Module module) {
AMS_SDMMC_IF_IS_PCV_CONTROL() {
return ClockResetController::pcv::IsAvailable(module);
} else {
return ClockResetController::reg::IsAvailable(module);
}
}
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
void SwitchToPcvControl() {
/* Acquire exclusive access to module state. */
AMS_SDMMC_LOCK_MODULE_MUTEX();
/* If we're already using pcv control, we don't need to do anything. */
AMS_SDMMC_IF_IS_PCV_CONTROL() {
return;
}
/* Finalize all modules. */
for (int i = 0; i < Module_Count; ++i) {
if (g_is_module_initialized[i]) {
ClockResetController::reg::Finalize(static_cast<Module>(i));
}
}
/* Mark that we've switched to pcv control. */
/* Initialize modules using pcv control. */
for (int i = 0; i < Module_Count; ++i) {
if (g_is_module_initialized[i]) {
ClockResetController::pcv::Initialize(static_cast<Module>(i));
}
}
}
#endif
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) {
AMS_SDMMC_IF_IS_PCV_CONTROL() {
return ClockResetController::pcv::SetClockFrequencyKHz(out_actual_frequency, module, target_frequency);
} else {
return ClockResetController::reg::SetClockFrequencyKHz(out_actual_frequency, module, target_frequency);
}
}
void AssertReset(Module module) {
AMS_SDMMC_IF_IS_PCV_CONTROL() {
return ClockResetController::pcv::AssertReset(module);
} else {
return ClockResetController::reg::AssertReset(module);
}
}
void ReleaseReset(Module module, u32 target_frequency_khz) {
AMS_SDMMC_IF_IS_PCV_CONTROL() {
return ClockResetController::pcv::ReleaseReset(module, target_frequency_khz);
} else {
return ClockResetController::reg::ReleaseReset(module, target_frequency_khz);
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::sdmmc::impl::ClockResetController {
enum Module {
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
Module_Sdmmc1 = 0,
Module_Sdmmc2 = 1,
Module_Sdmmc3 = 2,
Module_Sdmmc4 = 3,
#endif
Module_Count,
};
void Initialize(Module module);
void Finalize(Module module);
bool IsAvailable(Module module);
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
void SwitchToPcvControl();
#endif
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency);
void AssertReset(Module module);
void ReleaseReset(Module module, u32 target_frequency_khz);
}

View File

@@ -0,0 +1,95 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#include "sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp"
namespace ams::sdmmc::impl::ClockResetController::pcv {
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
void Initialize(Module module) {
AMS_UNUSED(module);
AMS_ABORT("PCV Control not implemented");
}
void Finalize(Module module) {
AMS_UNUSED(module);
AMS_ABORT("PCV Control not implemented");
}
bool IsAvailable(Module module) {
AMS_UNUSED(module);
AMS_ABORT("PCV Control not implemented");
}
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) {
AMS_UNUSED(out_actual_frequency, module, target_frequency);
AMS_ABORT("PCV Control not implemented");
}
void AssertReset(Module module) {
AMS_UNUSED(module);
AMS_ABORT("PCV Control not implemented");
}
void ReleaseReset(Module module, u32 target_frequency_khz) {
AMS_UNUSED(module, target_frequency_khz);
AMS_ABORT("PCV Control not implemented");
}
#else
void Initialize(Module module) {
AMS_UNUSED(module);
AMS_ABORT("PCV Control not supported");
}
void Finalize(Module module) {
AMS_UNUSED(module);
AMS_ABORT("PCV Control not supported");
}
bool IsAvailable(Module module) {
AMS_UNUSED(module);
AMS_ABORT("PCV Control not supported");
}
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) {
AMS_UNUSED(out_actual_frequency, module, target_frequency);
AMS_ABORT("PCV Control not supported");
}
void AssertReset(Module module) {
AMS_UNUSED(module);
AMS_ABORT("PCV Control not supported");
}
void ReleaseReset(Module module, u32 target_frequency_khz) {
AMS_UNUSED(module, target_frequency_khz);
AMS_ABORT("PCV Control not supported");
}
#endif
}

View File

@@ -0,0 +1,30 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include "sdmmc_clock_reset_controller.hpp"
namespace ams::sdmmc::impl::ClockResetController::pcv {
void Initialize(Module module);
void Finalize(Module module);
bool IsAvailable(Module module);
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency);
void AssertReset(Module module);
void ReleaseReset(Module module, u32 target_frequency_khz);
}

View File

@@ -0,0 +1,391 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#include "sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp"
#include "sdmmc_timer.hpp"
namespace ams::sdmmc::impl::ClockResetController::reg {
namespace {
#if defined(AMS_SDMMC_THREAD_SAFE)
constinit os::SdkMutex g_init_mutex;
#define AMS_SDMMC_LOCK_INIT_MUTEX() std::scoped_lock lk(g_init_mutex)
#else
#define AMS_SDMMC_LOCK_INIT_MUTEX()
#endif
constinit bool g_is_initialized = false;
struct ModuleInfo {
u32 target_frequency_khz;
u32 actual_frequency_khz;
};
constinit ModuleInfo g_module_infos[Module_Count] = {};
constexpr inline dd::PhysicalAddress ClockResetControllerRegistersPhysicalAddress = UINT64_C(0x60006000);
constexpr inline size_t ClockResetControllerRegistersSize = 4_KB;
constinit uintptr_t g_clkrst_registers_address = 0;
[[maybe_unused]] void InitializePllc4() {
/* Check that we have registers we can write to. */
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
/* Check if PLLC4_BASE has the expected value; if it does, we have nothing to do. */
constexpr u32 ExpectedPllc4Base = 0x58006804;
if (ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE) == ExpectedPllc4Base) {
return;
}
/* Disable PLLC4_ENABLE, if it's currently set. */
if (ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, ENABLE))) {
ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, DISABLE));
}
/* Operate on the register with PLLC4_ENABLE cleared. */
{
/* Clear IDDQ, read to be sure it takes, wait 5 us. */
ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_IDDQ, OFF));
ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE);
WaitMicroSeconds(5);
/* Write the expected value sans IDDQ/PLLC4_ENABLE. */
constexpr u32 ExpectedPllc4BaseMask = ~ams::reg::EncodeMask(CLK_RST_REG_BITS_MASK(PLLC4_BASE_PLLC4_ENABLE),
CLK_RST_REG_BITS_MASK(PLLC4_BASE_PLLC4_IDDQ));
ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, ExpectedPllc4Base & ExpectedPllc4BaseMask);
}
/* Write PLLC4_ENABLE, and read to be sure our configuration takes. */
ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, ENABLE));
ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE);
/* Wait up to 1s for changes to take. */
{
ManualTimer timer(1000);
while (true) {
/* Check if we're done. */
if (!ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_LOCK, NOT_LOCK))) {
break;
}
/* Check that we haven't timed out. */
AMS_ABORT_UNLESS(timer.Update());
}
}
}
void InitializeLegacyTmClk() {
/* Check that we have registers we can write to. */
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
/* Configure the legacy tm clock as 12MHz. */
ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_LEGACY_TM_CLK_SRC, PLLP_OUT0),
CLK_RST_REG_BITS_VALUE(CLK_SOURCE_LEGACY_TM_CLK_DIVISOR, 66));
/* Enable clock to the legacy tm. */
ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_Y_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_Y_CLK_ENB_LEGACY_TM, ENABLE));
}
bool IsResetReleased(Module module) {
/* Check that we have registers we can write to. */
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
/* Get the reset bit from RST_DEVICES_* */
switch (module) {
case Module_Sdmmc1: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC1_RST, DISABLE));
case Module_Sdmmc2: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC2_RST, DISABLE));
case Module_Sdmmc3: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_U, CLK_RST_REG_BITS_ENUM(RST_DEVICES_U_SWR_SDMMC3_RST, DISABLE));
case Module_Sdmmc4: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC4_RST, DISABLE));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
void SetReset(Module module) {
/* Check that we have registers we can write to. */
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
/* Set reset in RST_DEV_*_SET */
switch (module) {
case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC1_RST, ENABLE));
case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC2_RST, ENABLE));
case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_U_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_U_SDMMC3_RST, ENABLE));
case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC4_RST, ENABLE));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
void ClearReset(Module module) {
/* Check that we have registers we can write to. */
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
/* Set reset in RST_DEV_*_CLR */
switch (module) {
case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC1_RST, ENABLE));
case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC2_RST, ENABLE));
case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_U_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_U_SDMMC3_RST, ENABLE));
case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC4_RST, ENABLE));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
bool IsClockEnabled(Module module) {
/* Check that we have registers we can write to. */
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
/* Get the enable bit from CLK_OUT_ENB_* */
switch (module) {
case Module_Sdmmc1: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC1, ENABLE));
case Module_Sdmmc2: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC2, ENABLE));
case Module_Sdmmc3: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_U, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_U_CLK_ENB_SDMMC3, ENABLE));
case Module_Sdmmc4: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC4, ENABLE));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
void SetClockEnable(Module module) {
/* Check that we have registers we can write to. */
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
/* Set clock enable bit in CLK_ENB_*_SET */
switch (module) {
case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC1, ENABLE));
case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC2, ENABLE));
case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_U_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_U_CLK_ENB_SDMMC3, ENABLE));
case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC4, ENABLE));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
void ClearClockEnable(Module module) {
/* Check that we have registers we can write to. */
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
/* Set clock enable bit in CLK_ENB_*_CLR */
switch (module) {
case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC1, ENABLE));
case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC2, ENABLE));
case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_U_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_U_CLK_ENB_SDMMC3, ENABLE));
case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC4, ENABLE));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
void SetClockSourceSdmmc(u32 *out_actual_frequency_khz, Module module, u32 target_frequency_khz) {
/* Check that we can write to output. */
AMS_ABORT_UNLESS(out_actual_frequency_khz != nullptr);
/* Determine frequency/divisor. */
u32 clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLP_OUT0));
u8 n;
switch (target_frequency_khz) {
case 25'000:
*out_actual_frequency_khz = 24'728;
n = 31;
break;
case 26'000:
*out_actual_frequency_khz = 25'500;
n = 30;
break;
case 40'800:
*out_actual_frequency_khz = 40'800;
n = 18;
break;
case 50'000:
*out_actual_frequency_khz = 48'000;
n = 15;
break;
case 52'000:
*out_actual_frequency_khz = 51'000;
n = 14;
break;
case 100'000:
#if defined(AMS_SDMMC_SET_PLLC4_BASE)
*out_actual_frequency_khz = 99'840;
n = 2;
clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLC4_OUT2));
#else
*out_actual_frequency_khz = 90'667;
n = 7;
#endif
break;
case 200'000:
#if defined(AMS_SDMMC_SET_PLLC4_BASE)
*out_actual_frequency_khz = 199'680;
n = 0;
if (module == Module_Sdmmc2 || module == Module_Sdmmc4) {
clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMC24_SDMMC24_CLK_SRC, PLLC4_OUT2_LJ));
} else {
clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLC4_OUT2));
}
#else
*out_actual_frequency_khz = 163'200;
n = 3;
#endif
break;
case 208'000:
*out_actual_frequency_khz = 204'000;
n = 2;
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Set frequencies in module info. */
g_module_infos[module].target_frequency_khz = target_frequency_khz;
g_module_infos[module].actual_frequency_khz = *out_actual_frequency_khz;
/* Check that we have registers we can write to. */
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
/* Update the clock source. */
switch (module) {
case Module_Sdmmc1: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1, clk_m | static_cast<u32>(n)); break;
case Module_Sdmmc2: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2, clk_m | static_cast<u32>(n)); break;
case Module_Sdmmc3: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3, clk_m | static_cast<u32>(n)); break;
case Module_Sdmmc4: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4, clk_m | static_cast<u32>(n)); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
void EnsureControl(Module module) {
/* Read from RST_DEVICES_* to be sure previous configuration takes. */
switch (module) {
case Module_Sdmmc1: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break;
case Module_Sdmmc2: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break;
case Module_Sdmmc3: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_U); break;
case Module_Sdmmc4: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
}
void Initialize(Module module) {
/* Initialization isn't module specific. */
AMS_UNUSED(module);
/* Acquire exclusive access to the initialization lock. */
AMS_SDMMC_LOCK_INIT_MUTEX();
/* If we've already initialized, we don't need to do anything. */
if (g_is_initialized) {
return;
}
/* Clear module infos. */
std::memset(g_module_infos, 0, sizeof(g_module_infos));
/* Get the registers address. */
g_clkrst_registers_address = dd::QueryIoMapping(ClockResetControllerRegistersPhysicalAddress, ClockResetControllerRegistersSize);
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
/* Perform register initialization. */
#if defined(AMS_SDMMC_SET_PLLC4_BASE)
InitializePllc4();
#endif
InitializeLegacyTmClk();
/* Mark that we've initialized. */
g_is_initialized = true;
}
void Finalize(Module module) {
/* Nothing is needed for finalization. */
AMS_UNUSED(module);
}
bool IsAvailable(Module module) {
return IsResetReleased(module) && IsClockEnabled(module);
}
void SetClockFrequencyKHz(u32 *out_actual_frequency_khz, Module module, u32 target_frequency_khz) {
/* If we're not changing the clock frequency, we don't need to do anything. */
if (target_frequency_khz == g_module_infos[module].target_frequency_khz) {
*out_actual_frequency_khz = g_module_infos[module].actual_frequency_khz;
return;
}
/* Temporarily disable clock. */
const bool clock_enabled = IsClockEnabled(module);
if (clock_enabled) {
ClearClockEnable(module);
}
/* Set the clock source. */
SetClockSourceSdmmc(out_actual_frequency_khz, module, target_frequency_khz);
/* Re-enable clock, if we should. */
if (clock_enabled) {
SetClockEnable(module);
}
/* Ensure that our configuration takes. */
EnsureControl(module);
}
void AssertReset(Module module) {
/* Set reset and disable clock. */
SetReset(module);
ClearClockEnable(module);
/* Ensure that our configuration takes. */
EnsureControl(module);
}
void ReleaseReset(Module module, u32 target_frequency_khz) {
/* Disable clock if it's enabled. */
if (IsClockEnabled(module)) {
ClearClockEnable(module);
}
/* Set reset. */
SetReset(module);
/* Set the clock source. */
u32 actual_source_frequency_khz;
SetClockSourceSdmmc(std::addressof(actual_source_frequency_khz), module, target_frequency_khz);
/* Enable clock. */
SetClockEnable(module);
/* Ensure that our configuration takes. */
EnsureControl(module);
/* Wait 100 clocks. */
WaitClocks(100, actual_source_frequency_khz);
/* Clear reset. */
ClearReset(module);
/* Ensure that our configuration takes. */
EnsureControl(module);
}
}

View File

@@ -0,0 +1,30 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include "sdmmc_clock_reset_controller.hpp"
namespace ams::sdmmc::impl::ClockResetController::reg {
void Initialize(Module module);
void Finalize(Module module);
bool IsAvailable(Module module);
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency);
void AssertReset(Module module);
void ReleaseReset(Module module, u32 target_frequency_khz);
}

View File

@@ -0,0 +1,270 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
#include "sdmmc_device_detector.hpp"
namespace ams::sdmmc::impl {
bool DeviceDetector::IsCurrentInserted() {
return gpio::GetValue(std::addressof(this->gpio_pad_session)) == this->inserted_gpio_value;
}
void DeviceDetector::HandleDeviceStatus(bool prev_inserted, bool cur_inserted) {
if (!prev_inserted && !cur_inserted) {
/* Not inserted -> Not inserted, nothing to do. */
} else if (!prev_inserted && cur_inserted) {
/* Card was inserted. */
if (this->callback_info.inserted_callback != nullptr) {
this->callback_info.inserted_callback(this->callback_info.inserted_callback_arg);
}
} else if (prev_inserted && !cur_inserted) {
/* Card was removed. */
if (this->callback_info.removed_callback != nullptr) {
this->callback_info.removed_callback(this->callback_info.removed_callback_arg);
}
} else /* if (prev_inserted && cur_inserted) */ {
/* Card was removed, and then inserted. */
if (this->callback_info.removed_callback != nullptr) {
this->callback_info.removed_callback(this->callback_info.removed_callback_arg);
}
if (this->callback_info.inserted_callback != nullptr) {
this->callback_info.inserted_callback(this->callback_info.inserted_callback_arg);
}
}
}
void DeviceDetector::DetectorThread() {
/* Initialize the gpio session. */
sm::DoWithSession([] { gpio::Initialize(); });
gpio::OpenSession(std::addressof(this->gpio_pad_session), this->gpio_device_code);
gpio::SetDirection(std::addressof(this->gpio_pad_session), gpio::Direction_Input);
gpio::SetDebounceTime(std::addressof(this->gpio_pad_session), this->gpio_debounce_ms);
gpio::SetDebounceEnabled(std::addressof(this->gpio_pad_session), true);
gpio::SetInterruptMode(std::addressof(this->gpio_pad_session), gpio::InterruptMode_AnyEdge);
/* Get the gpio session's interrupt event. */
os::SystemEventType gpio_event;
R_ABORT_UNLESS(gpio::BindInterrupt(std::addressof(gpio_event), std::addressof(this->gpio_pad_session)));
/* Initialize and link waitable holders. */
os::WaitableManagerType wait_manager;
os::WaitableHolderType detector_thread_end_holder;
os::WaitableHolderType request_sleep_wake_event_holder;
os::WaitableHolderType gpio_event_holder;
os::InitializeWaitableManager(std::addressof(wait_manager));
os::InitializeWaitableHolder(std::addressof(detector_thread_end_holder), std::addressof(this->detector_thread_end_event));
os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(detector_thread_end_holder));
os::InitializeWaitableHolder(std::addressof(request_sleep_wake_event_holder), std::addressof(this->request_sleep_wake_event));
os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(request_sleep_wake_event_holder));
os::InitializeWaitableHolder(std::addressof(gpio_event_holder), std::addressof(gpio_event));
os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(gpio_event_holder));
/* Wait before detecting the initial state of the card. */
os::SleepThread(TimeSpan::FromMilliSeconds(this->gpio_debounce_ms));
bool cur_inserted = this->IsCurrentInserted();
this->is_prev_inserted = cur_inserted;
/* Set state as awake. */
this->state = State_Awake;
os::SignalEvent(std::addressof(this->ready_device_status_event));
/* Enable interrupts to be informed of device status. */
gpio::SetInterruptEnable(std::addressof(this->gpio_pad_session), true);
/* Wait, servicing our events. */
while (true) {
/* Get the signaled holder. */
os::WaitableHolderType *signaled_holder = os::WaitAny(std::addressof(wait_manager));
/* Process the holder. */
bool insert_change = false;
if (signaled_holder == std::addressof(detector_thread_end_holder)) {
/* We should kill ourselves. */
os::ClearEvent(std::addressof(this->detector_thread_end_event));
this->state = State_Finalized;
break;
} else if (signaled_holder == std::addressof(request_sleep_wake_event_holder)) {
/* A request for us to sleep/wake has come in, so we'll acknowledge it. */
os::ClearEvent(std::addressof(this->request_sleep_wake_event));
this->state = State_Sleep;
os::SignalEvent(std::addressof(this->acknowledge_sleep_awake_event));
/* Temporarily unlink our interrupt event. */
os::UnlinkWaitableHolder(std::addressof(gpio_event_holder));
/* Wait to be signaled. */
signaled_holder = os::WaitAny(std::addressof(wait_manager));
/* Link our interrupt event back in. */
os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(gpio_event_holder));
/* We're awake again. Either because we should exit, or because we were asked to wake up. */
os::ClearEvent(std::addressof(this->request_sleep_wake_event));
this->state = State_Awake;
os::SignalEvent(std::addressof(this->acknowledge_sleep_awake_event));
/* If we were asked to exit, do so. */
if (signaled_holder == std::addressof(detector_thread_end_holder)) {
/* We should kill ourselves. */
os::ClearEvent(std::addressof(this->detector_thread_end_event));
this->state = State_Finalized;
break;
} else /* if (signaled_holder == std::addressof(request_sleep_wake_event_holder)) */ {
if ((this->force_detection) ||
(({ bool active; R_SUCCEEDED(gpio::IsWakeEventActive(std::addressof(active), this->gpio_device_code)) && active; })) ||
(os::TryWaitSystemEvent(std::addressof(gpio_event))) ||
(this->is_prev_inserted != this->IsCurrentInserted()))
{
insert_change = true;
}
}
} else /* if (signaled_holder == std::addressof(gpio_event_holder)) */ {
/* An event was detected. */
insert_change = true;
}
/* Handle an insert change, if one occurred. */
if (insert_change) {
/* Call the relevant callback, if we have one. */
if (this->device_detection_event_callback != nullptr) {
this->device_detection_event_callback(this->device_detection_event_callback_arg);
}
/* Clear the interrupt event. */
os::ClearSystemEvent(std::addressof(gpio_event));
gpio::ClearInterruptStatus(std::addressof(this->gpio_pad_session));
gpio::SetInterruptEnable(std::addressof(this->gpio_pad_session), true);
/* Update insertion status. */
cur_inserted = this->IsCurrentInserted();
this->HandleDeviceStatus(this->is_prev_inserted, cur_inserted);
this->is_prev_inserted = cur_inserted;
}
}
/* Disable interrupts to our gpio event. */
gpio::SetInterruptEnable(std::addressof(this->gpio_pad_session), false);
/* Finalize and unlink waitable holders. */
os::UnlinkWaitableHolder(std::addressof(gpio_event_holder));
os::FinalizeWaitableHolder(std::addressof(gpio_event_holder));
os::UnlinkWaitableHolder(std::addressof(request_sleep_wake_event_holder));
os::FinalizeWaitableHolder(std::addressof(request_sleep_wake_event_holder));
os::UnlinkWaitableHolder(std::addressof(detector_thread_end_holder));
os::FinalizeWaitableHolder(std::addressof(detector_thread_end_holder));
os::FinalizeWaitableManager(std::addressof(wait_manager));
/* Finalize the gpio session. */
gpio::UnbindInterrupt(std::addressof(this->gpio_pad_session));
gpio::CloseSession(std::addressof(this->gpio_pad_session));
gpio::Finalize();
}
void DeviceDetector::Initialize(CallbackInfo *ci) {
/* Transition our state from finalized to initializing. */
AMS_ABORT_UNLESS(this->state == State_Finalized);
this->state = State_Initializing;
/* Set our callback infos. */
this->callback_info = *ci;
/* Initialize our events. */
os::InitializeEvent(std::addressof(this->ready_device_status_event), false, os::EventClearMode_ManualClear);
os::InitializeEvent(std::addressof(this->request_sleep_wake_event), false, os::EventClearMode_ManualClear);
os::InitializeEvent(std::addressof(this->acknowledge_sleep_awake_event), false, os::EventClearMode_ManualClear);
os::InitializeEvent(std::addressof(this->detector_thread_end_event), false, os::EventClearMode_ManualClear);
/* Create and start the detector thread. */
os::CreateThread(std::addressof(this->detector_thread), DetectorThreadEntry, this, this->detector_thread_stack, sizeof(this->detector_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(sdmmc, DeviceDetector));
os::SetThreadNamePointer(std::addressof(this->detector_thread), AMS_GET_SYSTEM_THREAD_NAME(sdmmc, DeviceDetector));
os::StartThread(std::addressof(this->detector_thread));
}
void DeviceDetector::Finalize() {
/* Ensure we're not already finalized. */
AMS_ABORT_UNLESS(this->state != State_Finalized);
/* Signal event to end the detector thread. */
os::SignalEvent(std::addressof(this->detector_thread_end_event));
os::WaitThread(std::addressof(this->detector_thread));
/* Finalize thread and events. */
os::DestroyThread(std::addressof(this->detector_thread));
os::FinalizeEvent(std::addressof(this->ready_device_status_event));
os::FinalizeEvent(std::addressof(this->request_sleep_wake_event));
os::FinalizeEvent(std::addressof(this->acknowledge_sleep_awake_event));
os::FinalizeEvent(std::addressof(this->detector_thread_end_event));
}
void DeviceDetector::PutToSleep() {
/* Signal request, wait for acknowledgement. */
os::SignalEvent(std::addressof(this->request_sleep_wake_event));
os::WaitEvent(std::addressof(this->acknowledge_sleep_awake_event));
os::ClearEvent(std::addressof(this->acknowledge_sleep_awake_event));
}
void DeviceDetector::Awaken(bool force_det) {
/* Signal request, wait for acknowledgement. */
this->force_detection = force_det;
os::SignalEvent(std::addressof(this->request_sleep_wake_event));
os::WaitEvent(std::addressof(this->acknowledge_sleep_awake_event));
os::ClearEvent(std::addressof(this->acknowledge_sleep_awake_event));
}
bool DeviceDetector::IsInserted() {
bool inserted = false;
switch (this->state) {
case State_Initializing:
/* Wait for us to know whether the device is inserted. */
os::WaitEvent(std::addressof(this->ready_device_status_event));
[[fallthrough]];
case State_Awake:
/* Get whether the device is currently inserted. */
inserted = this->IsCurrentInserted();
break;
case State_Sleep:
case State_Finalized:
/* Get whether the device was inserted when we last knew. */
inserted = this->is_prev_inserted;
break;
}
return inserted;
}
void DeviceDetector::RegisterDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg) {
this->device_detection_event_callback_arg = arg;
this->device_detection_event_callback = cb;
}
void DeviceDetector::UnregisterDetectionEventCallback() {
this->device_detection_event_callback = nullptr;
}
}
#endif

View File

@@ -0,0 +1,99 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
#include <stratosphere.hpp>
namespace ams::sdmmc::impl {
using InsertedCallback = void (*)(void *);
using RemovedCallback = void (*)(void *);
struct CallbackInfo {
InsertedCallback inserted_callback;
void *inserted_callback_arg;
RemovedCallback removed_callback;
void *removed_callback_arg;
};
class DeviceDetector {
private:
enum State {
State_Initializing = 0,
State_Awake = 1,
State_Sleep = 2,
State_Finalized = 3,
};
private:
alignas(os::ThreadStackAlignment) u8 detector_thread_stack[8_KB];
State state;
bool is_prev_inserted;
bool force_detection;
os::ThreadType detector_thread;
os::EventType detector_thread_end_event;
os::EventType request_sleep_wake_event;
os::EventType acknowledge_sleep_awake_event;
os::EventType ready_device_status_event;
DeviceCode gpio_device_code;
gpio::GpioValue inserted_gpio_value;
u32 gpio_debounce_ms;
gpio::GpioPadSession gpio_pad_session;
CallbackInfo callback_info;
DeviceDetectionEventCallback device_detection_event_callback;
void *device_detection_event_callback_arg;
private:
static void DetectorThreadEntry(void *arg) {
reinterpret_cast<DeviceDetector *>(arg)->DetectorThread();
}
void DetectorThread();
bool IsCurrentInserted();
void HandleDeviceStatus(bool prev_inserted, bool cur_inserted);
public:
explicit DeviceDetector(DeviceCode dc, gpio::GpioValue igv, u32 gd)
: gpio_device_code(dc), inserted_gpio_value(igv), gpio_debounce_ms(gd)
{
this->state = State_Finalized;
this->is_prev_inserted = false;
this->force_detection = false;
this->callback_info = {};
this->device_detection_event_callback = nullptr;
this->device_detection_event_callback_arg = nullptr;
}
void Initialize(CallbackInfo *ci);
void Finalize();
void PutToSleep();
void Awaken(bool force_det);
u32 GetDebounceMilliSeconds() const {
return this->gpio_debounce_ms;
}
bool IsInserted();
void RegisterDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg);
void UnregisterDetectionEventCallback();
};
}
#endif

View File

@@ -0,0 +1,352 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#include "sdmmc_gc_asic_device_accessor.hpp"
#include "sdmmc_timer.hpp"
namespace ams::sdmmc::impl {
#if defined(AMS_SDMMC_THREAD_SAFE)
#define AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX() std::scoped_lock lk(this->gc_asic_device.device_mutex)
#else
#define AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX()
#endif
#if defined(AMS_SDMMC_USE_OS_EVENTS)
#define AMS_SDMMC_CHECK_GC_ASIC_REMOVED() R_UNLESS(!this->gc_asic_device.IsRemoved(), sdmmc::ResultDeviceRemoved())
#else
#define AMS_SDMMC_CHECK_GC_ASIC_REMOVED()
#endif
Result GcAsicDeviceAccessor::IssueCommandWriteOperation(const void *op_buf, size_t op_buf_size) const {
/* Validate the operation buffer. */
AMS_ABORT_UNLESS(op_buf != nullptr);
AMS_ABORT_UNLESS(op_buf_size >= GcAsicOperationSize);
/* Issue the command. */
constexpr ResponseType CommandResponseType = ResponseType_R1;
Command command(CommandIndex_GcAsicWriteOperation, 0, CommandResponseType, false);
TransferData xfer_data(const_cast<void *>(op_buf), GcAsicOperationSize, 1, TransferDirection_WriteToDevice);
IHostController *hc = BaseDeviceAccessor::GetHostController();
Result result = hc->IssueCommand(std::addressof(command), std::addressof(xfer_data));
if (R_FAILED(result)) {
/* We failed to write operation. Check if we were removed. */
AMS_SDMMC_CHECK_GC_ASIC_REMOVED();
/* Determine what result we should return. */
Result return_result = result;
{
/* Issue a stop transmission command. */
u32 resp = 0;
result = hc->IssueStopTransmissionCommand(std::addressof(resp));
if (R_SUCCEEDED(result)) {
/* If we successfully stopped transmission but have an error status, we prefer to return that. */
result = this->gc_asic_device.CheckDeviceStatus(resp);
if (R_FAILED(result)) {
return_result = result;
}
}
/* Check again if we were removed. */
AMS_SDMMC_CHECK_GC_ASIC_REMOVED();
/* Request device status. */
u32 device_status;
result = BaseDeviceAccessor::IssueCommandSendStatus(std::addressof(device_status), 0);
/* If we got a device status error here and we didn't previously, we prefer to return that. */
if (!sdmmc::ResultDeviceStatusHasError::Includes(return_result) && sdmmc::ResultDeviceStatusHasError::Includes(result)) {
return_result = result;
}
}
return return_result;
}
/* Get the response. */
u32 resp;
hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType);
R_TRY(this->gc_asic_device.CheckDeviceStatus(resp));
return ResultSuccess();
}
Result GcAsicDeviceAccessor::IssueCommandFinishOperation() const {
/* Issue the command. */
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicFinishOperation, 0, true, DeviceState_Tran));
return ResultSuccess();
}
Result GcAsicDeviceAccessor::IssueCommandSleep() {
/* Issue the command. */
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicSleep, 0, true, DeviceState_Tran));
return ResultSuccess();
}
Result GcAsicDeviceAccessor::IssueCommandUpdateKey() const {
/* Issue the command. */
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicUpdateKey, 0, true, DeviceState_Tran));
return ResultSuccess();
}
Result GcAsicDeviceAccessor::StartupGcAsicDevice() {
/* Start up the host controller. */
IHostController *hc = BaseDeviceAccessor::GetHostController();
R_TRY(hc->Startup(BusPower_1_8V, BusWidth_8Bit, SpeedMode_GcAsicSpeed, false));
/* Wait 10 clocks for configuration to take. */
WaitClocks(10, hc->GetDeviceClockFrequencyKHz());
/* Perform tuning with command index 21. */
AMS_ABORT_UNLESS(hc->IsSupportedTuning());
R_TRY(hc->Tuning(SpeedMode_GcAsicSpeed, 21));
/* Set the device as low capacity/no memory. */
this->gc_asic_device.SetHighCapacity(false);
this->gc_asic_device.SetMemoryCapacity(0);
/* Enable power saving. */
hc->SetPowerSaving(true);
return ResultSuccess();
}
Result GcAsicDeviceAccessor::OnActivate() {
/* If we fail to start up the device, ensure the host controller is shut down. */
auto power_guard = SCOPE_GUARD { BaseDeviceAccessor::GetHostController()->Shutdown(); };
/* Try to start up the device. */
R_TRY(this->StartupGcAsicDevice());
/* We started up, so we don't need to power down. */
power_guard.Cancel();
return ResultSuccess();
}
Result GcAsicDeviceAccessor::OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) {
/* Check that we're not performing zero-byte rw. */
AMS_ABORT_UNLESS(num_sectors > 0);
/* Check that the buffer is big enough for the rw. */
AMS_ABORT_UNLESS((buf_size / SectorSize) >= num_sectors);
/* Perform the read/write. */
u32 num_transferred_blocks;
R_TRY(BaseDeviceAccessor::ReadWriteSingle(std::addressof(num_transferred_blocks), sector_index, num_sectors, buf, is_read));
/* Require that we read/wrote as many sectors as we expected. */
AMS_ABORT_UNLESS(num_transferred_blocks == num_sectors);
return ResultSuccess();
}
void GcAsicDeviceAccessor::Initialize() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* If we've already initialized, we don't need to do anything. */
if (this->is_initialized) {
return;
}
/* Set the base device to our gc asic device. */
BaseDeviceAccessor::SetDevice(std::addressof(this->gc_asic_device));
/* Initialize. */
IHostController *hc = BaseDeviceAccessor::GetHostController();
#if defined(AMS_SDMMC_USE_OS_EVENTS)
{
this->gc_asic_device.InitializeRemovedEvent();
hc->PreSetRemovedEvent(this->gc_asic_device.GetRemovedEvent());
}
#endif
hc->Initialize();
/* Mark ourselves as initialized. */
this->is_initialized = true;
}
void GcAsicDeviceAccessor::Finalize() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* If we've already finalized, we don't need to do anything. */
if (!this->is_initialized) {
return;
}
this->is_initialized = false;
/* Deactivate the device. */
BaseDeviceAccessor::Deactivate();
/* Finalize the host controller. */
BaseDeviceAccessor::GetHostController()->Finalize();
/* Finalize the removed event. */
#if defined(AMS_SDMMC_USE_OS_EVENTS)
{
this->gc_asic_device.FinalizeRemovedEvent();
}
#endif
}
Result GcAsicDeviceAccessor::GetSpeedMode(SpeedMode *out_speed_mode) const {
/* Check that we can write to output. */
AMS_ABORT_UNLESS(out_speed_mode != nullptr);
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* Check that we're accessible. */
R_TRY(this->gc_asic_device.CheckAccessible());
*out_speed_mode = SpeedMode_GcAsicSpeed;
return ResultSuccess();
}
void GcAsicDeviceAccessor::PutGcAsicToSleep() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* If the device isn't awake, we don't need to do anything. */
if (!this->gc_asic_device.IsAwake()) {
return;
}
/* If necessary, put the host controller to sleep. */
#if defined(AMS_SDMMC_USE_OS_EVENTS)
if (this->gc_asic_device.IsActive() && !this->gc_asic_device.IsRemoved())
#else
if (this->gc_asic_device.IsActive())
#endif
{
BaseDeviceAccessor::GetHostController()->PutToSleep();
}
/* Put the gc asic device to sleep. */
this->gc_asic_device.PutToSleep();
}
Result GcAsicDeviceAccessor::AwakenGcAsic() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* If the device is awake, we don't need to do anything. */
R_SUCCEED_IF(this->gc_asic_device.IsAwake());
/* Wake the device. */
this->gc_asic_device.Awaken();
/* Wake the host controller, if we need to.*/
#if defined(AMS_SDMMC_USE_OS_EVENTS)
if (this->gc_asic_device.IsActive() && !this->gc_asic_device.IsRemoved())
#else
if (this->gc_asic_device.IsActive())
#endif
{
R_TRY(BaseDeviceAccessor::GetHostController()->Awaken());
}
return ResultSuccess();
}
Result GcAsicDeviceAccessor::WriteGcAsicOperation(const void *op_buf, size_t op_buf_size) {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* Check that we're accessible. */
R_TRY(this->gc_asic_device.CheckAccessible());
/* Issue the command. */
R_TRY(this->IssueCommandWriteOperation(op_buf, op_buf_size));
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
return ResultSuccess();
}
Result GcAsicDeviceAccessor::FinishGcAsicOperation() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* Check that we're accessible. */
R_TRY(this->gc_asic_device.CheckAccessible());
/* Issue the command. */
R_TRY(this->IssueCommandFinishOperation());
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
return ResultSuccess();
}
Result GcAsicDeviceAccessor::AbortGcAsicOperation() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* Check that we're accessible. */
R_TRY(this->gc_asic_device.CheckAccessible());
/* Issue stop transmission command. */
u32 resp = 0;
R_TRY(BaseDeviceAccessor::GetHostController()->IssueStopTransmissionCommand(std::addressof(resp)));
R_TRY(this->gc_asic_device.CheckDeviceStatus(resp));
return ResultSuccess();
}
Result GcAsicDeviceAccessor::SleepGcAsic() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* Check that we're accessible. */
R_TRY(this->gc_asic_device.CheckAccessible());
/* Issue the command. */
R_TRY(this->IssueCommandSleep());
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
return ResultSuccess();
}
Result GcAsicDeviceAccessor::UpdateGcAsicKey() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* Check that we're accessible. */
R_TRY(this->gc_asic_device.CheckAccessible());
/* Issue the command. */
R_TRY(this->IssueCommandUpdateKey());
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
return ResultSuccess();
}
}

View File

@@ -0,0 +1,96 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include "sdmmc_base_device_accessor.hpp"
namespace ams::sdmmc::impl {
class GcAsicDevice : public BaseDevice {
private:
static constexpr u16 Rca = 0;
private:
#if defined(AMS_SDMMC_USE_OS_EVENTS)
mutable os::EventType removed_event;
#endif
public:
#if defined(AMS_SDMMC_USE_OS_EVENTS)
virtual os::EventType *GetRemovedEvent() const override {
return std::addressof(this->removed_event);
}
#endif
virtual DeviceType GetDeviceType() const override {
return DeviceType_GcAsic;
}
virtual u16 GetRca() const override {
return Rca;
}
};
class GcAsicDeviceAccessor : public BaseDeviceAccessor {
private:
GcAsicDevice gc_asic_device;
bool is_initialized;
private:
Result IssueCommandWriteOperation(const void *op_buf, size_t op_buf_size) const;
Result IssueCommandFinishOperation() const;
Result IssueCommandSleep();
Result IssueCommandUpdateKey() const;
Result StartupGcAsicDevice();
protected:
virtual Result OnActivate() override;
virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) override;
virtual Result ReStartup() override {
AMS_ABORT("Can't ReStartup GcAsic\n");
}
public:
virtual void Initialize() override;
virtual void Finalize() override;
virtual Result GetSpeedMode(SpeedMode *out_speed_mode) const override;
public:
explicit GcAsicDeviceAccessor(IHostController *hc) : BaseDeviceAccessor(hc), is_initialized(false) {
/* ... */
}
void PutGcAsicToSleep();
Result AwakenGcAsic();
Result WriteGcAsicOperation(const void *op_buf, size_t op_buf_size);
Result FinishGcAsicOperation();
Result AbortGcAsicOperation();
Result SleepGcAsic();
Result UpdateGcAsicKey();
void SignalGcRemovedEvent() {
#if defined(AMS_SDMMC_USE_OS_EVENTS)
this->gc_asic_device.SignalRemovedEvent();
#else
AMS_ABORT("SignalGcRemovedEvent called without event support\n");
#endif
}
void ClearGcRemovedEvent() {
#if defined(AMS_SDMMC_USE_OS_EVENTS)
this->gc_asic_device.ClearRemovedEvent();
#else
AMS_ABORT("ClearGcRemovedEvent called without event support\n");
#endif
}
};
}

View File

@@ -0,0 +1,49 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include "sdmmc_i_host_controller.hpp"
namespace ams::sdmmc::impl {
class IDeviceAccessor {
public:
virtual void Initialize() = 0;
virtual void Finalize() = 0;
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0;
virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0;
#endif
virtual Result Activate() = 0;
virtual void Deactivate() = 0;
virtual Result ReadWrite(u32 sector_index, u32 num_sectors, void *buffer, size_t buffer_size, bool is_read) = 0;
virtual Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width) = 0;
virtual Result GetSpeedMode(SpeedMode *out) const = 0;
virtual Result GetMemoryCapacity(u32 *out_sectors) const = 0;
virtual Result GetDeviceStatus(u32 *out) const = 0;
virtual Result GetOcr(u32 *out) const = 0;
virtual Result GetRca(u16 *out) const = 0;
virtual Result GetCid(void *out, size_t size) const = 0;
virtual Result GetCsd(void *out, size_t size) const = 0;
virtual void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) = 0;
};
}

View File

@@ -0,0 +1,196 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#if defined(AMS_SDMMC_USE_OS_EVENTS)
#include <stratosphere/os.hpp>
#endif
namespace ams::sdmmc::impl {
enum ResponseType {
ResponseType_R0 = 0,
ResponseType_R1 = 1,
ResponseType_R2 = 2,
ResponseType_R3 = 3,
ResponseType_R6 = 4,
ResponseType_R7 = 5,
};
enum TransferDirection {
TransferDirection_ReadFromDevice = 0,
TransferDirection_WriteToDevice = 1,
};
enum CommandIndex {
/* Generic commands. */
CommandIndex_GoIdleState = 0,
CommandIndex_SendOpCond = 1,
CommandIndex_AllSendCid = 2,
CommandIndex_SendRelativeAddr = 3,
CommandIndex_SetRelativeAddr = 3,
CommandIndex_SetDsr = 4,
CommandIndex_Switch = 6,
CommandIndex_SelectCard = 7,
CommandIndex_DeselectCard = 7,
CommandIndex_SendIfCond = 8,
CommandIndex_SendExtCsd = 8,
CommandIndex_SendCsd = 9,
CommandIndex_SendCid = 10,
CommandIndex_VoltageSwitch = 11,
CommandIndex_StopTransmission = 12,
CommandIndex_SendStatus = 13,
CommandIndex_SendTaskStatus = 13,
CommandIndex_GoInactiveState = 15,
CommandIndex_SetBlockLen = 16,
CommandIndex_ReadSingleBlock = 17,
CommandIndex_ReadMultipleBlock = 18,
CommandIndex_SendTuningBlock = 19,
CommandIndex_SpeedClassControl = 20,
CommandIndex_AddressExtension = 22,
CommandIndex_SetBlockCount = 23,
CommandIndex_WriteBlock = 24,
CommandIndex_WriteMultipleBlock = 25,
CommandIndex_ProgramCsd = 27,
CommandIndex_SetWriteProt = 28,
CommandIndex_ClearWriteProt = 29,
CommandIndex_SendWriteProt = 30,
CommandIndex_EraseWriteBlockStart = 32,
CommandIndex_EraseWriteBlockEnd = 33,
CommandIndex_EraseGroupStart = 35,
CommandIndex_EraseGroupEnd = 36,
CommandIndex_Erase = 38,
CommandIndex_LockUnlock = 42,
CommandIndex_AppCmd = 55,
CommandIndex_GenCmd = 56,
/* Nintendo specific vendor commands for lotus3. */
CommandIndex_GcAsicWriteOperation = 60,
CommandIndex_GcAsicFinishOperation = 61,
CommandIndex_GcAsicSleep = 62,
CommandIndex_GcAsicUpdateKey = 63,
};
struct Command {
u32 command_index;
u32 command_argument;
ResponseType response_type;
bool is_busy;
constexpr Command(u32 ci, u32 ca, ResponseType r, bool b) : command_index(ci), command_argument(ca), response_type(r), is_busy(b) { /* ... */ }
};
struct TransferData {
void *buffer;
size_t block_size;
u32 num_blocks;
TransferDirection transfer_direction;
bool is_multi_block_transfer;
bool is_stop_transmission_command_enabled;
constexpr TransferData(void *b, size_t bs, u32 nb, TransferDirection xd, bool mb, bool st)
: buffer(b), block_size(bs), num_blocks(nb), transfer_direction(xd), is_multi_block_transfer(mb), is_stop_transmission_command_enabled(st)
{
if (this->num_blocks > 1) {
AMS_ABORT_UNLESS(this->is_multi_block_transfer);
}
}
constexpr TransferData(void *b, size_t bs, u32 nb, TransferDirection xd)
: buffer(b), block_size(bs), num_blocks(nb), transfer_direction(xd), is_multi_block_transfer(false), is_stop_transmission_command_enabled(false)
{
AMS_ABORT_UNLESS(this->num_blocks == 1);
}
};
class IHostController {
public:
#if defined(AMS_SDMMC_USE_OS_EVENTS)
virtual void PreSetRemovedEvent(ams::os::EventType *event) = 0;
#endif
virtual void Initialize() = 0;
virtual void Finalize() = 0;
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0;
virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0;
#endif
virtual void SetWorkBuffer(void *wb, size_t wb_size) = 0;
virtual Result Startup(BusPower bus_power, BusWidth bus_width, SpeedMode speed_mode, bool power_saving_enable) = 0;
virtual void Shutdown();
virtual void PutToSleep();
virtual Result Awaken();
virtual Result SwitchToSdr12();
virtual bool IsSupportedBusPower(BusPower bus_power) const = 0;
virtual BusPower GetBusPower() const = 0;
virtual bool IsSupportedBusWidth(BusWidth bus_width) const = 0;
virtual void SetBusWidth(BusWidth bus_width) = 0;
virtual BusWidth GetBusWidth() const = 0;
virtual Result SetSpeedMode(SpeedMode speed_mode) = 0;
virtual SpeedMode GetSpeedMode() const = 0;
virtual u32 GetDeviceClockFrequencyKHz() const = 0;
virtual void SetPowerSaving(bool en) = 0;
virtual bool IsPowerSavingEnable() const = 0;
virtual void EnableDeviceClock() = 0;
virtual void DisableDeviceClock() = 0;
virtual bool IsDeviceClockEnable() const = 0;
virtual u32 GetMaxTransferNumBlocks() const = 0;
virtual void ChangeCheckTransferInterval(u32 ms) = 0;
virtual void SetDefaultCheckTransferInterval() = 0;
virtual Result IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) = 0;
virtual Result IssueStopTransmissionCommand(u32 *out_response) = 0;
ALWAYS_INLINE Result IssueCommand(const Command *command, TransferData *xfer_data) {
return this->IssueCommand(command, xfer_data, nullptr);
}
ALWAYS_INLINE Result IssueCommand(const Command *command) {
return this->IssueCommand(command, nullptr, nullptr);
}
virtual void GetLastResponse(u32 *out_response, size_t response_size, ResponseType response_type) const = 0;
virtual void GetLastStopTransmissionResponse(u32 *out_response, size_t response_size) const = 0;
virtual bool IsSupportedTuning() const = 0;
virtual Result Tuning(SpeedMode speed_mode, u32 command_index) = 0;
virtual void SaveTuningStatusForHs400() = 0;
virtual Result GetInternalStatus() const = 0;
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,62 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::sdmmc::impl {
Result SetSdCardVoltageEnabled(bool en);
Result SetSdCardVoltageValue(u32 micro_volts);
namespace pinmux_impl {
enum PinAssignment {
PinAssignment_Sdmmc1OutputHigh = 2,
PinAssignment_Sdmmc1ResetState = 3,
PinAssignment_Sdmmc1SchmtEnable = 4,
PinAssignment_Sdmmc1SchmtDisable = 5,
};
void SetPinAssignment(PinAssignment assignment);
}
namespace gpio_impl {
enum GpioValue {
GpioValue_Low = 0,
GpioValue_High = 1
};
enum Direction {
Direction_Input = 0,
Direction_Output = 1,
};
enum GpioPadName {
GpioPadName_PowSdEn = 2,
};
void OpenSession(GpioPadName pad);
void CloseSession(GpioPadName pad);
void SetDirection(GpioPadName pad, Direction direction);
void SetValue(GpioPadName pad, GpioValue value);
}
}

View File

@@ -0,0 +1,728 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#include "sdmmc_mmc_device_accessor.hpp"
#include "sdmmc_timer.hpp"
namespace ams::sdmmc::impl {
#if defined(AMS_SDMMC_THREAD_SAFE)
#define AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX() std::scoped_lock lk(this->mmc_device.device_mutex)
#else
#define AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX()
#endif
namespace {
constexpr inline u32 OcrCardPowerUpStatus = (1 << 31);
constexpr inline u32 OcrAccessMode_Mask = (3 << 29);
constexpr inline u32 OcrAccessMode_SectorMode = (2 << 29);
constexpr inline u8 ManufacturerId_Toshiba = 0x11;
enum DeviceType : u8 {
DeviceType_HighSpeed26MHz = (1u << 0),
DeviceType_HighSpeed52MHz = (1u << 1),
DeviceType_HighSpeedDdr52MHz1_8VOr3_0V = (1u << 2),
DeviceType_HighSpeedDdr52MHz1_2V = (1u << 3),
DeviceType_Hs200Sdr200MHz1_8V = (1u << 4),
DeviceType_Hs200Sdr200MHz1_2V = (1u << 5),
DeviceType_Hs400Sdr200MHz1_8V = (1u << 6),
DeviceType_Hs400Sdr200MHz1_2V = (1u << 7),
};
constexpr bool IsToshibaMmc(const u8 *cid) {
/* Check whether the CID's manufacturer id field is Toshiba. */
AMS_ABORT_UNLESS(cid != nullptr);
return cid[14] == ManufacturerId_Toshiba;
}
constexpr bool IsLessThanSpecification4(const u8 *csd) {
const u8 spec_vers = ((csd[14] >> 2) & 0xF);
return spec_vers < 4;
}
constexpr bool IsBkopAutoEnable(const u8 *ext_csd) {
/* Check the AUTO_EN bit of BKOPS_EN. */
return (ext_csd[163] & (1u << 1)) != 0;
}
constexpr u8 GetDeviceType(const u8 *ext_csd) {
/* Get the DEVICE_TYPE register. */
AMS_ABORT_UNLESS(ext_csd != nullptr);
return ext_csd[196];
}
constexpr bool IsSupportedHs400(u8 device_type) {
return (device_type & DeviceType_Hs400Sdr200MHz1_8V) != 0;
}
constexpr bool IsSupportedHs200(u8 device_type) {
return (device_type & DeviceType_Hs200Sdr200MHz1_8V) != 0;
}
constexpr bool IsSupportedHighSpeed(u8 device_type) {
return (device_type & DeviceType_HighSpeed52MHz) != 0;
}
constexpr u32 GetMemoryCapacityFromExtCsd(const u32 *ext_csd) {
/* Get the SEC_COUNT register. */
AMS_ABORT_UNLESS(ext_csd != nullptr);
return ext_csd[212 / sizeof(u32)];
}
constexpr u32 GetBootPartitionMemoryCapacityFromExtCsd(const u8 *ext_csd) {
/* Get the BOOT_SIZE_MULT register. */
AMS_ABORT_UNLESS(ext_csd != nullptr);
return ext_csd[226] * (128_KB / SectorSize);
}
constexpr Result GetCurrentSpeedModeFromExtCsd(SpeedMode *out, const u8 *ext_csd) {
/* Get the HS_TIMING register. */
AMS_ABORT_UNLESS(out != nullptr);
AMS_ABORT_UNLESS(ext_csd != nullptr);
switch (ext_csd[185] & 0xF) {
case 0: *out = SpeedMode_MmcLegacySpeed; break;
case 1: *out = SpeedMode_MmcHighSpeed; break;
case 2: *out = SpeedMode_MmcHs200; break;
case 3: *out = SpeedMode_MmcHs400; break;
default: return sdmmc::ResultUnexpectedMmcExtendedCsdValue();
}
return ResultSuccess();
}
}
void MmcDevice::SetOcrAndHighCapacity(u32 ocr) {
/* Set ocr. */
BaseDevice::SetOcr(ocr);
/* Set high capacity. */
BaseDevice::SetHighCapacity((ocr & OcrAccessMode_Mask) == OcrAccessMode_SectorMode);
}
Result MmcDeviceAccessor::IssueCommandSendOpCond(u32 *out_ocr, BusPower bus_power) const {
/* Get the command argument. */
u32 arg = OcrAccessMode_SectorMode;
switch (bus_power) {
case BusPower_1_8V: arg |= 0x000080; break;
case BusPower_3_3V: arg |= 0x03F800; break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Issue the command. */
constexpr ResponseType CommandResponseType = ResponseType_R3;
Command command(CommandIndex_SendOpCond, arg, CommandResponseType, false);
IHostController *hc = BaseDeviceAccessor::GetHostController();
R_TRY(hc->IssueCommand(std::addressof(command)));
/* Get the response. */
hc->GetLastResponse(out_ocr, sizeof(*out_ocr), CommandResponseType);
return ResultSuccess();
}
Result MmcDeviceAccessor::IssueCommandSetRelativeAddr() const {
/* Get rca. */
const u32 rca = this->mmc_device.GetRca();
AMS_ABORT_UNLESS(rca > 0);
/* Issue comamnd. */
const u32 arg = rca << 16;
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_SetRelativeAddr, arg, false, DeviceState_Unknown));
return ResultSuccess();
}
Result MmcDeviceAccessor::IssueCommandSwitch(CommandSwitch cs) const {
/* Get the command argument. */
const u32 arg = GetCommandSwitchArgument(cs);
/* Issue the command. */
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_Switch, arg, true, DeviceState_Unknown));
return ResultSuccess();
}
Result MmcDeviceAccessor::IssueCommandSendExtCsd(void *dst, size_t dst_size) const {
/* Validate the output buffer. */
AMS_ABORT_UNLESS(dst != nullptr);
AMS_ABORT_UNLESS(dst_size >= MmcExtendedCsdSize);
/* Issue the command. */
constexpr ResponseType CommandResponseType = ResponseType_R1;
Command command(CommandIndex_SendExtCsd, 0, CommandResponseType, false);
TransferData xfer_data(dst, MmcExtendedCsdSize, 1, TransferDirection_ReadFromDevice);
IHostController *hc = BaseDeviceAccessor::GetHostController();
R_TRY(hc->IssueCommand(std::addressof(command), std::addressof(xfer_data)));
/* Get the response. */
u32 resp;
hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType);
R_TRY(this->mmc_device.CheckDeviceStatus(resp));
return ResultSuccess();
}
Result MmcDeviceAccessor::IssueCommandEraseGroupStart(u32 sector_index) const {
/* Get the command argument. */
const u32 arg = this->mmc_device.IsHighCapacity() ? sector_index : sector_index * SectorSize;
/* Issue the command. */
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_EraseGroupStart, arg, false, DeviceState_Unknown));
return ResultSuccess();
}
Result MmcDeviceAccessor::IssueCommandEraseGroupEnd(u32 sector_index) const {
/* Get the command argument. */
const u32 arg = this->mmc_device.IsHighCapacity() ? sector_index : sector_index * SectorSize;
/* Issue the command. */
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_EraseGroupEnd, arg, false, DeviceState_Tran));
return ResultSuccess();
}
Result MmcDeviceAccessor::IssueCommandErase() const {
/* Issue the command. */
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_Erase, 0, false, DeviceState_Tran));
return ResultSuccess();
}
Result MmcDeviceAccessor::CancelToshibaMmcModel() {
/* Special erase sequence done by Nintendo on Toshiba MMCs. */
R_TRY(this->IssueCommandSwitch(CommandSwitch_SetBitsProductionStateAwarenessEnable));
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
R_TRY(this->IssueCommandSwitch(CommandSwitch_ClearBitsAutoModeEnable));
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteProductionStateAwarenessPreSolderingWrites));
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteProductionStateAwarenessPreSolderingPostWrites));
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteProductionStateAwarenessNormal));
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
return ResultSuccess();
}
Result MmcDeviceAccessor::ChangeToReadyState(BusPower bus_power) {
/* Be prepared to wait up to 1.5 seconds to change state. */
ManualTimer timer(1500);
while (true) {
/* Get the ocr, and check if we're done. */
u32 ocr;
R_TRY(this->IssueCommandSendOpCond(std::addressof(ocr), bus_power));
if ((ocr & OcrCardPowerUpStatus) != 0) {
this->mmc_device.SetOcrAndHighCapacity(ocr);
return ResultSuccess();
}
/* Check if we've timed out. */
R_UNLESS(timer.Update(), sdmmc::ResultMmcInitializationSoftwareTimeout());
/* Try again in 1ms. */
WaitMicroSeconds(1000);
}
}
Result MmcDeviceAccessor::ExtendBusWidth(BusWidth max_bw) {
/* If the maximum bus width is 1bit, we can't extend. */
R_SUCCEED_IF(max_bw == BusWidth_1Bit);
/* Determine what bus width to switch to. */
IHostController *hc = BaseDeviceAccessor::GetHostController();
BusWidth target_bw = BusWidth_1Bit;
CommandSwitch cs;
if (max_bw == BusWidth_8Bit && hc->IsSupportedBusWidth(BusWidth_8Bit)) {
target_bw = BusWidth_8Bit;
cs = CommandSwitch_WriteBusWidth8Bit;
} else if ((max_bw == BusWidth_8Bit || max_bw == BusWidth_4Bit) && hc->IsSupportedBusWidth(BusWidth_4Bit)) {
target_bw = BusWidth_4Bit;
cs = CommandSwitch_WriteBusWidth4Bit;
} else {
/* Target bus width is 1bit. */
return ResultSuccess();
}
/* Set the bus width. */
R_TRY(this->IssueCommandSwitch(cs));
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
hc->SetBusWidth(target_bw);
return ResultSuccess();
}
Result MmcDeviceAccessor::EnableBkopsAuto() {
/* Issue the command. */
R_TRY(this->IssueCommandSwitch(CommandSwitch_SetBitsBkopsEnAutoEn));
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
return ResultSuccess();
}
Result MmcDeviceAccessor::ChangeToHighSpeed(bool check_before) {
/* Issue high speed command. */
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteHsTimingHighSpeed));
/* If we should check status before setting mode, do so. */
if (check_before) {
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
}
/* Set the host controller to high speed. */
R_TRY(BaseDeviceAccessor::GetHostController()->SetSpeedMode(SpeedMode_MmcHighSpeed));
/* If we should check status after setting mode, do so. */
if (!check_before) {
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
}
return ResultSuccess();
}
Result MmcDeviceAccessor::ChangeToHs200() {
/* Issue Hs200 command. */
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteHsTimingHs200));
/* Set the host controller to Hs200. */
IHostController *hc = BaseDeviceAccessor::GetHostController();
R_TRY(hc->SetSpeedMode(SpeedMode_MmcHs200));
/* Perform tuning using command index 21. */
R_TRY(hc->Tuning(SpeedMode_MmcHs200, 21));
/* Check status. */
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
return ResultSuccess();
}
Result MmcDeviceAccessor::ChangeToHs400() {
/* Change first to Hs200. */
R_TRY(this->ChangeToHs200());
/* Save tuning status. */
IHostController *hc = BaseDeviceAccessor::GetHostController();
hc->SaveTuningStatusForHs400();
/* Change to high speed. */
R_TRY(this->ChangeToHighSpeed(false));
/* Issue Hs400 command. */
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteBusWidth8BitDdr));
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteHsTimingHs400));
/* Set the host controller to Hs400. */
R_TRY(hc->SetSpeedMode(SpeedMode_MmcHs400));
/* Check status. */
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
return ResultSuccess();
}
Result MmcDeviceAccessor::ExtendBusSpeed(u8 device_type, SpeedMode max_sm) {
/* We want to switch to the highest speed we can. */
/* Check Hs400/Hs200 first. */
IHostController *hc = BaseDeviceAccessor::GetHostController();
if (hc->IsSupportedTuning() && hc->GetBusPower() == BusPower_1_8V) {
if (hc->GetBusWidth() == BusWidth_8Bit && IsSupportedHs400(device_type) && max_sm == SpeedMode_MmcHs400) {
return this->ChangeToHs400();
} else if ((hc->GetBusWidth() == BusWidth_8Bit || hc->GetBusWidth() == BusWidth_4Bit) && IsSupportedHs200(device_type) && (max_sm == SpeedMode_MmcHs400 || max_sm == SpeedMode_MmcHs200)) {
return this->ChangeToHs200();
}
}
/* Check if we can switch to high speed. */
if (IsSupportedHighSpeed(device_type)) {
return this->ChangeToHighSpeed(true);
}
/* We can't, so stay at normal speeds. */
return ResultSuccess();
}
Result MmcDeviceAccessor::StartupMmcDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size) {
/* Start up at an appropriate bus power. */
IHostController *hc = BaseDeviceAccessor::GetHostController();
const BusPower bp = hc->IsSupportedBusPower(BusPower_1_8V) ? BusPower_1_8V : BusPower_3_3V;
R_TRY(hc->Startup(bp, BusWidth_1Bit, SpeedMode_MmcIdentification, false));
/* Wait 1ms for configuration to take. */
WaitMicroSeconds(1000);
/* Wait an additional 74 clocks for configuration to take. */
WaitClocks(74, hc->GetDeviceClockFrequencyKHz());
/* Go to idle state. */
R_TRY(BaseDeviceAccessor::IssueCommandGoIdleState());
this->current_partition = MmcPartition_UserData;
/* Go to ready state. */
R_TRY(this->ChangeToReadyState(bp));
/* Get the CID. */
R_TRY(BaseDeviceAccessor::IssueCommandAllSendCid(wb, wb_size));
this->mmc_device.SetCid(wb, wb_size);
const bool is_toshiba = IsToshibaMmc(static_cast<const u8 *>(wb));
/* Issue set relative addr. */
R_TRY(this->IssueCommandSetRelativeAddr());
/* Get the CSD. */
R_TRY(BaseDeviceAccessor::IssueCommandSendCsd(wb, wb_size));
this->mmc_device.SetCsd(wb, wb_size);
const bool spec_under_4 = IsLessThanSpecification4(static_cast<const u8 *>(wb));
/* Set the speed mode to legacy. */
R_TRY(hc->SetSpeedMode(SpeedMode_MmcLegacySpeed));
/* Issue select card command. */
R_TRY(BaseDeviceAccessor::IssueCommandSelectCard());
/* Set block length to sector size. */
R_TRY(BaseDeviceAccessor::IssueCommandSetBlockLenToSectorSize());
/* If the device SPEC_VERS is less than 4, extended csd/switch aren't supported. */
if (spec_under_4) {
R_TRY(this->mmc_device.SetLegacyMemoryCapacity());
this->mmc_device.SetActive();
return ResultSuccess();
}
/* Extend the bus width to the largest that we can. */
R_TRY(this->ExtendBusWidth(max_bw));
/* Get the extended csd. */
R_TRY(this->IssueCommandSendExtCsd(wb, wb_size));
AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast<uintptr_t>(wb), alignof(u32)));
this->mmc_device.SetMemoryCapacity(GetMemoryCapacityFromExtCsd(static_cast<const u32 *>(wb)));
/* If the mmc is manufactured by toshiba, try to enable bkops auto. */
if (is_toshiba && !IsBkopAutoEnable(static_cast<const u8 *>(wb))) {
/* NOTE: Nintendo does not check the result of this. */
this->EnableBkopsAuto();
}
/* Extend the bus speed to as fast as we can. */
const u8 device_type = GetDeviceType(static_cast<const u8 *>(wb));
R_TRY(this->ExtendBusSpeed(device_type, max_sm));
/* Enable power saving. */
hc->SetPowerSaving(true);
return ResultSuccess();
}
Result MmcDeviceAccessor::OnActivate() {
/* Define the possible startup parameters. */
constexpr const struct {
BusWidth bus_width;
SpeedMode speed_mode;
} StartupParameters[] = {
#if defined(AMS_SDMMC_ENABLE_MMC_HS400)
{ BusWidth_8Bit, SpeedMode_MmcHs400 },
#else
{ BusWidth_8Bit, SpeedMode_MmcHighSpeed },
#endif
{ BusWidth_8Bit, SpeedMode_MmcHighSpeed },
{ BusWidth_1Bit, SpeedMode_MmcHighSpeed },
};
/* Try to start up with each set of parameters. */
Result result;
for (int i = 0; i < static_cast<int>(util::size(StartupParameters)); ++i) {
/* Alias the parameters. */
const auto &params = StartupParameters[i];
/* Set our max bus width/speed mode. */
this->max_bus_width = params.bus_width;
this->max_speed_mode = params.speed_mode;
/* Try to start up the device. */
result = this->StartupMmcDevice(this->max_bus_width, this->max_speed_mode, this->work_buffer, this->work_buffer_size);
if (R_SUCCEEDED(result)) {
/* If we previously failed to start up the device, log the error correction. */
if (i != 0) {
BaseDeviceAccessor::PushErrorLog(true, "S %d %d:0", this->max_bus_width, this->max_speed_mode);
BaseDeviceAccessor::IncrementNumActivationErrorCorrections();
}
return ResultSuccess();
}
/* Log that our startup failed. */
BaseDeviceAccessor::PushErrorLog(false, "S %d %d:%X", this->max_bus_width, this->max_speed_mode, result.GetValue());
/* Shut down the host controller before we try to start up again. */
BaseDeviceAccessor::GetHostController()->Shutdown();
}
/* We failed to start up with all sets of parameters. */
BaseDeviceAccessor::PushErrorTimeStamp();
return result;
}
Result MmcDeviceAccessor::OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) {
/* Get the sector index alignment. */
u32 sector_index_alignment = 0;
if (!is_read) {
constexpr u32 MmcWriteSectorAlignment = 16_KB / SectorSize;
sector_index_alignment = MmcWriteSectorAlignment;
AMS_ABORT_UNLESS(util::IsAligned(sector_index, MmcWriteSectorAlignment));
}
/* Do the read/write. */
return BaseDeviceAccessor::ReadWriteMultiple(sector_index, num_sectors, sector_index_alignment, buf, buf_size, is_read);
}
Result MmcDeviceAccessor::ReStartup() {
/* Shut down the host controller. */
BaseDeviceAccessor::GetHostController()->Shutdown();
/* Perform start up. */
Result result = this->StartupMmcDevice(this->max_bus_width, this->max_speed_mode, this->work_buffer, this->work_buffer_size);
if (R_FAILED(result)) {
BaseDeviceAccessor::PushErrorLog(false, "S %d %d:%X", this->max_bus_width, this->max_speed_mode, result.GetValue());
return result;
}
return ResultSuccess();
}
void MmcDeviceAccessor::Initialize() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
/* If we've already initialized, we don't need to do anything. */
if (this->is_initialized) {
return;
}
/* Set the base device to our mmc device. */
BaseDeviceAccessor::SetDevice(std::addressof(this->mmc_device));
/* Initialize. */
BaseDeviceAccessor::GetHostController()->Initialize();
this->is_initialized = true;
}
void MmcDeviceAccessor::Finalize() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
/* If we've already finalized, we don't need to do anything. */
if (!this->is_initialized) {
return;
}
this->is_initialized = false;
/* Deactivate the device. */
BaseDeviceAccessor::Deactivate();
/* Finalize the host controller. */
BaseDeviceAccessor::GetHostController()->Finalize();
}
Result MmcDeviceAccessor::GetSpeedMode(SpeedMode *out_speed_mode) const {
/* Check that we can write to output. */
AMS_ABORT_UNLESS(out_speed_mode != nullptr);
/* Get the current speed mode from the ext csd. */
R_TRY(GetMmcExtendedCsd(this->work_buffer, this->work_buffer_size));
R_TRY(GetCurrentSpeedModeFromExtCsd(out_speed_mode, static_cast<const u8 *>(this->work_buffer)));
return ResultSuccess();
}
void MmcDeviceAccessor::PutMmcToSleep() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
/* If the device isn't awake, we don't need to do anything. */
if (!this->mmc_device.IsAwake()) {
return;
}
/* Put the device to sleep. */
this->mmc_device.PutToSleep();
/* If necessary, put the host controller to sleep. */
if (this->mmc_device.IsActive()) {
BaseDeviceAccessor::GetHostController()->PutToSleep();
}
}
void MmcDeviceAccessor::AwakenMmc() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
/* If the device is awake, we don't need to do anything. */
if (this->mmc_device.IsAwake()) {
return;
}
/* Wake the host controller, if we need to.*/
if (this->mmc_device.IsActive()) {
const Result result = BaseDeviceAccessor::GetHostController()->Awaken();
if (R_FAILED(result)) {
BaseDeviceAccessor::PushErrorLog(true, "A:%X", result.GetValue());
}
}
/* Wake the device. */
this->mmc_device.Awaken();
}
Result MmcDeviceAccessor::SelectMmcPartition(MmcPartition part) {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
/* Check that we can access the device. */
R_TRY(this->mmc_device.CheckAccessible());
/* Determine the appropriate SWITCH subcommand. */
CommandSwitch cs;
switch (part) {
case MmcPartition_UserData: cs = CommandSwitch_WritePartitionAccessDefault; break;
case MmcPartition_BootPartition1: cs = CommandSwitch_WritePartitionAccessRwBootPartition1; break;
case MmcPartition_BootPartition2: cs = CommandSwitch_WritePartitionAccessRwBootPartition2; break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Change partition. */
this->current_partition = MmcPartition_Unknown;
{
R_TRY(this->IssueCommandSwitch(cs));
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
}
this->current_partition = part;
return ResultSuccess();
}
Result MmcDeviceAccessor::EraseMmc() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
/* Check that we can access the device. */
R_TRY(this->mmc_device.CheckAccessible());
/* Get the partition capacity. */
u32 part_capacity;
switch (this->current_partition) {
case MmcPartition_UserData:
part_capacity = this->mmc_device.GetMemoryCapacity();
break;
case MmcPartition_BootPartition1:
case MmcPartition_BootPartition2:
R_TRY(this->GetMmcBootPartitionCapacity(std::addressof(part_capacity)));
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Begin the erase. */
R_TRY(this->IssueCommandEraseGroupStart(0));
R_TRY(this->IssueCommandEraseGroupEnd(part_capacity - 1));
/* Issue the erase command, allowing 30 seconds for it to complete. */
ManualTimer timer(30000);
Result result = this->IssueCommandErase();
R_TRY_CATCH(result) {
R_CATCH(sdmmc::ResultDataTimeoutError) { /* Data timeout error is acceptable. */ }
R_CATCH(sdmmc::ResultCommandCompleteSoftwareTimeout) { /* Command complete software timeout error is acceptable. */ }
R_CATCH(sdmmc::ResultBusySoftwareTimeout) { /* Busy software timeout error is acceptable. */ }
} R_END_TRY_CATCH;
/* Wait for the erase to finish. */
while (true) {
/* Check if we're done. */
result = BaseDeviceAccessor::IssueCommandSendStatus();
if (R_SUCCEEDED(result)) {
break;
}
/* Otherwise, check if we should reject the error. */
if (!sdmmc::ResultUnexpectedDeviceState::Includes(result)) {
return result;
}
/* Check if timeout has been exceeded. */
R_UNLESS(timer.Update(), sdmmc::ResultMmcEraseSoftwareTimeout());
}
/* If the partition is user data, check if we need to perform toshiba-specific erase. */
if (this->current_partition == MmcPartition_UserData) {
u8 cid[DeviceCidSize];
this->mmc_device.GetCid(cid, sizeof(cid));
if (IsToshibaMmc(cid)) {
/* NOTE: Nintendo does not check the result of this operation. */
this->CancelToshibaMmcModel();
}
}
return ResultSuccess();
}
Result MmcDeviceAccessor::GetMmcBootPartitionCapacity(u32 *out_num_sectors) const {
/* Get the capacity from the extended csd. */
AMS_ABORT_UNLESS(out_num_sectors != nullptr);
R_TRY(this->GetMmcExtendedCsd(this->work_buffer, this->work_buffer_size));
*out_num_sectors = GetBootPartitionMemoryCapacityFromExtCsd(static_cast<const u8 *>(this->work_buffer));
return ResultSuccess();
}
Result MmcDeviceAccessor::GetMmcExtendedCsd(void *dst, size_t dst_size) const {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
/* Check that we can access the device. */
R_TRY(this->mmc_device.CheckAccessible());
/* Get the csd. */
u8 csd[DeviceCsdSize];
this->mmc_device.GetCsd(csd, sizeof(csd));
/* Check that the card supports ext csd. */
R_UNLESS(!IsLessThanSpecification4(csd), sdmmc::ResultMmcNotSupportExtendedCsd());
/* Get the ext csd. */
R_TRY(this->IssueCommandSendExtCsd(dst, dst_size));
return ResultSuccess();
}
}

View File

@@ -0,0 +1,143 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include "sdmmc_base_device_accessor.hpp"
namespace ams::sdmmc::impl {
class MmcDevice : public BaseDevice {
private:
static constexpr u16 Rca = 2;
public:
#if defined(AMS_SDMMC_USE_OS_EVENTS)
virtual os::EventType *GetRemovedEvent() const override {
/* Mmc can't be removed. */
return nullptr;
}
#endif
virtual DeviceType GetDeviceType() const override {
return DeviceType_Mmc;
}
virtual u16 GetRca() const override {
return Rca;
}
void SetOcrAndHighCapacity(u32 ocr);
};
class MmcDeviceAccessor : public BaseDeviceAccessor {
private:
MmcDevice mmc_device;
void *work_buffer;
size_t work_buffer_size;
BusWidth max_bus_width;
SpeedMode max_speed_mode;
MmcPartition current_partition;
bool is_initialized;
private:
enum CommandSwitch {
CommandSwitch_SetBitsProductionStateAwarenessEnable = 0,
CommandSwitch_ClearBitsAutoModeEnable = 1,
CommandSwitch_WriteProductionStateAwarenessNormal = 2,
CommandSwitch_WriteProductionStateAwarenessPreSolderingWrites = 3,
CommandSwitch_WriteProductionStateAwarenessPreSolderingPostWrites = 4,
CommandSwitch_SetBitsBkopsEnAutoEn = 5,
CommandSwitch_WriteBusWidth1Bit = 6,
CommandSwitch_WriteBusWidth4Bit = 7,
CommandSwitch_WriteBusWidth8Bit = 8,
CommandSwitch_WriteBusWidth8BitDdr = 9,
CommandSwitch_WriteHsTimingLegacySpeed = 10,
CommandSwitch_WriteHsTimingHighSpeed = 11,
CommandSwitch_WriteHsTimingHs200 = 12,
CommandSwitch_WriteHsTimingHs400 = 13,
CommandSwitch_WritePartitionAccessDefault = 14,
CommandSwitch_WritePartitionAccessRwBootPartition1 = 15,
CommandSwitch_WritePartitionAccessRwBootPartition2 = 16,
};
static constexpr ALWAYS_INLINE u32 GetCommandSwitchArgument(CommandSwitch cs) {
switch (cs) {
case CommandSwitch_SetBitsProductionStateAwarenessEnable: return 0x01111000;
case CommandSwitch_ClearBitsAutoModeEnable: return 0x02112000;
case CommandSwitch_WriteProductionStateAwarenessNormal: return 0x03850000;
case CommandSwitch_WriteProductionStateAwarenessPreSolderingWrites: return 0x03850100;
case CommandSwitch_WriteProductionStateAwarenessPreSolderingPostWrites: return 0x03850200;
case CommandSwitch_SetBitsBkopsEnAutoEn: return 0x01A30200;
case CommandSwitch_WriteBusWidth1Bit: return 0x03B70000;
case CommandSwitch_WriteBusWidth4Bit: return 0x03B70100;
case CommandSwitch_WriteBusWidth8Bit: return 0x03B70200;
case CommandSwitch_WriteBusWidth8BitDdr: return 0x03B70600;
case CommandSwitch_WriteHsTimingLegacySpeed: return 0x03B90000;
case CommandSwitch_WriteHsTimingHighSpeed: return 0x03B90100;
case CommandSwitch_WriteHsTimingHs200: return 0x03B90200;
case CommandSwitch_WriteHsTimingHs400: return 0x03B90300;
case CommandSwitch_WritePartitionAccessDefault: return 0x03B30000;
case CommandSwitch_WritePartitionAccessRwBootPartition1: return 0x03B30100;
case CommandSwitch_WritePartitionAccessRwBootPartition2: return 0x03B30200;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
private:
Result IssueCommandSendOpCond(u32 *out_ocr, BusPower bus_power) const;
Result IssueCommandSetRelativeAddr() const;
Result IssueCommandSwitch(CommandSwitch cs) const;
Result IssueCommandSendExtCsd(void *dst, size_t dst_size) const;
Result IssueCommandEraseGroupStart(u32 sector_index) const;
Result IssueCommandEraseGroupEnd(u32 sector_index) const;
Result IssueCommandErase() const;
Result CancelToshibaMmcModel();
Result ChangeToReadyState(BusPower bus_power);
Result ExtendBusWidth(BusWidth max_bus_width);
Result EnableBkopsAuto();
Result ChangeToHighSpeed(bool check_before);
Result ChangeToHs200();
Result ChangeToHs400();
Result ExtendBusSpeed(u8 device_type, SpeedMode max_sm);
Result StartupMmcDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size);
protected:
virtual Result OnActivate() override;
virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) override;
virtual Result ReStartup() override;
public:
virtual void Initialize() override;
virtual void Finalize() override;
virtual Result GetSpeedMode(SpeedMode *out_speed_mode) const override;
public:
explicit MmcDeviceAccessor(IHostController *hc)
: BaseDeviceAccessor(hc), work_buffer(nullptr), work_buffer_size(0),
max_bus_width(BusWidth_8Bit), max_speed_mode(SpeedMode_MmcHs400), current_partition(MmcPartition_Unknown),
is_initialized(false)
{
/* ... */
}
void SetMmcWorkBuffer(void *wb, size_t wb_size) {
this->work_buffer = wb;
this->work_buffer_size = wb_size;
}
void PutMmcToSleep();
void AwakenMmc();
Result SelectMmcPartition(MmcPartition part);
Result EraseMmc();
Result GetMmcBootPartitionCapacity(u32 *out_num_sectors) const;
Result GetMmcExtendedCsd(void *dst, size_t dst_size) const;
};
}

View File

@@ -0,0 +1,50 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#include "sdmmc_port_gc_asic0.hpp"
#include "sdmmc_select_sdmmc_controller.hpp"
namespace ams::sdmmc::impl {
namespace {
SdmmcControllerForPortGcAsic0 g_gc_asic0_host_controller;
GcAsicDeviceAccessor g_gc_asic0_device_accessor(std::addressof(g_gc_asic0_host_controller));
}
IHostController *GetHostControllerOfPortGcAsic0() {
return std::addressof(g_gc_asic0_host_controller);
}
IDeviceAccessor *GetDeviceAccessorOfPortGcAsic0() {
return std::addressof(g_gc_asic0_device_accessor);
}
GcAsicDeviceAccessor *GetGcAsicDeviceAccessorOfPortGcAsic0() {
return std::addressof(g_gc_asic0_device_accessor);
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include "sdmmc_i_host_controller.hpp"
#include "sdmmc_i_device_accessor.hpp"
#include "sdmmc_gc_asic_device_accessor.hpp"
namespace ams::sdmmc::impl {
IHostController *GetHostControllerOfPortGcAsic0();
IDeviceAccessor *GetDeviceAccessorOfPortGcAsic0();
GcAsicDeviceAccessor *GetGcAsicDeviceAccessorOfPortGcAsic0();
}

View File

@@ -0,0 +1,51 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#include "sdmmc_port_mmc0.hpp"
#include "sdmmc_select_sdmmc_controller.hpp"
#include "sdmmc_base_device_accessor.hpp"
namespace ams::sdmmc::impl {
namespace {
SdmmcControllerForPortMmc0 g_mmc0_host_controller;
MmcDeviceAccessor g_mmc0_device_accessor(std::addressof(g_mmc0_host_controller));
}
IHostController *GetHostControllerOfPortMmc0() {
return std::addressof(g_mmc0_host_controller);
}
IDeviceAccessor *GetDeviceAccessorOfPortMmc0() {
return std::addressof(g_mmc0_device_accessor);
}
MmcDeviceAccessor *GetMmcDeviceAccessorOfPortMmc0() {
return std::addressof(g_mmc0_device_accessor);
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include "sdmmc_i_host_controller.hpp"
#include "sdmmc_i_device_accessor.hpp"
#include "sdmmc_mmc_device_accessor.hpp"
namespace ams::sdmmc::impl {
IHostController *GetHostControllerOfPortMmc0();
IDeviceAccessor *GetDeviceAccessorOfPortMmc0();
MmcDeviceAccessor *GetMmcDeviceAccessorOfPortMmc0();
}

View File

@@ -0,0 +1,63 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#include "sdmmc_port_sd_card0.hpp"
#include "sdmmc_select_sdmmc_controller.hpp"
namespace ams::sdmmc::impl {
namespace {
SdmmcControllerForPortSdCard0 g_sd_card0_host_controller;
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
constexpr inline u32 SdCard0DebounceMilliSeconds = 128;
DeviceDetector g_sd_card0_detector(gpio::DeviceCode_SdCd, gpio::GpioValue_Low, SdCard0DebounceMilliSeconds);
SdCardDeviceAccessor g_sd_card0_device_accessor(std::addressof(g_sd_card0_host_controller), std::addressof(g_sd_card0_detector));
#else
SdCardDeviceAccessor g_sd_card0_device_accessor(std::addressof(g_sd_card0_host_controller));
#endif
}
IHostController *GetHostControllerOfPortSdCard0() {
return std::addressof(g_sd_card0_host_controller);
}
IDeviceAccessor *GetDeviceAccessorOfPortSdCard0() {
return std::addressof(g_sd_card0_device_accessor);
}
SdCardDeviceAccessor *GetSdCardDeviceAccessorOfPortSdCard0() {
return std::addressof(g_sd_card0_device_accessor);
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include "sdmmc_i_host_controller.hpp"
#include "sdmmc_i_device_accessor.hpp"
#include "sdmmc_sd_card_device_accessor.hpp"
namespace ams::sdmmc::impl {
IHostController *GetHostControllerOfPortSdCard0();
IDeviceAccessor *GetDeviceAccessorOfPortSdCard0();
SdCardDeviceAccessor *GetSdCardDeviceAccessorOfPortSdCard0();
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,222 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include "sdmmc_base_device_accessor.hpp"
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
#include "sdmmc_device_detector.hpp"
#endif
namespace ams::sdmmc::impl {
class SdCardDevice : public BaseDevice {
private:
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
mutable os::EventType removed_event;
#endif
u16 rca;
bool is_valid_rca;
bool is_uhs_i_mode;
public:
SdCardDevice() : rca(0) {
this->OnDeactivate();
}
virtual void Deactivate() override {
this->OnDeactivate();
BaseDevice::Deactivate();
}
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
virtual os::EventType *GetRemovedEvent() const override {
return std::addressof(this->removed_event);
}
#elif defined(AMS_SDMMC_USE_OS_EVENTS)
virtual os::EventType *GetRemovedEvent() const override {
/* Mmc can't be removed. */
return nullptr;
}
#endif
virtual DeviceType GetDeviceType() const override {
return DeviceType_SdCard;
}
virtual u16 GetRca() const override {
AMS_ABORT_UNLESS(this->is_valid_rca);
return this->rca;
}
void OnDeactivate() {
this->is_valid_rca = false;
this->is_uhs_i_mode = false;
}
void SetRca(u16 v) {
this->rca = v;
this->is_valid_rca = true;
}
void SetOcrAndHighCapacity(u32 ocr);
void SetUhsIMode(bool en) {
this->is_uhs_i_mode = en;
}
bool IsUhsIMode() const {
return this->is_uhs_i_mode;
}
};
enum SdCardApplicationCommandIndex : std::underlying_type<CommandIndex>::type {
SdApplicationCommandIndex_SetBusWidth = 6,
SdApplicationCommandIndex_SdStatus = 13,
SdApplicationCommandIndex_SendNumWriteBlocks = 22,
SdApplicationCommandIndex_SetWriteBlockEraseCount = 23,
SdApplicationCommandIndex_SdSendOpCond = 41,
SdApplicationCommandIndex_SetClearCardDetect = 42,
SdApplicationCommandIndex_SendScr = 51,
};
enum SwitchFunctionAccessMode {
SwitchFunctionAccessMode_Default = 0,
SwitchFunctionAccessMode_HighSpeed = 1,
SwitchFunctionAccessMode_Sdr50 = 2,
SwitchFunctionAccessMode_Sdr104 = 3,
SwitchFunctionAccessMode_Ddr50 = 4,
};
class SdCardDeviceAccessor : public BaseDeviceAccessor {
private:
SdCardDevice sd_card_device;
void *work_buffer;
size_t work_buffer_size;
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
DeviceDetector *sd_card_detector;
#endif
BusWidth max_bus_width;
SpeedMode max_speed_mode;
bool is_initialized;
private:
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
void RemovedCallback();
static void RemovedCallbackEntry(void *arg) {
static_cast<SdCardDeviceAccessor *>(arg)->RemovedCallback();
}
#endif
Result IssueCommandSendRelativeAddr(u16 *out_rca) const;
Result IssueCommandSendIfCond() const;
Result IssueCommandCheckSupportedFunction(void *dst, size_t dst_size) const;
Result IssueCommandSwitchAccessMode(void *dst, size_t dst_size, bool set_function, SwitchFunctionAccessMode access_mode) const;
Result IssueCommandVoltageSwitch() const;
Result IssueCommandAppCmd(DeviceState expected_state, u32 ignore_mask = 0) const;
Result IssueCommandSetBusWidth4Bit() const;
Result IssueCommandSdStatus(void *dst, size_t dst_size) const;
Result IssueCommandSendOpCond(u32 *out_ocr, bool spec_under_2, bool uhs_i_supported) const;
Result IssueCommandClearCardDetect() const;
Result IssueCommandSendScr(void *dst, size_t dst_size) const;
Result EnterUhsIMode();
Result ChangeToReadyState(bool spec_under_2, bool uhs_i_supported);
Result ChangeToStbyStateAndGetRca();
Result SetMemoryCapacity(const void *csd);
Result GetScr(void *dst, size_t dst_size) const;
Result ExtendBusWidth(BusWidth max_bw, u8 sd_bw);
Result SwitchAccessMode(SwitchFunctionAccessMode access_mode, void *wb, size_t wb_size);
Result ExtendBusSpeedAtUhsIMode(SpeedMode max_sm, void *wb, size_t wb_size);
Result ExtendBusSpeedAtNonUhsIMode(SpeedMode max_sm, bool spec_under_1_1, void *wb, size_t wb_size);
Result GetSdStatus(void *dst, size_t dst_size) const;
Result StartupSdCardDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size);
void TryDisconnectDat3PullUpResistor() const;
protected:
virtual Result OnActivate() override;
virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) override;
virtual Result ReStartup() override;
public:
virtual void Initialize() override;
virtual void Finalize() override;
virtual Result Activate() override;
virtual Result GetSpeedMode(SpeedMode *out_speed_mode) const override;
public:
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
explicit SdCardDeviceAccessor(IHostController *hc, DeviceDetector *dd) : BaseDeviceAccessor(hc), sd_card_detector(dd)
#else
explicit SdCardDeviceAccessor(IHostController *hc) : BaseDeviceAccessor(hc)
#endif
{
this->work_buffer = nullptr;
this->work_buffer_size = 0;
this->max_bus_width = BusWidth_4Bit;
this->max_speed_mode = SpeedMode_SdCardSdr104;
this->is_initialized = false;
}
void SetSdCardWorkBuffer(void *wb, size_t wb_size) {
this->work_buffer = wb;
this->work_buffer_size = wb_size;
}
void PutSdCardToSleep();
void AwakenSdCard();
Result GetSdCardProtectedAreaCapacity(u32 *out_num_sectors) const;
Result GetSdCardScr(void *dst, size_t dst_size) const;
Result GetSdCardSwitchFunctionStatus(void *dst, size_t dst_size, SdCardSwitchFunction switch_function) const;
Result GetSdCardCurrentConsumption(u16 *out_current_consumption, SpeedMode speed_mode) const;
Result GetSdCardSdStatus(void *dst, size_t dst_size) const;
bool IsSdCardInserted() {
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
return this->sd_card_detector->IsInserted();
#else
AMS_ABORT("IsSdCardInserted without SdCardDetector");
#endif
}
bool IsSdCardRemoved() {
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
return this->sd_card_device.IsRemoved();
#else
AMS_ABORT("IsSdCardRemoved without SdCardDetector");
#endif
}
void RegisterSdCardDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg) {
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
return this->sd_card_detector->RegisterDetectionEventCallback(cb, arg);
#else
AMS_UNUSED(cb, arg);
AMS_ABORT("RegisterSdCardDetectionEventCallback without SdCardDetector");
#endif
}
void UnregisterSdCardDetectionEventCallback() {
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
return this->sd_card_detector->UnregisterDetectionEventCallback();
#else
AMS_ABORT("UnregisterSdCardDetectionEventCallback without SdCardDetector");
#endif
}
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,188 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include "sdmmc_i_host_controller.hpp"
#include "sdmmc_sd_host_standard_registers.hpp"
namespace ams::sdmmc::impl {
class SdHostStandardController : public IHostController {
protected:
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
struct BufferInfo {
uintptr_t buffer_address;
size_t buffer_size;
dd::DeviceVirtualAddress buffer_device_virtual_address;
};
static constexpr inline auto NumBufferInfos = 3;
#endif
protected:
SdHostStandardRegisters *registers;
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
BufferInfo buffer_infos[NumBufferInfos];
#endif
#if defined(AMS_SDMMC_USE_OS_EVENTS)
os::WaitableManagerType waitable_manager;
os::InterruptEventType *interrupt_event;
os::WaitableHolderType interrupt_event_holder;
os::EventType *removed_event;
os::WaitableHolderType removed_event_holder;
#endif
u64 next_sdma_address;
u32 check_transfer_interval_ms;
u32 device_clock_frequency_khz;
bool is_power_saving_enable;
bool is_device_clock_enable;
ResponseType last_response_type;
u32 last_response[4];
u32 last_stop_transmission_response;
protected:
#if defined(AMS_SDMMC_USE_OS_EVENTS)
void PreSetInterruptEvent(os::InterruptEventType *ie) {
this->interrupt_event = ie;
}
bool IsRemoved() const {
return this->removed_event != nullptr && os::TryWaitEvent(this->removed_event);
}
#endif
void SetDeviceClockFrequencyKHz(u32 khz) {
this->device_clock_frequency_khz = khz;
}
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
void ResetBufferInfos();
dd::DeviceVirtualAddress GetDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size);
#endif
void EnsureControl();
Result EnableInternalClock();
void SetBusPower(BusPower bus_power);
void EnableInterruptStatus();
void DisableInterruptStatus();
#if defined(AMS_SDMMC_USE_OS_EVENTS)
Result WaitInterrupt(u32 timeout_ms);
void ClearInterrupt();
#endif
void SetTransfer(u32 *out_num_transferred_blocks, const TransferData *xfer_data);
void SetTransferForTuning();
void SetCommand(const Command *command, bool has_xfer_data);
void SetCommandForTuning(u32 command_index);
Result ResetCmdDatLine();
Result AbortTransaction();
Result WaitWhileCommandInhibit(bool has_dat);
Result CheckAndClearInterruptStatus(volatile u16 *out_normal_int_status, u16 wait_mask);
Result WaitCommandComplete();
Result WaitTransferComplete();
Result WaitWhileBusy();
void GetResponse(u32 *out_response, size_t response_size, ResponseType response_type) const;
Result IssueCommandWithDeviceClock(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks);
Result IssueStopTransmissionCommandWithDeviceClock(u32 *out_response);
ALWAYS_INLINE Result CheckRemoved() {
#if defined(AMS_SDMMC_USE_OS_EVENTS)
R_UNLESS(!this->IsRemoved(), sdmmc::ResultDeviceRemoved());
#endif
return ResultSuccess();
}
public:
SdHostStandardController(dd::PhysicalAddress registers_phys_addr, size_t registers_size);
#if defined(AMS_SDMMC_USE_OS_EVENTS)
virtual void PreSetRemovedEvent(os::EventType *e) override {
this->removed_event = e;
}
#endif
virtual void Initialize() override;
virtual void Finalize() override;
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override;
virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override;
#endif
virtual void SetWorkBuffer(void *wb, size_t wb_size) override;
virtual Result SwitchToSdr12() override {
AMS_ABORT("SwitchToSdr12 not supported\n");
}
virtual BusPower GetBusPower() const override;
virtual void SetBusWidth(BusWidth bus_width) override;
virtual BusWidth GetBusWidth() const override;
virtual u32 GetDeviceClockFrequencyKHz() const override {
return this->device_clock_frequency_khz;
}
virtual void SetPowerSaving(bool en) override;
virtual bool IsPowerSavingEnable() const override {
return this->is_power_saving_enable;
}
virtual void EnableDeviceClock() override;
virtual void DisableDeviceClock() override;
virtual bool IsDeviceClockEnable() const override {
return this->is_device_clock_enable;
}
virtual u32 GetMaxTransferNumBlocks() const override {
return SdHostStandardRegisters::BlockCountMax;
}
virtual void ChangeCheckTransferInterval(u32 ms) override;
virtual void SetDefaultCheckTransferInterval() override;
virtual Result IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) override;
virtual Result IssueStopTransmissionCommand(u32 *out_response) override;
virtual void GetLastResponse(u32 *out_response, size_t response_size, ResponseType response_type) const override;
virtual void GetLastStopTransmissionResponse(u32 *out_response, size_t response_size) const override;
virtual bool IsSupportedTuning() const override {
return false;
}
virtual Result Tuning(SpeedMode speed_mode, u32 command_index) {
AMS_UNUSED(speed_mode, command_index);
AMS_ABORT("Tuning not supported\n");
}
virtual void SaveTuningStatusForHs400() {
AMS_ABORT("SaveTuningStatusForHs400 not supported\n");
}
};
}

View File

@@ -0,0 +1,193 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::sdmmc::impl {
struct SdHostStandardRegisters {
volatile uint32_t dma_address;
volatile uint16_t block_size;
volatile uint16_t block_count;
volatile uint32_t argument;
volatile uint16_t transfer_mode;
volatile uint16_t command;
volatile uint32_t response[0x4];
volatile uint32_t buffer;
volatile uint32_t present_state;
volatile uint8_t host_control;
volatile uint8_t power_control;
volatile uint8_t block_gap_control;
volatile uint8_t wake_up_control;
volatile uint16_t clock_control;
volatile uint8_t timeout_control;
volatile uint8_t software_reset;
volatile uint16_t normal_int_status;
volatile uint16_t error_int_status;
volatile uint16_t normal_int_enable;
volatile uint16_t error_int_enable;
volatile uint16_t normal_signal_enable;
volatile uint16_t error_signal_enable;
volatile uint16_t acmd12_err;
volatile uint16_t host_control2;
volatile uint32_t capabilities;
volatile uint32_t capabilities_1;
volatile uint32_t max_current;
volatile uint32_t _0x4c;
volatile uint16_t set_acmd12_error;
volatile uint16_t set_int_error;
volatile uint8_t adma_error;
volatile uint8_t _0x56[0x3];
volatile uint32_t adma_address;
volatile uint32_t upper_adma_address;
volatile uint16_t preset_for_init;
volatile uint16_t preset_for_default;
volatile uint16_t preset_for_high;
volatile uint16_t preset_for_sdr12;
volatile uint16_t preset_for_sdr25;
volatile uint16_t preset_for_sdr50;
volatile uint16_t preset_for_sdr104;
volatile uint16_t preset_for_ddr50;
volatile uint32_t _0x70[0x23];
volatile uint16_t slot_int_status;
volatile uint16_t host_version;
static constexpr inline u16 BlockCountMax = 0xFFFF;
};
static_assert(sizeof(SdHostStandardRegisters) == 0x100);
constexpr inline size_t SdmaBufferBoundary = 512_KB;
#define SD_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (SD_HOST_STANDARD, NAME)
#define SD_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (SD_HOST_STANDARD, NAME, VALUE)
#define SD_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (SD_HOST_STANDARD, NAME, ENUM)
#define SD_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(SD_HOST_STANDARD, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
#define DEFINE_SD_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (SD_HOST_STANDARD, NAME, __OFFSET__, __WIDTH__)
#define DEFINE_SD_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE)
#define DEFINE_SD_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
#define DEFINE_SD_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_SD_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
DEFINE_SD_REG(BLOCK_SIZE_TRANSFER_BLOCK_SIZE, 0, 12);
DEFINE_SD_REG_THREE_BIT_ENUM(BLOCK_SIZE_SDMA_BUFFER_BOUNDARY, 12, 4_KB, 8_KB, 16_KB, 32_KB, 64_KB, 128_KB, 256_KB, 512_KB);
constexpr inline size_t SdHostStandardBlockSizeTransferBlockSizeMax = 0xFFF;
DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_DMA_ENABLE, 0, DISABLE, ENABLE);
DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_BLOCK_COUNT_ENABLE, 1, DISABLE, ENABLE);
DEFINE_SD_REG_TWO_BIT_ENUM(TRANSFER_MODE_AUTO_CMD_ENABLE, 2, DISABLE, CMD12_ENABLE, CMD23_ENABLE, AUTO_SELECT);
DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_DATA_TRANSFER_DIRECTION, 4, WRITE, READ);
DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_MULTI_BLOCK_SELECT, 5, SINGLE_BLOCK, MULTI_BLOCK);
DEFINE_SD_REG_TWO_BIT_ENUM(COMMAND_RESPONSE_TYPE, 0, NO_RESPONSE, RESPONSE_LENGTH_136, RESPONSE_LENGTH_48, RESPONSE_LENGTH_48_CHECK_BUSY_AFTER_RESPONSE);
DEFINE_SD_REG_BIT_ENUM(COMMAND_CRC_CHECK, 3, DISABLE, ENABLE);
DEFINE_SD_REG_BIT_ENUM(COMMAND_INDEX_CHECK, 4, DISABLE, ENABLE);
DEFINE_SD_REG_BIT_ENUM(COMMAND_DATA_PRESENT, 5, NO_DATA_PRESENT, DATA_PRESENT);
DEFINE_SD_REG(COMMAND_COMMAND_INDEX, 8, 6);
constexpr inline size_t SdHostStandardCommandIndexMax = 0x3F;
DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_COMMAND_INHIBIT_CMD, 0, READY, NOT_READY);
DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_COMMAND_INHIBIT_DAT, 1, READY, NOT_READY);
DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT0_LINE_SIGNAL_LEVEL, 20, LOW, HIGH);
DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT1_LINE_SIGNAL_LEVEL, 21, LOW, HIGH);
DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT2_LINE_SIGNAL_LEVEL, 22, LOW, HIGH);
DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT3_LINE_SIGNAL_LEVEL, 23, LOW, HIGH);
DEFINE_SD_REG(PRESENT_STATE_DAT0_3_LINE_SIGNAL_LEVEL, 20, 4);
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL_DATA_TRANSFER_WIDTH, 1, ONE_BIT, FOUR_BIT);
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL_HIGH_SPEED_ENABLE, 2, NORMAL_SPEED, HIGH_SPEED);
DEFINE_SD_REG_TWO_BIT_ENUM(HOST_CONTROL_DMA_SELECT, 3, SDMA, RESERVED1, ADMA2, ADMA2_OR_ADMA3);
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL_EXTENDED_DATA_TRANSFER_WIDTH, 5, USE_DATA_TRANSFER_WIDTH, EIGHT_BIT);
DEFINE_SD_REG_BIT_ENUM(POWER_CONTROL_SD_BUS_POWER_FOR_VDD1, 0, OFF, ON);
DEFINE_SD_REG_THREE_BIT_ENUM(POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1, 1, RESERVED0, RESERVED1, RESERVED2, RESERVED3, RESERVED4, 1_8V, 3_0V, 3_3V);
DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_INTERNAL_CLOCK_ENABLE, 0, STOP, OSCILLATE);
DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_INTERNAL_CLOCK_STABLE, 1, NOT_READY, READY);
DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, 2, DISABLE, ENABLE);
DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_CLOCK_GENERATOR_SELECT, 5, DIVIDED_CLOCK, PROGRAMMABLE_CLOCK);
DEFINE_SD_REG(CLOCK_CONTROL_UPPER_BITS_OF_SDCLK_FREQUENCY_SELECT, 6, 2);
DEFINE_SD_REG(CLOCK_CONTROL_SDCLK_FREQUENCY_SELECT, 8, 8);
DEFINE_SD_REG(TIMEOUT_CONTROL_DATA_TIMEOUT_COUNTER, 0, 4);
DEFINE_SD_REG_BIT_ENUM(SOFTWARE_RESET_FOR_ALL, 0, WORK, RESET);
DEFINE_SD_REG_BIT_ENUM(SOFTWARE_RESET_FOR_CMD, 1, WORK, RESET);
DEFINE_SD_REG_BIT_ENUM(SOFTWARE_RESET_FOR_DAT, 2, WORK, RESET);
DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_COMMAND_COMPLETE, 0, NOT_COMPLETE, COMPLETE);
DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_TRANSFER_COMPLETE, 1, NOT_COMPLETE, COMPLETE);
DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_DMA_INTERRUPT, 3, NOT_GENERATED, GENERATED);
DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_ERROR_INTERRUPT, 15, NO_ERROR, ERROR);
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_TIMEOUT, 0, NO_ERROR, ERROR);
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_CRC, 1, NO_ERROR, ERROR);
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_END_BIT, 2, NO_ERROR, ERROR);
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_INDEX, 3, NO_ERROR, ERROR);
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_DATA_TIMEOUT, 4, NO_ERROR, ERROR);
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_DATA_CRC, 5, NO_ERROR, ERROR);
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_DATA_END_BIT, 6, NO_ERROR, ERROR);
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_AUTO_CMD, 8, NO_ERROR, ERROR);
DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_TIMEOUT, 1, NO_ERROR, ERROR);
DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_CRC, 2, NO_ERROR, ERROR);
DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_END_BIT, 3, NO_ERROR, ERROR);
DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_INDEX, 4, NO_ERROR, ERROR);
DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_COMMAND_COMPLETE, 0, MASKED, ENABLED);
DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_TRANSFER_COMPLETE, 1, MASKED, ENABLED);
DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_DMA_INTERRUPT, 3, MASKED, ENABLED);
DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, 5, MASKED, ENABLED);
#define SD_HOST_STANDARD_NORMAL_INTERRUPT_ENABLE_ISSUE_COMMAND(__ENUM__) \
SD_REG_BITS_ENUM(NORMAL_INTERRUPT_COMMAND_COMPLETE, __ENUM__), \
SD_REG_BITS_ENUM(NORMAL_INTERRUPT_TRANSFER_COMPLETE, __ENUM__), \
SD_REG_BITS_ENUM(NORMAL_INTERRUPT_DMA_INTERRUPT, __ENUM__)
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_TIMEOUT_ERROR, 0, MASKED, ENABLED);
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_CRC_ERROR, 1, MASKED, ENABLED);
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_END_BIT_ERROR, 2, MASKED, ENABLED);
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_INDEX_ERROR, 3, MASKED, ENABLED);
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_DATA_TIMEOUT_ERROR, 4, MASKED, ENABLED);
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_DATA_CRC_ERROR, 5, MASKED, ENABLED);
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_DATA_END_BIT_ERROR, 6, MASKED, ENABLED);
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_AUTO_CMD_ERROR, 8, MASKED, ENABLED);
#define SD_HOST_STANDARD_ERROR_INTERRUPT_ENABLE_ISSUE_COMMAND(__ENUM__) \
SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_TIMEOUT_ERROR, __ENUM__), \
SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_CRC_ERROR, __ENUM__), \
SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_END_BIT_ERROR, __ENUM__), \
SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_INDEX_ERROR, __ENUM__), \
SD_REG_BITS_ENUM(ERROR_INTERRUPT_DATA_TIMEOUT_ERROR, __ENUM__), \
SD_REG_BITS_ENUM(ERROR_INTERRUPT_DATA_CRC_ERROR, __ENUM__), \
SD_REG_BITS_ENUM(ERROR_INTERRUPT_DATA_END_BIT_ERROR, __ENUM__), \
SD_REG_BITS_ENUM(ERROR_INTERRUPT_AUTO_CMD_ERROR, __ENUM__)
DEFINE_SD_REG_THREE_BIT_ENUM(HOST_CONTROL2_UHS_MODE_SELECT, 0, SDR12, SDR25, SDR50, SDR104, DDR50, HS400, RSVD6, UHS_II);
constexpr inline auto SD_HOST_STANDARD_HOST_CONTROL2_UHS_MODE_SELECT_HS200 = SD_HOST_STANDARD_HOST_CONTROL2_UHS_MODE_SELECT_SDR104;
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 3, 3_3V_SIGNALING, 1_8V_SIGNALING);
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_EXECUTE_TUNING, 6, TUNING_COMPLETED, EXECUTE_TUNING);
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_SAMPLING_CLOCK, 7, USING_FIXED_CLOCK, USING_TUNED_CLOCK);
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_HOST_VERSION_4_ENABLE, 12, VERSION_300_COMPATIBLE, VERSION_4);
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_64_BIT_ADDRESSING, 13, 32_BIT_ADDRESSING, 64_BIT_ADDRESSING);
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_PRESET_VALUE_ENABLE, 15, HOST_DRIVER, AUTOMATIC_SELECTION);
DEFINE_SD_REG_BIT_ENUM(CAPABILITIES_64_BIT_SYSTEM_ADDRESS_SUPPORT_FOR_V3, 28, NOT_SUPPORTED, SUPPORTED);
}

View File

@@ -0,0 +1,682 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include "sdmmc_sd_host_standard_controller.hpp"
#include "sdmmc_clock_reset_controller.hpp"
namespace ams::sdmmc::impl {
bool IsSocMariko();
constexpr inline size_t SdmmcRegistersSize = 0x200;
constexpr inline dd::PhysicalAddress ApbMiscRegistersPhysicalAddress = UINT64_C(0x70000000);
constexpr inline size_t ApbMiscRegistersSize = 16_KB;
class SdmmcController : public SdHostStandardController {
private:
struct SdmmcRegisters {
/* Standard registers. */
volatile SdHostStandardRegisters sd_host_standard_registers;
/* Vendor specific registers */
volatile uint32_t vendor_clock_cntrl;
volatile uint32_t vendor_sys_sw_cntrl;
volatile uint32_t vendor_err_intr_status;
volatile uint32_t vendor_cap_overrides;
volatile uint32_t vendor_boot_cntrl;
volatile uint32_t vendor_boot_ack_timeout;
volatile uint32_t vendor_boot_dat_timeout;
volatile uint32_t vendor_debounce_count;
volatile uint32_t vendor_misc_cntrl;
volatile uint32_t max_current_override;
volatile uint32_t max_current_override_hi;
volatile uint32_t _0x12c[0x20];
volatile uint32_t vendor_io_trim_cntrl;
/* Start of sdmmc2/sdmmc4 only */
volatile uint32_t vendor_dllcal_cfg;
volatile uint32_t vendor_dll_ctrl0;
volatile uint32_t vendor_dll_ctrl1;
volatile uint32_t vendor_dllcal_cfg_sta;
/* End of sdmmc2/sdmmc4 only */
volatile uint32_t vendor_tuning_cntrl0;
volatile uint32_t vendor_tuning_cntrl1;
volatile uint32_t vendor_tuning_status0;
volatile uint32_t vendor_tuning_status1;
volatile uint32_t vendor_clk_gate_hysteresis_count;
volatile uint32_t vendor_preset_val0;
volatile uint32_t vendor_preset_val1;
volatile uint32_t vendor_preset_val2;
volatile uint32_t sdmemcomppadctrl;
volatile uint32_t auto_cal_config;
volatile uint32_t auto_cal_interval;
volatile uint32_t auto_cal_status;
volatile uint32_t io_spare;
volatile uint32_t sdmmca_mccif_fifoctrl;
volatile uint32_t timeout_wcoal_sdmmca;
volatile uint32_t _0x1fc;
};
static_assert(sizeof(SdmmcRegisters) == SdmmcRegistersSize);
private:
SdmmcRegisters *sdmmc_registers;
bool is_shutdown;
bool is_awake;
SpeedMode current_speed_mode;
BusPower bus_power_before_sleep;
BusWidth bus_width_before_sleep;
SpeedMode speed_mode_before_sleep;
u8 tap_value_before_sleep;
bool is_powersaving_enable_before_sleep;
u8 tap_value_for_hs_400;
bool is_valid_tap_value_for_hs_400;
Result drive_strength_calibration_status;
private:
void ReleaseReset(SpeedMode speed_mode);
void AssertReset();
Result StartupCore(BusPower bus_power);
Result SetClockTrimmer(SpeedMode speed_mode, u8 tap_value);
u8 GetCurrentTapValue();
Result CalibrateDll();
Result SetSpeedModeWithTapValue(SpeedMode speed_mode, u8 tap_value);
Result IssueTuningCommand(u32 command_index);
protected:
void SetDriveCodeOffsets(BusPower bus_power);
void CalibrateDriveStrength(BusPower bus_power);
virtual void SetPad() = 0;
virtual ClockResetController::Module GetClockResetModule() const = 0;
#if defined(AMS_SDMMC_USE_OS_EVENTS)
virtual int GetInterruptNumber() const = 0;
virtual os::InterruptEventType *GetInterruptEvent() const = 0;
#endif
virtual bool IsNeedPeriodicDriveStrengthCalibration() = 0;
virtual void ClearPadParked() = 0;
virtual Result PowerOn(BusPower bus_power) = 0;
virtual void PowerOff() = 0;
virtual Result LowerBusPower() = 0;
virtual void SetSchmittTrigger(BusPower bus_power) = 0;
virtual u8 GetOutboundTapValue() const = 0;
virtual u8 GetDefaultInboundTapValue() const = 0;
virtual u8 GetVrefSelValue() const = 0;
virtual void SetSlewCodes() = 0;
virtual void GetAutoCalOffsets(u8 *out_auto_cal_pd_offset, u8 *out_auto_cal_pu_offset, BusPower bus_power) const = 0;
virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) = 0;
public:
explicit SdmmcController(dd::PhysicalAddress registers_phys_addr) : SdHostStandardController(registers_phys_addr, SdmmcRegistersSize) {
/* Set sdmmc registers. */
static_assert(offsetof(SdmmcRegisters, sd_host_standard_registers) == 0);
this->sdmmc_registers = reinterpret_cast<SdmmcRegisters *>(this->registers);
this->is_shutdown = true;
this->is_awake = true;
this->is_valid_tap_value_for_hs_400 = false;
this->drive_strength_calibration_status = sdmmc::ResultDriveStrengthCalibrationNotCompleted();
this->tap_value_for_hs_400 = 0;
this->current_speed_mode = SpeedMode_MmcIdentification;
this->bus_power_before_sleep = BusPower_Off;
this->bus_width_before_sleep = BusWidth_1Bit;
this->speed_mode_before_sleep = SpeedMode_MmcIdentification;
this->tap_value_before_sleep = 0;
this->is_powersaving_enable_before_sleep = false;
}
virtual void Initialize() override {
/* Set pad. */
this->SetPad();
/* Initialize our clock/reset module. */
ClockResetController::Initialize(this->GetClockResetModule());
/* If necessary, initialize our interrupt event. */
#if defined(AMS_SDMMC_USE_OS_EVENTS)
{
os::InterruptEventType *interrupt_event = this->GetInterruptEvent();
os::InitializeInterruptEvent(interrupt_event, this->GetInterruptNumber(), os::EventClearMode_ManualClear);
SdHostStandardController::PreSetInterruptEvent(interrupt_event);
}
#endif
/* Perform base initialization. */
SdHostStandardController::Initialize();
}
virtual void Finalize() override {
/* Perform base finalization. */
SdHostStandardController::Finalize();
/* If necessary, finalize our interrupt event. */
#if defined(AMS_SDMMC_USE_OS_EVENTS)
{
os::FinalizeInterruptEvent(this->GetInterruptEvent());
}
#endif
/* Finalize our clock/reset module. */
ClockResetController::Finalize(this->GetClockResetModule());
}
virtual Result Startup(BusPower bus_power, BusWidth bus_width, SpeedMode speed_mode, bool power_saving_enable) override;
virtual void Shutdown() override;
virtual void PutToSleep() override;
virtual Result Awaken() override;
virtual Result SwitchToSdr12() override;
virtual Result SetSpeedMode(SpeedMode speed_mode) override;
virtual SpeedMode GetSpeedMode() const override {
return this->current_speed_mode;
}
virtual void SetPowerSaving(bool en) override;
virtual void EnableDeviceClock() override;
virtual Result IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) override;
virtual Result IssueStopTransmissionCommand(u32 *out_response) override;
virtual bool IsSupportedTuning() const override {
return true;
}
virtual Result Tuning(SpeedMode speed_mode, u32 command_index) override;
virtual void SaveTuningStatusForHs400() override;
virtual Result GetInternalStatus() const override {
return this->drive_strength_calibration_status;
}
};
constexpr inline dd::PhysicalAddress Sdmmc1RegistersPhysicalAddress = UINT64_C(0x700B0000);
class Sdmmc1Controller : public SdmmcController {
private:
#if defined(AMS_SDMMC_USE_OS_EVENTS)
static constinit inline os::InterruptEventType s_interrupt_event{};
#endif
/* NOTE: This is a fascimile of pcv's Sdmmc1PowerController. */
class PowerController {
NON_COPYABLE(PowerController);
NON_MOVEABLE(PowerController);
private:
BusPower current_bus_power;
private:
Result ControlVddioSdmmc1(BusPower bus_power);
void SetSdmmcIoMode(bool is_3_3V);
void ControlRailSdmmc1Io(bool is_power_on);
public:
PowerController();
~PowerController();
Result PowerOn(BusPower bus_power);
Result PowerOff();
Result LowerBusPower();
};
private:
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
/* TODO: pinmux::PinmuxSession pinmux_session; */
#endif
BusPower current_bus_power;
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
bool is_pcv_control;
#endif
TYPED_STORAGE(PowerController) power_controller_storage;
PowerController *power_controller;
private:
Result PowerOnForRegisterControl(BusPower bus_power);
void PowerOffForRegisterControl();
Result LowerBusPowerForRegisterControl();
void SetSchmittTriggerForRegisterControl(BusPower bus_power);
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
Result PowerOnForPcvControl(BusPower bus_power);
void PowerOffForPcvControl();
Result LowerBusPowerForPcvControl();
void SetSchmittTriggerForPcvControl(BusPower bus_power);
#endif
protected:
virtual void SetPad() override {
/* Nothing is needed here. */
}
virtual ClockResetController::Module GetClockResetModule() const override {
return ClockResetController::Module_Sdmmc1;
}
#if defined(AMS_SDMMC_USE_OS_EVENTS)
virtual int GetInterruptNumber() const override {
return 46;
}
virtual os::InterruptEventType *GetInterruptEvent() const override {
return std::addressof(s_interrupt_event);
}
#endif
virtual bool IsNeedPeriodicDriveStrengthCalibration() override {
return !IsSocMariko();
}
virtual void ClearPadParked() override {
/* Nothing is needed here. */
}
virtual Result PowerOn(BusPower bus_power) override;
virtual void PowerOff() override;
virtual Result LowerBusPower() override;
virtual void SetSchmittTrigger(BusPower bus_power) override;
virtual u8 GetOutboundTapValue() const override {
if (IsSocMariko()) {
return 0xE;
} else {
return 0x2;
}
}
virtual u8 GetDefaultInboundTapValue() const override {
if (IsSocMariko()) {
return 0xB;
} else {
return 0x4;
}
}
virtual u8 GetVrefSelValue() const override {
if (IsSocMariko()) {
return 0x0;
} else {
return 0x7;
}
}
virtual void SetSlewCodes() override {
if (IsSocMariko()) {
/* Do nothing. */
} else {
/* Ensure that we can control registers. */
SdHostStandardController::EnsureControl();
/* Get the apb registers address. */
const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize);
/* Write the slew values to APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL. */
reg::ReadWrite(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_CLK_CFG_CAL_DRVDN_SLWR, 0x1),
APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_CLK_CFG_CAL_DRVDN_SLWF, 0x1));
/* Read to be sure our config takes. */
reg::Read(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL);
}
}
virtual void GetAutoCalOffsets(u8 *out_auto_cal_pd_offset, u8 *out_auto_cal_pu_offset, BusPower bus_power) const override {
/* Ensure that we can write the offsets. */
AMS_ABORT_UNLESS(out_auto_cal_pd_offset != nullptr);
AMS_ABORT_UNLESS(out_auto_cal_pu_offset != nullptr);
/* Set the offsets. */
if (IsSocMariko()) {
switch (bus_power) {
case BusPower_1_8V:
*out_auto_cal_pd_offset = 6;
*out_auto_cal_pu_offset = 6;
break;
case BusPower_3_3V:
*out_auto_cal_pd_offset = 0;
*out_auto_cal_pu_offset = 0;
break;
case BusPower_Off:
AMS_UNREACHABLE_DEFAULT_CASE();
}
} else {
switch (bus_power) {
case BusPower_1_8V:
*out_auto_cal_pd_offset = 0x7B;
*out_auto_cal_pu_offset = 0x7B;
break;
case BusPower_3_3V:
*out_auto_cal_pd_offset = 0x7D;
*out_auto_cal_pu_offset = 0;
break;
case BusPower_Off:
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
}
virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) override {
/* Ensure that we can control registers. */
SdHostStandardController::EnsureControl();
/* Get the apb registers address. */
const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize);
/* Determine the drive code values. */
u8 drvdn, drvup;
if (IsSocMariko()) {
drvdn = 0x8;
drvup = 0x8;
} else {
switch (bus_power) {
case BusPower_1_8V:
drvdn = 0xF;
drvup = 0xB;
break;
case BusPower_3_3V:
drvdn = 0xC;
drvup = 0xC;
break;
case BusPower_Off:
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
/* Write the drv up/down values to APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL. */
reg::ReadWrite(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_PAD_CAL_DRVDN, drvdn),
APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_PAD_CAL_DRVUP, drvup));
/* Read to be sure our config takes. */
reg::Read(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL);
}
public:
Sdmmc1Controller() : SdmmcController(Sdmmc1RegistersPhysicalAddress) {
this->current_bus_power = BusPower_Off;
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
this->is_pcv_control = false;
#endif
this->power_controller = nullptr;
}
virtual void Initialize() override;
virtual void Finalize() override;
void InitializeForRegisterControl();
void FinalizeForRegisterControl();
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
void InitializeForPcvControl();
void FinalizeForPcvControl();
#endif
virtual bool IsSupportedBusPower(BusPower bus_power) const override {
switch (bus_power) {
case BusPower_Off: return true;
case BusPower_1_8V: return true;
case BusPower_3_3V: return true;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
virtual bool IsSupportedBusWidth(BusWidth bus_width) const override {
switch (bus_width) {
case BusWidth_1Bit: return true;
case BusWidth_4Bit: return true;
case BusWidth_8Bit: return false;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
};
class Sdmmc2And4Controller : public SdmmcController {
protected:
virtual bool IsNeedPeriodicDriveStrengthCalibration() override {
return false;
}
virtual Result PowerOn(BusPower bus_power) override {
/* Power for SDMMC2/4 is assumed on, so we don't need to do anything. */
AMS_UNUSED(bus_power);
return ResultSuccess();
}
virtual void PowerOff() override {
/* Power for SDMMC2/4 is assumed on, so we don't need to do anything. */
}
virtual Result LowerBusPower() override {
AMS_ABORT("Sdmmc2And4Controller cannot lower bus power\n");
}
virtual void SetSchmittTrigger(BusPower bus_power) override {
/* Do nothing. */
AMS_UNUSED(bus_power);
}
virtual u8 GetOutboundTapValue() const override {
if (IsSocMariko()) {
return 0xD;
} else {
return 0x8;
}
}
virtual u8 GetDefaultInboundTapValue() const override {
if (IsSocMariko()) {
return 0xB;
} else {
return 0x0;
}
}
virtual u8 GetVrefSelValue() const override {
return 0x7;
}
virtual void SetSlewCodes() override {
/* Do nothing. */
}
virtual void GetAutoCalOffsets(u8 *out_auto_cal_pd_offset, u8 *out_auto_cal_pu_offset, BusPower bus_power) const override {
/* Ensure that we can write the offsets. */
AMS_ABORT_UNLESS(out_auto_cal_pd_offset != nullptr);
AMS_ABORT_UNLESS(out_auto_cal_pu_offset != nullptr);
/* Sdmmc2And4Controller only supports 1.8v. */
AMS_ABORT_UNLESS(bus_power == BusPower_1_8V);
/* Set the offsets. */
*out_auto_cal_pd_offset = 5;
*out_auto_cal_pu_offset = 5;
}
public:
explicit Sdmmc2And4Controller(dd::PhysicalAddress registers_phys_addr) : SdmmcController(registers_phys_addr) {
/* ... */
}
virtual bool IsSupportedBusPower(BusPower bus_power) const override {
switch (bus_power) {
case BusPower_Off: return true;
case BusPower_1_8V: return true;
case BusPower_3_3V: return false;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
virtual bool IsSupportedBusWidth(BusWidth bus_width) const override {
switch (bus_width) {
case BusWidth_1Bit: return true;
case BusWidth_4Bit: return true;
case BusWidth_8Bit: return true;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
};
constexpr inline dd::PhysicalAddress Sdmmc2RegistersPhysicalAddress = UINT64_C(0x700B0200);
class Sdmmc2Controller : public Sdmmc2And4Controller {
private:
#if defined(AMS_SDMMC_USE_OS_EVENTS)
static constinit inline os::InterruptEventType s_interrupt_event{};
#endif
protected:
virtual void SetPad() override {
/* Nothing is needed here. */
}
virtual ClockResetController::Module GetClockResetModule() const override {
return ClockResetController::Module_Sdmmc2;
}
#if defined(AMS_SDMMC_USE_OS_EVENTS)
virtual int GetInterruptNumber() const override {
return 47;
}
virtual os::InterruptEventType *GetInterruptEvent() const override {
return std::addressof(s_interrupt_event);
}
#endif
virtual void ClearPadParked() override {
if (IsSocMariko()) {
/* Nothing is needed here. */
} else {
/* Get the apb registers address. */
const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize);
/* Clear all MISC2PMC_EMMC2_*_PARK bits. */
reg::ReadWrite(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC2_PAD_CFGPADCTRL_MISC2PMC_EMMC2_ALL_PARK, 0));
/* Read to be sure our config takes. */
reg::Read(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL);
}
}
virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) override {
/* SDMMC2 only supports 1.8v. */
AMS_ABORT_UNLESS(bus_power == BusPower_1_8V);
/* Ensure that we can control registers. */
SdHostStandardController::EnsureControl();
/* Get the apb registers address. */
const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize);
if (IsSocMariko()) {
/* Write the drv up/down values to APB_MISC_GP_SDMMC2_PAD_CFGPADCTRL. */
reg::ReadWrite(apb_address + APB_MISC_GP_SDMMC2_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_SDMMC2_PAD_CFGPADCTRL_CFG2TMC_SDMMC2_PAD_CAL_DRVDN, 0xA),
APB_MISC_REG_BITS_VALUE(GP_SDMMC2_PAD_CFGPADCTRL_CFG2TMC_SDMMC2_PAD_CAL_DRVUP, 0xA));
/* Read to be sure our config takes. */
reg::Read(apb_address + APB_MISC_GP_SDMMC2_PAD_CFGPADCTRL);
} else {
/* Write the drv up/down values to APB_MISC_GP_EMMC4_PAD_CFGPADCTRL. */
reg::ReadWrite(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC2_PAD_CFGPADCTRL_CFG2TMC_EMMC2_PAD_DRVDN_COMP, 0x10),
APB_MISC_REG_BITS_VALUE(GP_EMMC2_PAD_CFGPADCTRL_CFG2TMC_EMMC2_PAD_DRVUP_COMP, 0x10));
/* Read to be sure our config takes. */
reg::Read(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL);
}
}
public:
Sdmmc2Controller() : Sdmmc2And4Controller(Sdmmc2RegistersPhysicalAddress) {
/* ... */
}
};
constexpr inline dd::PhysicalAddress Sdmmc4RegistersPhysicalAddress = UINT64_C(0x700B0600);
class Sdmmc4Controller : public Sdmmc2And4Controller {
private:
#if defined(AMS_SDMMC_USE_OS_EVENTS)
static constinit inline os::InterruptEventType s_interrupt_event{};
#endif
protected:
virtual void SetPad() override {
if (IsSocMariko()) {
/* Get the apb registers address. */
const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize);
/* Enable Schmitt Trigger in emmc4 iobrick. */
reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL, APB_MISC_REG_BITS_ENUM(GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_E_SCH, ENABLE));
/* Clear CMD_PULLU, CLK_PULLD, DQS_PULLD. */
reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_PUPD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_CMD_PUPD_PULLU, 0),
APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_CLK_PUPD_PULLD, 0),
APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DQS_PUPD_PULLD, 0));
/* Read again to be sure our config takes. */
reg::Read(apb_address + APB_MISC_GP_EMMC4_PAD_PUPD_CFGPADCTRL);
} else {
/* On Erista, we can just leave the reset value intact. */
}
}
virtual ClockResetController::Module GetClockResetModule() const override {
return ClockResetController::Module_Sdmmc4;
}
#if defined(AMS_SDMMC_USE_OS_EVENTS)
virtual int GetInterruptNumber() const override {
return 63;
}
virtual os::InterruptEventType *GetInterruptEvent() const override {
return std::addressof(s_interrupt_event);
}
#endif
virtual void ClearPadParked() override {
/* Get the apb registers address. */
const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize);
/* Clear all MISC2PMC_EMMC4_*_PARK bits. */
reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_CFGPADCTRL_MISC2PMC_EMMC4_ALL_PARK, 0));
/* Read to be sure our config takes. */
reg::Read(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL);
}
virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) override {
/* SDMMC4 only supports 1.8v. */
AMS_ABORT_UNLESS(bus_power == BusPower_1_8V);
/* Ensure that we can control registers. */
SdHostStandardController::EnsureControl();
/* Get the apb registers address. */
const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize);
/* Determine the drv up/down values. */
u8 drvdn, drvup;
if (IsSocMariko()) {
drvdn = 0xA;
drvup = 0xA;
} else {
drvdn = 0x10;
drvup = 0x10;
}
/* Write the drv up/down values to APB_MISC_GP_EMMC4_PAD_CFGPADCTRL. */
reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DRVDN_COMP, drvdn),
APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DRVUP_COMP, drvup));
/* Read to be sure our config takes. */
reg::Read(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL);
}
public:
Sdmmc4Controller() : Sdmmc2And4Controller(Sdmmc4RegistersPhysicalAddress) {
/* ... */
}
};
}

View File

@@ -0,0 +1,33 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
#include "sdmmc_sdmmc_controller.board.nintendo_nx.hpp"
namespace ams::sdmmc::impl {
using SdmmcControllerForPortSdCard0 = Sdmmc1Controller;
using SdmmcControllerForPortGcAsic0 = Sdmmc2Controller;
using SdmmcControllerForPortMmc0 = Sdmmc4Controller;
}
#else
#error "Unknown board for ams::sdmmc::SdmmcController"
#endif

View File

@@ -0,0 +1,90 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#include "sdmmc_timer.hpp"
namespace ams::sdmmc::impl {
namespace {
#if defined(AMS_SDMMC_USE_OS_TIMER)
void SpinWaitMicroSeconds(u32 us) {
const os::Tick timeout_tick = os::GetSystemTick() + os::ConvertToTick(TimeSpan::FromMicroSeconds(us)) + os::Tick(1);
while (true) {
if (os::GetSystemTick() > timeout_tick) {
break;
}
}
}
ALWAYS_INLINE void DataSynchronizationBarrier() {
#if defined(ATMOSPHERE_ARCH_ARM64)
__asm__ __volatile__("dsb sy" ::: "memory");
#elif defined(ATMOSPHERE_ARCH_ARM)
__asm__ __volatile__("dsb" ::: "memory");
#else
#error "Unknown architecture for DataSynchronizationBarrier"
#endif
}
ALWAYS_INLINE void InstructionSynchronizationBarrier() {
#if defined(ATMOSPHERE_ARCH_ARM64) || defined(ATMOSPHERE_ARCH_ARM)
__asm__ __volatile__("isb" ::: "memory");
#else
#error "Unknown architecture for InstructionSynchronizationBarrier"
#endif
}
#endif
}
void WaitMicroSeconds(u32 us) {
#if defined(AMS_SDMMC_USE_OS_TIMER)
/* Ensure that nothing is reordered before we wait. */
DataSynchronizationBarrier();
InstructionSynchronizationBarrier();
/* If the time is small, spinloop, otherwise pend ourselves. */
if (us < 100) {
SpinWaitMicroSeconds(us);
} else {
os::SleepThread(TimeSpan::FromMicroSeconds(us));
}
/* Ensure that nothing is reordered after we wait. */
DataSynchronizationBarrier();
InstructionSynchronizationBarrier();
#elif defined(AMS_SDMMC_USE_UTIL_TIMER)
util::WaitMicroSeconds(us);
#else
#error "Unknown context for ams::sdmmc::impl::WaitMicroSeconds"
#endif
}
void WaitClocks(u32 num_clocks, u32 clock_frequency_khz) {
AMS_ABORT_UNLESS(clock_frequency_khz > 0);
WaitMicroSeconds(util::DivideUp(1000 * num_clocks, clock_frequency_khz));
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::sdmmc::impl {
void WaitMicroSeconds(u32 us);
void WaitClocks(u32 num_clocks, u32 clock_frequency_khz);
#if defined(AMS_SDMMC_USE_OS_TIMER)
class ManualTimer {
private:
os::Tick timeout_tick;
bool is_timed_out;
public:
explicit ManualTimer(u32 ms) {
this->timeout_tick = os::GetSystemTick() + os::ConvertToTick(TimeSpan::FromMilliSeconds(ms));
this->is_timed_out = false;
}
bool Update() {
if (this->is_timed_out) {
return false;
}
this->is_timed_out = os::GetSystemTick() > this->timeout_tick;
return true;
}
};
#elif defined(AMS_SDMMC_USE_UTIL_TIMER)
class ManualTimer {
private:
u32 timeout_us;
bool is_timed_out;
public:
explicit ManualTimer(u32 ms) {
this->timeout_us = util::GetMicroSeconds() + (ms * 1000);
this->is_timed_out = false;
}
bool Update() {
if (this->is_timed_out) {
return false;
}
this->is_timed_out = util::GetMicroSeconds() > this->timeout_us;
return true;
}
};
#else
#error "Unknown context for ams::sdmmc::ManualTimer"
#endif
}

View File

@@ -0,0 +1,145 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#include "impl/sdmmc_i_host_controller.hpp"
#include "impl/sdmmc_i_device_accessor.hpp"
#include "impl/sdmmc_clock_reset_controller.hpp"
#include "impl/sdmmc_port_mmc0.hpp"
#include "impl/sdmmc_port_sd_card0.hpp"
#include "impl/sdmmc_port_gc_asic0.hpp"
namespace ams::sdmmc {
namespace {
impl::IHostController *GetHostController(Port port) {
/* Get the controller. */
impl::IHostController *host_controller = nullptr;
switch (port) {
case Port_Mmc0: host_controller = impl::GetHostControllerOfPortMmc0(); break;
case Port_SdCard0: host_controller = impl::GetHostControllerOfPortSdCard0(); break;
case Port_GcAsic0: host_controller = impl::GetHostControllerOfPortGcAsic0(); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Ensure it's valid */
AMS_ABORT_UNLESS(host_controller != nullptr);
return host_controller;
}
impl::IDeviceAccessor *GetDeviceAccessor(Port port) {
/* Get the accessor. */
impl::IDeviceAccessor *device_accessor = nullptr;
switch (port) {
case Port_Mmc0: device_accessor = impl::GetDeviceAccessorOfPortMmc0(); break;
case Port_SdCard0: device_accessor = impl::GetDeviceAccessorOfPortSdCard0(); break;
case Port_GcAsic0: device_accessor = impl::GetDeviceAccessorOfPortGcAsic0(); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Ensure it's valid */
AMS_ABORT_UNLESS(device_accessor != nullptr);
return device_accessor;
}
}
void Initialize(Port port) {
return GetDeviceAccessor(port)->Initialize();
}
void Finalize(Port port) {
return GetDeviceAccessor(port)->Finalize();
}
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
void SwitchToPcvClockResetControl() {
return impl::ClockResetController::SwitchToPcvControl();
}
#endif
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
void RegisterDeviceVirtualAddress(Port port, uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) {
return GetDeviceAccessor(port)->RegisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address);
}
void UnregisterDeviceVirtualAddress(Port port, uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) {
return GetDeviceAccessor(port)->UnregisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address);
}
#endif
void ChangeCheckTransferInterval(Port port, u32 ms) {
return GetHostController(port)->ChangeCheckTransferInterval(ms);
}
void SetDefaultCheckTransferInterval(Port port) {
return GetHostController(port)->SetDefaultCheckTransferInterval();
}
Result Activate(Port port) {
return GetDeviceAccessor(port)->Activate();
}
void Deactivate(Port port) {
return GetDeviceAccessor(port)->Deactivate();
}
Result Read(void *dst, size_t dst_size, Port port, u32 sector_index, u32 num_sectors) {
return GetDeviceAccessor(port)->ReadWrite(sector_index, num_sectors, dst, dst_size, true);
}
Result Write(Port port, u32 sector_index, u32 num_sectors, const void *src, size_t src_size) {
return GetDeviceAccessor(port)->ReadWrite(sector_index, num_sectors, const_cast<void *>(src), src_size, false);
}
Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port) {
return GetDeviceAccessor(port)->CheckConnection(out_speed_mode, out_bus_width);
}
Result GetDeviceSpeedMode(SpeedMode *out, Port port) {
return GetDeviceAccessor(port)->GetSpeedMode(out);
}
Result GetDeviceMemoryCapacity(u32 *out_num_sectors, Port port) {
return GetDeviceAccessor(port)->GetMemoryCapacity(out_num_sectors);
}
Result GetDeviceStatus(u32 *out_device_status, Port port) {
return GetDeviceAccessor(port)->GetDeviceStatus(out_device_status);
}
Result GetDeviceCid(void *out, size_t out_size, Port port) {
return GetDeviceAccessor(port)->GetCid(out, out_size);
}
Result GetDeviceCsd(void *out, size_t out_size, Port port) {
return GetDeviceAccessor(port)->GetCsd(out, out_size);
}
void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size, Port port) {
return GetDeviceAccessor(port)->GetAndClearErrorInfo(out_error_info, out_log_size, out_log_buffer, log_buffer_size);
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#include "impl/sdmmc_gc_asic_device_accessor.hpp"
#include "impl/sdmmc_port_mmc0.hpp"
#include "impl/sdmmc_port_sd_card0.hpp"
#include "impl/sdmmc_port_gc_asic0.hpp"
namespace ams::sdmmc {
namespace {
impl::GcAsicDeviceAccessor *GetGcAsicDeviceAccessor(Port port) {
/* Get the accessor. */
impl::GcAsicDeviceAccessor *gc_asic_device_accessor = nullptr;
switch (port) {
case Port_GcAsic0: gc_asic_device_accessor = impl::GetGcAsicDeviceAccessorOfPortGcAsic0(); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Ensure it's valid */
AMS_ABORT_UNLESS(gc_asic_device_accessor != nullptr);
return gc_asic_device_accessor;
}
}
void PutGcAsicToSleep(Port port) {
return GetGcAsicDeviceAccessor(port)->PutGcAsicToSleep();
}
Result AwakenGcAsic(Port port) {
return GetGcAsicDeviceAccessor(port)->AwakenGcAsic();
}
Result WriteGcAsicOperation(Port port, const void *op_buf, size_t op_buf_size) {
return GetGcAsicDeviceAccessor(port)->WriteGcAsicOperation(op_buf, op_buf_size);
}
Result FinishGcAsicOperation(Port port) {
return GetGcAsicDeviceAccessor(port)->FinishGcAsicOperation();
}
Result AbortGcAsicOperation(Port port) {
return GetGcAsicDeviceAccessor(port)->AbortGcAsicOperation();
}
Result SleepGcAsic(Port port) {
return GetGcAsicDeviceAccessor(port)->SleepGcAsic();
}
Result UpdateGcAsicKey(Port port) {
return GetGcAsicDeviceAccessor(port)->UpdateGcAsicKey();
}
void SignalGcRemovedEvent(Port port) {
return GetGcAsicDeviceAccessor(port)->SignalGcRemovedEvent();
}
void ClearGcRemovedEvent(Port port) {
return GetGcAsicDeviceAccessor(port)->ClearGcRemovedEvent();
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#include "impl/sdmmc_mmc_device_accessor.hpp"
#include "impl/sdmmc_port_mmc0.hpp"
#include "impl/sdmmc_port_sd_card0.hpp"
#include "impl/sdmmc_port_gc_asic0.hpp"
namespace ams::sdmmc {
namespace {
impl::MmcDeviceAccessor *GetMmcDeviceAccessor(Port port) {
/* Get the accessor. */
impl::MmcDeviceAccessor *mmc_device_accessor = nullptr;
switch (port) {
case Port_Mmc0: mmc_device_accessor = impl::GetMmcDeviceAccessorOfPortMmc0(); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Ensure it's valid */
AMS_ABORT_UNLESS(mmc_device_accessor != nullptr);
return mmc_device_accessor;
}
}
void SetMmcWorkBuffer(Port port, void *buffer, size_t buffer_size) {
return GetMmcDeviceAccessor(port)->SetMmcWorkBuffer(buffer, buffer_size);
}
void PutMmcToSleep(Port port) {
return GetMmcDeviceAccessor(port)->PutMmcToSleep();
}
void AwakenMmc(Port port) {
return GetMmcDeviceAccessor(port)->AwakenMmc();
}
Result SelectMmcPartition(Port port, MmcPartition mmc_partition) {
return GetMmcDeviceAccessor(port)->SelectMmcPartition(mmc_partition);
}
Result EraseMmc(Port port) {
return GetMmcDeviceAccessor(port)->EraseMmc();
}
Result GetMmcBootPartitionCapacity(u32 *out_num_sectors, Port port) {
return GetMmcDeviceAccessor(port)->GetMmcBootPartitionCapacity(out_num_sectors);
}
Result GetMmcExtendedCsd(void *out_buffer, size_t buffer_size, Port port) {
return GetMmcDeviceAccessor(port)->GetMmcExtendedCsd(out_buffer, buffer_size);
}
Result CheckMmcConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port) {
return GetMmcDeviceAccessor(port)->CheckConnection(out_speed_mode, out_bus_width);
}
}

View File

@@ -0,0 +1,103 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#include "impl/sdmmc_sd_card_device_accessor.hpp"
#include "impl/sdmmc_port_mmc0.hpp"
#include "impl/sdmmc_port_sd_card0.hpp"
#include "impl/sdmmc_port_gc_asic0.hpp"
namespace ams::sdmmc {
namespace {
impl::SdCardDeviceAccessor *GetSdCardDeviceAccessor(Port port) {
/* Get the accessor. */
impl::SdCardDeviceAccessor *sd_card_device_accessor = nullptr;
switch (port) {
case Port_SdCard0: sd_card_device_accessor = impl::GetSdCardDeviceAccessorOfPortSdCard0(); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Ensure it's valid */
AMS_ABORT_UNLESS(sd_card_device_accessor != nullptr);
return sd_card_device_accessor;
}
}
void SetSdCardWorkBuffer(Port port, void *buffer, size_t buffer_size) {
return GetSdCardDeviceAccessor(port)->SetSdCardWorkBuffer(buffer, buffer_size);
}
void PutSdCardToSleep(Port port) {
return GetSdCardDeviceAccessor(port)->PutSdCardToSleep();
}
void AwakenSdCard(Port port) {
return GetSdCardDeviceAccessor(port)->AwakenSdCard();
}
Result GetSdCardProtectedAreaCapacity(u32 *out_num_sectors, Port port) {
return GetSdCardDeviceAccessor(port)->GetSdCardProtectedAreaCapacity(out_num_sectors);
}
Result GetSdCardScr(void *dst, size_t dst_size, Port port) {
return GetSdCardDeviceAccessor(port)->GetSdCardScr(dst, dst_size);
}
Result GetSdCardSwitchFunctionStatus(void *dst, size_t dst_size, Port port, SdCardSwitchFunction switch_function) {
return GetSdCardDeviceAccessor(port)->GetSdCardSwitchFunctionStatus(dst, dst_size, switch_function);
}
Result GetSdCardCurrentConsumption(u16 *out_current_consumption, Port port, SpeedMode speed_mode) {
return GetSdCardDeviceAccessor(port)->GetSdCardCurrentConsumption(out_current_consumption, speed_mode);
}
Result GetSdCardSdStatus(void *dst, size_t dst_size, Port port) {
return GetSdCardDeviceAccessor(port)->GetSdCardSdStatus(dst, dst_size);
}
Result CheckSdCardConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port) {
return GetSdCardDeviceAccessor(port)->CheckConnection(out_speed_mode, out_bus_width);
}
bool IsSdCardInserted(Port port) {
return GetSdCardDeviceAccessor(port)->IsSdCardInserted();
}
bool IsSdCardRemoved(Port port) {
return GetSdCardDeviceAccessor(port)->IsSdCardRemoved();
}
void RegisterSdCardDetectionEventCallback(Port port, DeviceDetectionEventCallback callback, void *arg) {
return GetSdCardDeviceAccessor(port)->RegisterSdCardDetectionEventCallback(callback, arg);
}
void UnregisterSdCardDetectionEventCallback(Port port) {
return GetSdCardDeviceAccessor(port)->UnregisterSdCardDetectionEventCallback();
}
}

View File

@@ -0,0 +1,434 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <vapours.hpp>
namespace ams::util {
#pragma GCC push_options
#pragma GCC optimize ("-Os")
namespace {
/* Useful definitions for our VSNPrintf implementation. */
enum FormatSpecifierFlag : u32 {
FormatSpecifierFlag_None = 0,
FormatSpecifierFlag_EmptySign = (1 << 0),
FormatSpecifierFlag_ForceSign = (1 << 1),
FormatSpecifierFlag_Hash = (1 << 2),
FormatSpecifierFlag_LeftJustify = (1 << 3),
FormatSpecifierFlag_ZeroPad = (1 << 4),
FormatSpecifierFlag_Char = (1 << 5),
FormatSpecifierFlag_Short = (1 << 6),
FormatSpecifierFlag_Long = (1 << 7),
FormatSpecifierFlag_LongLong = (1 << 8),
FormatSpecifierFlag_Uppercase = (1 << 9),
FormatSpecifierFlag_HasPrecision = (1 << 10),
};
using FormatSpecifierFlagStorage = std::underlying_type<FormatSpecifierFlag>::type;
constexpr ALWAYS_INLINE bool IsDigit(char c) {
return '0' <= c && c <= '9';
}
constexpr ALWAYS_INLINE u32 ParseU32(const char *&str) {
u32 value = 0;
do {
value = (value * 10) + static_cast<u32>(*(str++) - '0');
} while (IsDigit(*str));
return value;
}
constexpr ALWAYS_INLINE size_t Strnlen(const char *str, size_t max) {
const char *cur = str;
while (*cur && max--) {
cur++;
}
return static_cast<size_t>(cur - str);
}
int TVSNPrintfImpl(char * const dst, const size_t dst_size, const char *format, ::std::va_list vl) {
size_t dst_index = 0;
auto WriteCharacter = [dst, dst_size, &dst_index](char c) ALWAYS_INLINE_LAMBDA {
if (const size_t i = (dst_index++); i < dst_size) {
dst[i] = c;
}
};
/* Loop over every character in the string, looking for format specifiers. */
while (*format) {
if (const char c = *(format++); c != '%') {
WriteCharacter(c);
continue;
}
/* We have to parse a format specifier. */
/* Start by parsing flags. */
FormatSpecifierFlagStorage flags = FormatSpecifierFlag_None;
auto SetFlag = [&flags](FormatSpecifierFlag f) ALWAYS_INLINE_LAMBDA { flags |= f; };
auto ClearFlag = [&flags](FormatSpecifierFlag f) ALWAYS_INLINE_LAMBDA { flags &= ~f; };
auto HasFlag = [&flags](FormatSpecifierFlag f) ALWAYS_INLINE_LAMBDA { return (flags & f) != 0; };
{
bool parsed_flags = false;
while (!parsed_flags) {
switch (*format) {
case ' ': SetFlag(FormatSpecifierFlag_EmptySign); format++; break;
case '+': SetFlag(FormatSpecifierFlag_ForceSign); format++; break;
case '#': SetFlag(FormatSpecifierFlag_Hash); format++; break;
case '-': SetFlag(FormatSpecifierFlag_LeftJustify); format++; break;
case '0': SetFlag(FormatSpecifierFlag_ZeroPad); format++; break;
default:
parsed_flags = true;
break;
}
}
}
/* Next, parse width. */
u32 width = 0;
if (IsDigit(*format)) {
/* Integer width. */
width = ParseU32(format);
} else if (*format == '*') {
/* Dynamic width. */
const int _width = va_arg(vl, int);
if (_width >= 0) {
width = static_cast<u32>(_width);
} else {
SetFlag(FormatSpecifierFlag_LeftJustify);
width = static_cast<u32>(-_width);
}
format++;
}
/* Next, parse precision if present. */
u32 precision = 0;
if (*format == '.') {
SetFlag(FormatSpecifierFlag_HasPrecision);
format++;
if (IsDigit(*format)) {
/* Integer precision. */
precision = ParseU32(format);
} else if (*format == '*') {
/* Dynamic precision. */
const int _precision = va_arg(vl, int);
if (_precision > 0) {
precision = static_cast<u32>(_precision);
}
format++;
}
}
/* Parse length. */
constexpr bool SizeIsLong = sizeof(size_t) == sizeof(long);
constexpr bool IntMaxIsLong = sizeof(intmax_t) == sizeof(long);
constexpr bool PtrDiffIsLong = sizeof(ptrdiff_t) == sizeof(long);
switch (*format) {
case 'z':
SetFlag(SizeIsLong ? FormatSpecifierFlag_Long : FormatSpecifierFlag_LongLong);
format++;
break;
case 'j':
SetFlag(IntMaxIsLong ? FormatSpecifierFlag_Long : FormatSpecifierFlag_LongLong);
format++;
break;
case 't':
SetFlag(PtrDiffIsLong ? FormatSpecifierFlag_Long : FormatSpecifierFlag_LongLong);
format++;
break;
case 'h':
SetFlag(FormatSpecifierFlag_Short);
format++;
if (*format == 'h') {
SetFlag(FormatSpecifierFlag_Char);
format++;
}
break;
case 'l':
SetFlag(FormatSpecifierFlag_Long);
format++;
if (*format == 'l') {
SetFlag(FormatSpecifierFlag_LongLong);
format++;
}
break;
default:
break;
}
const char specifier = *(format++);
switch (specifier) {
case 'p':
if constexpr (sizeof(uintptr_t) == sizeof(long long)) {
SetFlag(FormatSpecifierFlag_LongLong);
} else {
SetFlag(FormatSpecifierFlag_Long);
}
SetFlag(FormatSpecifierFlag_Hash);
[[fallthrough]];
case 'd':
case 'i':
case 'u':
case 'b':
case 'o':
case 'x':
case 'X':
{
/* Determine the base to print with. */
u32 base;
switch (specifier) {
case 'b':
base = 2;
break;
case 'o':
base = 8;
break;
case 'X':
SetFlag(FormatSpecifierFlag_Uppercase);
[[fallthrough]];
case 'p':
case 'x':
base = 16;
break;
default:
base = 10;
ClearFlag(FormatSpecifierFlag_Hash);
break;
}
/* Precision implies no zero-padding. */
if (HasFlag(FormatSpecifierFlag_HasPrecision)) {
ClearFlag(FormatSpecifierFlag_ZeroPad);
}
/* Unsigned types don't get signs. */
const bool is_unsigned = base != 10 || specifier == 'u';
if (is_unsigned) {
ClearFlag(FormatSpecifierFlag_EmptySign);
ClearFlag(FormatSpecifierFlag_ForceSign);
}
auto PrintInteger = [&](bool negative, uintmax_t value) {
constexpr size_t BufferSize = 64; /* Binary digits for 64-bit numbers may use 64 digits. */
char buf[BufferSize];
size_t len = 0;
/* No hash flag for zero. */
if (value == 0) {
ClearFlag(FormatSpecifierFlag_Hash);
}
if (!HasFlag(FormatSpecifierFlag_HasPrecision) || value != 0) {
do {
const char digit = static_cast<char>(value % base);
buf[len++] = (digit < 10) ? ('0' + digit) : ((HasFlag(FormatSpecifierFlag_Uppercase) ? 'A' : 'a') + digit - 10);
value /= base;
} while (value);
}
/* Determine our prefix length. */
size_t prefix_len = 0;
const bool has_sign = negative || HasFlag(FormatSpecifierFlag_ForceSign) || HasFlag(FormatSpecifierFlag_EmptySign);
if (has_sign) {
prefix_len++;
}
if (HasFlag(FormatSpecifierFlag_Hash)) {
prefix_len += (base != 8) ? 2 : 1;
}
/* Determine zero-padding count. */
size_t num_zeroes = (len < precision) ? precision - len : 0;
if (!HasFlag(FormatSpecifierFlag_LeftJustify) && HasFlag(FormatSpecifierFlag_ZeroPad)) {
num_zeroes = (len + prefix_len < width) ? width - len - prefix_len : 0;
}
/* Print out left padding. */
if (!HasFlag(FormatSpecifierFlag_LeftJustify)) {
for (size_t i = len + prefix_len + num_zeroes; i < static_cast<size_t>(width); i++) {
WriteCharacter(' ');
}
}
/* Print out sign. */
if (negative) {
WriteCharacter('-');
} else if (HasFlag(FormatSpecifierFlag_ForceSign)) {
WriteCharacter('+');
} else if (HasFlag(FormatSpecifierFlag_EmptySign)) {
WriteCharacter(' ');
}
/* Print out base prefix. */
if (HasFlag(FormatSpecifierFlag_Hash)) {
WriteCharacter('0');
if (base == 2) {
WriteCharacter('b');
} else if (base == 16) {
WriteCharacter('x');
}
}
/* Print out zeroes. */
for (size_t i = 0; i < num_zeroes; i++) {
WriteCharacter('0');
}
/* Print out digits. */
for (size_t i = 0; i < len; i++) {
WriteCharacter(buf[len - 1 - i]);
}
/* Print out right padding. */
if (HasFlag(FormatSpecifierFlag_LeftJustify)) {
for (size_t i = len + prefix_len + num_zeroes; i < static_cast<size_t>(width); i++) {
WriteCharacter(' ');
}
}
};
/* Output the integer. */
if (is_unsigned) {
uintmax_t n = 0;
if (HasFlag(FormatSpecifierFlag_LongLong)) {
n = static_cast<unsigned long long>(va_arg(vl, unsigned long long));
} else if (HasFlag(FormatSpecifierFlag_Long)) {
n = static_cast<unsigned long>(va_arg(vl, unsigned long));
} else if (HasFlag(FormatSpecifierFlag_Char)) {
n = static_cast<unsigned char>(va_arg(vl, unsigned int));
} else if (HasFlag(FormatSpecifierFlag_Short)) {
n = static_cast<unsigned short>(va_arg(vl, unsigned int));
} else {
n = static_cast<unsigned int>(va_arg(vl, unsigned int));
}
if (specifier == 'p' && n == 0) {
WriteCharacter('(');
WriteCharacter('n');
WriteCharacter('i');
WriteCharacter('l');
WriteCharacter(')');
} else {
PrintInteger(false, n);
}
} else {
intmax_t n = 0;
if (HasFlag(FormatSpecifierFlag_LongLong)) {
n = static_cast<signed long long>(va_arg(vl, signed long long));
} else if (HasFlag(FormatSpecifierFlag_Long)) {
n = static_cast<signed long>(va_arg(vl, signed long));
} else if (HasFlag(FormatSpecifierFlag_Char)) {
n = static_cast<signed char>(va_arg(vl, signed int));
} else if (HasFlag(FormatSpecifierFlag_Short)) {
n = static_cast<signed short>(va_arg(vl, signed int));
} else {
n = static_cast<signed int>(va_arg(vl, signed int));
}
const bool negative = n < 0;
const uintmax_t u = (negative) ? static_cast<uintmax_t>(-n) : static_cast<uintmax_t>(n);
PrintInteger(negative, u);
}
}
break;
case 'c':
{
size_t len = 1;
if (!HasFlag(FormatSpecifierFlag_LeftJustify)) {
while (len++ < width) {
WriteCharacter(' ');
}
}
WriteCharacter(static_cast<char>(va_arg(vl, int)));
if (HasFlag(FormatSpecifierFlag_LeftJustify)) {
while (len++ < width) {
WriteCharacter(' ');
}
}
}
break;
case 's':
{
const char *str = va_arg(vl, char *);
if (str == nullptr) {
str = "(null)";
}
size_t len = Strnlen(str, precision > 0 ? precision : std::numeric_limits<size_t>::max());
if (HasFlag(FormatSpecifierFlag_HasPrecision)) {
len = (len < precision) ? len : precision;
}
if (!HasFlag(FormatSpecifierFlag_LeftJustify)) {
while (len++ < width) {
WriteCharacter(' ');
}
}
while (*str && (!HasFlag(FormatSpecifierFlag_HasPrecision) || (precision--) != 0)) {
WriteCharacter(*(str++));
}
if (HasFlag(FormatSpecifierFlag_LeftJustify)) {
while (len++ < width) {
WriteCharacter(' ');
}
}
}
break;
case '%':
default:
WriteCharacter(specifier);
break;
}
}
/* Ensure null termination. */
WriteCharacter('\0');
dst[dst_size - 1] = '\0';
/* Return number of characters that would have been printed sans the null terminator. */
return static_cast<int>(dst_index) - 1;
}
}
#pragma GCC pop_options
int TVSNPrintf(char *dst, size_t dst_size, const char *fmt, std::va_list vl) {
return TVSNPrintfImpl(dst, dst_size, fmt, vl);
}
int TSNPrintf(char *dst, size_t dst_size, const char *fmt, ...) {
std::va_list vl;
va_start(vl, fmt);
const int len = TVSNPrintf(dst, dst_size, fmt, vl);
va_end(vl);
return len;
}
int VSNPrintf(char *dst, size_t dst_size, const char *fmt, std::va_list vl) {
/* TODO: floating point support? */
return TVSNPrintfImpl(dst, dst_size, fmt, vl);
}
int SNPrintf(char *dst, size_t dst_size, const char *fmt, ...) {
std::va_list vl;
va_start(vl, fmt);
const int len = VSNPrintf(dst, dst_size, fmt, vl);
va_end(vl);
return len;
}
}