From 877b2cf790a66cc7ff741a8c1474b0f052540f09 Mon Sep 17 00:00:00 2001 From: TuxSH Date: Mon, 3 Feb 2020 23:01:00 +0000 Subject: [PATCH 1/7] libvapours: introduce BITL, MASK, MASKL, MASK2, MASK2L which were already present in other ams components --- .../libvapours/include/vapours/types.hpp | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/libraries/libvapours/include/vapours/types.hpp b/libraries/libvapours/include/vapours/types.hpp index 891d661db..0d4b4b6f0 100644 --- a/libraries/libvapours/include/vapours/types.hpp +++ b/libraries/libvapours/include/vapours/types.hpp @@ -52,6 +52,31 @@ typedef u32 Result; ///< Function error code result type. #define BIT(n) (1U<<(n)) #endif +/// Creates a bitmask from a bit number (long). +#ifndef BITL +#define BITL(n) (1UL<<(n)) +#endif + +/// Creates a bitmask representing the n least significant bits. +#ifndef MASK +#define MASK(n) (BIT(n) - 1U) +#endif + +/// Creates a bitmask representing the n least significant bits (long). +#ifndef MASKL +#define MASKL(n) (BITL(n) - 1UL) +#endif + +/// Creates a bitmask for bit range extraction. +#ifndef MASK2 +#define MASK2(a,b) (MASK(a) & ~MASK(b)) +#endif + +/// Creates a bitmask for bit range extraction (long). +#ifndef MASK2L +#define MASK2L(a,b) (MASKL(a) & ~MASKL(b)) +#endif + /// Marks a function as not returning, for the purposes of compiler optimization. #ifndef NORETURN #define NORETURN __attribute__((noreturn)) From 108b997ef27bf51b1505b835c8cda4723e4dab8d Mon Sep 17 00:00:00 2001 From: TuxSH Date: Tue, 4 Feb 2020 01:24:16 +0000 Subject: [PATCH 2/7] libvapours: introduce BitsOf, Bits --- .../include/vapours/util/util_bitutil.hpp | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 libraries/libvapours/include/vapours/util/util_bitutil.hpp diff --git a/libraries/libvapours/include/vapours/util/util_bitutil.hpp b/libraries/libvapours/include/vapours/util/util_bitutil.hpp new file mode 100644 index 000000000..1a3063c0f --- /dev/null +++ b/libraries/libvapours/include/vapours/util/util_bitutil.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "../defines.hpp" +#include "../types.hpp" + +namespace ams::util { + + template + class BitsOf { + private: + static_assert(std::is_integral::value); + + static constexpr ALWAYS_INLINE int GetLsbPos(T v) { + return __builtin_ctzll(static_cast(v)); + } + + T value; + public: + /* Note: GCC has a bug in constant-folding here. Workaround: wrap entire caller with constexpr. */ + constexpr ALWAYS_INLINE BitsOf(T value = T(0u)) : value(value) { + /* ... */ + } + + constexpr ALWAYS_INLINE bool operator==(const BitsOf &other) const { + return this->value == other.value; + } + + constexpr ALWAYS_INLINE bool operator!=(const BitsOf &other) const { + return this->value != other.value; + } + + constexpr ALWAYS_INLINE int operator*() const { + return GetLsbPos(this->value); + } + + constexpr ALWAYS_INLINE BitsOf &operator++() { + this->value &= ~(T(1u) << GetLsbPos(this->value)); + return *this; + } + + constexpr ALWAYS_INLINE BitsOf &operator++(int) { + BitsOf ret(this->value); + ++(*this); + return ret; + } + + constexpr ALWAYS_INLINE BitsOf begin() const { + return *this; + } + + constexpr ALWAYS_INLINE BitsOf end() const { + return BitsOf(T(0u)); + } + }; + + template + T CombineBits(Args... args) { + return (... | (T(1u) << args)); + } + +} From 30e39e60f77efda7cbdf571afdd0ee0771fc1e18 Mon Sep 17 00:00:00 2001 From: TuxSH Date: Tue, 4 Feb 2020 02:04:59 +0000 Subject: [PATCH 3/7] libvapour: forgot to add util_bitutil.hpp to util.hpp --- libraries/libvapours/include/vapours/util.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/libvapours/include/vapours/util.hpp b/libraries/libvapours/include/vapours/util.hpp index 53bf9eac3..532abd3c5 100644 --- a/libraries/libvapours/include/vapours/util.hpp +++ b/libraries/libvapours/include/vapours/util.hpp @@ -25,3 +25,4 @@ #include "util/util_typed_storage.hpp" #include "util/util_intrusive_list.hpp" #include "util/util_intrusive_red_black_tree.hpp" +#include "util/util_bitutil.hpp" From c6a736a6f62e535800683417a519de3865e20e40 Mon Sep 17 00:00:00 2001 From: TuxSH Date: Tue, 4 Feb 2020 02:09:46 +0000 Subject: [PATCH 4/7] libvapours: constexpr ALWAYS_INLINE T CombineBits --- libraries/libvapours/include/vapours/util/util_bitutil.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/libvapours/include/vapours/util/util_bitutil.hpp b/libraries/libvapours/include/vapours/util/util_bitutil.hpp index 1a3063c0f..ade7193b9 100644 --- a/libraries/libvapours/include/vapours/util/util_bitutil.hpp +++ b/libraries/libvapours/include/vapours/util/util_bitutil.hpp @@ -70,7 +70,7 @@ namespace ams::util { }; template - T CombineBits(Args... args) { + constexpr ALWAYS_INLINE T CombineBits(Args... args) { return (... | (T(1u) << args)); } From 1309e93e26a82362e4ae1d8808b764f3a5216b06 Mon Sep 17 00:00:00 2001 From: TuxSH Date: Tue, 4 Feb 2020 19:02:17 +0000 Subject: [PATCH 5/7] libvapours: reorganize includes.hpp --- .../libvapours/include/vapours/includes.hpp | 31 ++++++------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/libraries/libvapours/include/vapours/includes.hpp b/libraries/libvapours/include/vapours/includes.hpp index 8d4e73436..9180881e0 100644 --- a/libraries/libvapours/include/vapours/includes.hpp +++ b/libraries/libvapours/include/vapours/includes.hpp @@ -26,36 +26,29 @@ #include #include - +/* C++ headers. */ #include #include #include #include #include - -/* Stratosphere wants stdlib headers, others do not.. */ -#ifdef ATMOSPHERE_IS_STRATOSPHERE - -/* C++ headers. */ #include #include #include -#include -#include -#include #include #include #include + +/* Stratosphere wants additional libstdc++ headers, others do not. */ +#ifdef ATMOSPHERE_IS_STRATOSPHERE + +#include +#include +#include #include #include #include -#endif /* ATMOSPHERE_IS_STRATOSPHERE */ - -#ifdef ATMOSPHERE_BOARD_NINTENDO_SWITCH - -#ifdef ATMOSPHERE_IS_STRATOSPHERE - /* Libnx. */ #include @@ -64,13 +57,7 @@ /* Non-EL0 code can't include libnx. */ #include "types.hpp" -#endif - -#else - -#error "Unsupported board" - -#endif /* ATMOSPHERE_BOARD_NINTENDO_SWITCH */ +#endif /* ATMOSPHERE_IS_STRATOSPHERE */ /* Atmosphere meta. */ #include "ams_version.h" From 9dc7a4dc18756e3965d514bdf196644aae8ffadf Mon Sep 17 00:00:00 2001 From: hexkyz Date: Thu, 6 Feb 2020 19:15:13 +0000 Subject: [PATCH 6/7] fusee/sept: don't abort on SDMMC DMA transfer failures --- fusee/fusee-primary/src/sdmmc/sdmmc_core.c | 14 +++++--------- fusee/fusee-secondary/src/sdmmc/sdmmc_core.c | 14 +++++--------- sept/sept-secondary/src/sdmmc/sdmmc_core.c | 14 +++++--------- 3 files changed, 15 insertions(+), 27 deletions(-) diff --git a/fusee/fusee-primary/src/sdmmc/sdmmc_core.c b/fusee/fusee-primary/src/sdmmc/sdmmc_core.c index 8d70676cd..e0805d991 100644 --- a/fusee/fusee-primary/src/sdmmc/sdmmc_core.c +++ b/fusee/fusee-primary/src/sdmmc/sdmmc_core.c @@ -615,7 +615,7 @@ static void sdmmc_autocal_run(sdmmc_t *sdmmc, SdmmcBusVoltage voltage) while ((sdmmc->regs->auto_cal_status & SDMMC_AUTOCAL_ACTIVE)) { /* Ensure we haven't timed out. */ if (get_time_since(timebase) > SDMMC_AUTOCAL_TIMEOUT) { - sdmmc_error(sdmmc, "Auto-calibration timed out!"); + sdmmc_warn(sdmmc, "Auto-calibration timed out!"); /* Force a register read to refresh the clock control value. */ sdmmc_get_sd_clock_control(sdmmc); @@ -1642,11 +1642,9 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u is_dma = true; dma_blkcnt = sdmmc_dma_init(sdmmc, req); + /* Warn in case initialization failed. This could indicate hardware failure. */ if (!dma_blkcnt) - { - sdmmc_error(sdmmc, "Failed to initialize the DMA transfer!"); - return 0; - } + sdmmc_warn(sdmmc, "Failed to initialize the DMA transfer!"); /* If this is a SDMA write operation, copy the data into our bounce buffer. */ if (!sdmmc->use_adma && !req->is_read) @@ -1672,11 +1670,9 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u /* Process the DMA request. */ if (req) { + /* Warn in case updating failed. This could indicate hardware failure. */ if (!sdmmc_dma_update(sdmmc)) - { - sdmmc_error(sdmmc, "Failed to process the DMA transfer!"); - return 0; - } + sdmmc_warn(sdmmc, "Failed to process the DMA transfer!"); /* If this is a SDMA read operation, copy the data from our bounce buffer. */ if (!sdmmc->use_adma && req->is_read) diff --git a/fusee/fusee-secondary/src/sdmmc/sdmmc_core.c b/fusee/fusee-secondary/src/sdmmc/sdmmc_core.c index 8d70676cd..e0805d991 100644 --- a/fusee/fusee-secondary/src/sdmmc/sdmmc_core.c +++ b/fusee/fusee-secondary/src/sdmmc/sdmmc_core.c @@ -615,7 +615,7 @@ static void sdmmc_autocal_run(sdmmc_t *sdmmc, SdmmcBusVoltage voltage) while ((sdmmc->regs->auto_cal_status & SDMMC_AUTOCAL_ACTIVE)) { /* Ensure we haven't timed out. */ if (get_time_since(timebase) > SDMMC_AUTOCAL_TIMEOUT) { - sdmmc_error(sdmmc, "Auto-calibration timed out!"); + sdmmc_warn(sdmmc, "Auto-calibration timed out!"); /* Force a register read to refresh the clock control value. */ sdmmc_get_sd_clock_control(sdmmc); @@ -1642,11 +1642,9 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u is_dma = true; dma_blkcnt = sdmmc_dma_init(sdmmc, req); + /* Warn in case initialization failed. This could indicate hardware failure. */ if (!dma_blkcnt) - { - sdmmc_error(sdmmc, "Failed to initialize the DMA transfer!"); - return 0; - } + sdmmc_warn(sdmmc, "Failed to initialize the DMA transfer!"); /* If this is a SDMA write operation, copy the data into our bounce buffer. */ if (!sdmmc->use_adma && !req->is_read) @@ -1672,11 +1670,9 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u /* Process the DMA request. */ if (req) { + /* Warn in case updating failed. This could indicate hardware failure. */ if (!sdmmc_dma_update(sdmmc)) - { - sdmmc_error(sdmmc, "Failed to process the DMA transfer!"); - return 0; - } + sdmmc_warn(sdmmc, "Failed to process the DMA transfer!"); /* If this is a SDMA read operation, copy the data from our bounce buffer. */ if (!sdmmc->use_adma && req->is_read) diff --git a/sept/sept-secondary/src/sdmmc/sdmmc_core.c b/sept/sept-secondary/src/sdmmc/sdmmc_core.c index 8d70676cd..e0805d991 100644 --- a/sept/sept-secondary/src/sdmmc/sdmmc_core.c +++ b/sept/sept-secondary/src/sdmmc/sdmmc_core.c @@ -615,7 +615,7 @@ static void sdmmc_autocal_run(sdmmc_t *sdmmc, SdmmcBusVoltage voltage) while ((sdmmc->regs->auto_cal_status & SDMMC_AUTOCAL_ACTIVE)) { /* Ensure we haven't timed out. */ if (get_time_since(timebase) > SDMMC_AUTOCAL_TIMEOUT) { - sdmmc_error(sdmmc, "Auto-calibration timed out!"); + sdmmc_warn(sdmmc, "Auto-calibration timed out!"); /* Force a register read to refresh the clock control value. */ sdmmc_get_sd_clock_control(sdmmc); @@ -1642,11 +1642,9 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u is_dma = true; dma_blkcnt = sdmmc_dma_init(sdmmc, req); + /* Warn in case initialization failed. This could indicate hardware failure. */ if (!dma_blkcnt) - { - sdmmc_error(sdmmc, "Failed to initialize the DMA transfer!"); - return 0; - } + sdmmc_warn(sdmmc, "Failed to initialize the DMA transfer!"); /* If this is a SDMA write operation, copy the data into our bounce buffer. */ if (!sdmmc->use_adma && !req->is_read) @@ -1672,11 +1670,9 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u /* Process the DMA request. */ if (req) { + /* Warn in case updating failed. This could indicate hardware failure. */ if (!sdmmc_dma_update(sdmmc)) - { - sdmmc_error(sdmmc, "Failed to process the DMA transfer!"); - return 0; - } + sdmmc_warn(sdmmc, "Failed to process the DMA transfer!"); /* If this is a SDMA read operation, copy the data from our bounce buffer. */ if (!sdmmc->use_adma && req->is_read) From 34bb800440abe33e0d14bd466491fc00f4cfe916 Mon Sep 17 00:00:00 2001 From: hexkyz Date: Tue, 11 Feb 2020 17:13:01 +0000 Subject: [PATCH 7/7] fusee/sept: restore and further match official behavior for SDMMC --- fusee/fusee-primary/src/sdmmc/sdmmc_core.c | 85 ++++++++++++-------- fusee/fusee-secondary/src/sdmmc/sdmmc_core.c | 85 ++++++++++++-------- sept/sept-secondary/src/sdmmc/sdmmc_core.c | 85 ++++++++++++-------- 3 files changed, 150 insertions(+), 105 deletions(-) diff --git a/fusee/fusee-primary/src/sdmmc/sdmmc_core.c b/fusee/fusee-primary/src/sdmmc/sdmmc_core.c index e0805d991..9a0eb4d2c 100644 --- a/fusee/fusee-primary/src/sdmmc/sdmmc_core.c +++ b/fusee/fusee-primary/src/sdmmc/sdmmc_core.c @@ -1314,9 +1314,9 @@ static int sdmmc_wait_busy(sdmmc_t *sdmmc) static void sdmmc_intr_enable(sdmmc_t *sdmmc) { - /* Set all error bits and enable the relevant interrupts. */ - sdmmc->regs->int_enable |= 0x017F0000; + /* Enable the relevant interrupts and set all error bits. */ sdmmc->regs->int_enable |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT); + sdmmc->regs->int_enable |= 0x017F0000; /* Refresh status. */ sdmmc->regs->int_status = sdmmc->regs->int_status; @@ -1324,34 +1324,35 @@ static void sdmmc_intr_enable(sdmmc_t *sdmmc) static void sdmmc_intr_disable(sdmmc_t *sdmmc) { - /* Clear all error bits and the interrupts. */ + /* Clear all error bits and disable the relevant interrupts. */ sdmmc->regs->int_enable &= ~(0x017F0000); sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT); - - /* Refresh status. */ - sdmmc->regs->int_status = sdmmc->regs->int_status; } -static bool sdmmc_intr_check_status(sdmmc_t *sdmmc, uint16_t status_mask) +static int sdmmc_intr_check(sdmmc_t *sdmmc, uint16_t *status_out, uint16_t status_mask) { - bool is_masked = (sdmmc->regs->int_status & status_mask); + uint32_t int_status = sdmmc->regs->int_status; - /* Mask status. */ - if (is_masked) - sdmmc->regs->int_status &= status_mask; + sdmmc_debug(sdmmc, "INTSTS: %08X", int_status); - return is_masked; -} - -static bool sdmmc_intr_check_error(sdmmc_t *sdmmc) -{ - bool is_error = (sdmmc->regs->int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT); + /* Return the status, if necessary. */ + if (status_out) + *status_out = (int_status & 0xFFFF); - /* Refresh status. */ - if (is_error) - sdmmc->regs->int_status = sdmmc->regs->int_status; + if (int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) + { + /* Acknowledge error by refreshing status. */ + sdmmc->regs->int_status = int_status; + return -1; + } + else if (int_status & status_mask) + { + /* Mask the status. */ + sdmmc->regs->int_status = (int_status & status_mask); + return 1; + } - return is_error; + return 0; } static int sdmmc_dma_init(sdmmc_t *sdmmc, sdmmc_request_t *req) @@ -1442,15 +1443,23 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc) /* Watch over the DMA transfer. */ while (!is_timeout) { + /* Check interrupts. */ + uint16_t intr_status = 0; + int intr_res = sdmmc_intr_check(sdmmc, &intr_status, TEGRA_MMC_NORINTSTS_XFER_COMPLETE | TEGRA_MMC_NORINTSTS_DMA_INTERRUPT); + /* An error has been raised. Reset. */ - if (sdmmc_intr_check_error(sdmmc)) + if (intr_res < 0) { sdmmc_do_sw_reset(sdmmc); return 0; } + /* Transfer is over. */ + if (intr_status & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) + return 1; + /* We have a DMA interrupt. Restart the transfer where it was interrupted. */ - if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_DMA_INTERRUPT)) + if (intr_status & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) { if (sdmmc->use_adma) { @@ -1467,10 +1476,6 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc) sdmmc->next_dma_addr += 0x80000; } - /* Transfer is over. */ - if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_XFER_COMPLETE)) - return 1; - /* Keep checking if timeout expired. */ is_timeout = (get_time_since(timebase) > 2000000); } @@ -1526,12 +1531,15 @@ static int sdmmc_wait_for_cmd(sdmmc_t *sdmmc) /* Wait for CMD to finish. */ while (!is_err && !is_timeout) { + /* Check interrupts. */ + int intr_res = sdmmc_intr_check(sdmmc, 0, TEGRA_MMC_NORINTSTS_CMD_COMPLETE); + /* Command is done. */ - if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_CMD_COMPLETE)) + if (intr_res > 0) return 1; /* Check for any raised errors. */ - is_err = sdmmc_intr_check_error(sdmmc); + is_err = (intr_res < 0); /* Keep checking if timeout expired. */ is_timeout = (get_time_since(timebase) > 2000000); @@ -1642,9 +1650,12 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u is_dma = true; dma_blkcnt = sdmmc_dma_init(sdmmc, req); - /* Warn in case initialization failed. This could indicate hardware failure. */ + /* Abort in case initialization failed. */ if (!dma_blkcnt) - sdmmc_warn(sdmmc, "Failed to initialize the DMA transfer!"); + { + sdmmc_error(sdmmc, "Failed to initialize the DMA transfer!"); + return 0; + } /* If this is a SDMA write operation, copy the data into our bounce buffer. */ if (!sdmmc->use_adma && !req->is_read) @@ -1667,12 +1678,16 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u /* Save response, if necessary. */ sdmmc_save_response(sdmmc, cmd->flags); - /* Process the DMA request. */ + /* Update the DMA request. */ if (req) { - /* Warn in case updating failed. This could indicate hardware failure. */ + /* Disable interrupts and abort in case updating failed. */ if (!sdmmc_dma_update(sdmmc)) - sdmmc_warn(sdmmc, "Failed to process the DMA transfer!"); + { + sdmmc_warn(sdmmc, "Failed to update the DMA transfer!"); + sdmmc_intr_disable(sdmmc); + return 0; + } /* If this is a SDMA read operation, copy the data from our bounce buffer. */ if (!sdmmc->use_adma && req->is_read) @@ -1836,7 +1851,7 @@ static int sdmmc_send_tuning(sdmmc_t *sdmmc, uint32_t opcode) while (!is_timeout) { /* Buffer Read Ready was asserted. */ - if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY)) + if (sdmmc_intr_check(sdmmc, 0, TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY) > 0) { /* Manually disable the Buffer Read Ready interrupt. */ sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY); diff --git a/fusee/fusee-secondary/src/sdmmc/sdmmc_core.c b/fusee/fusee-secondary/src/sdmmc/sdmmc_core.c index e0805d991..9a0eb4d2c 100644 --- a/fusee/fusee-secondary/src/sdmmc/sdmmc_core.c +++ b/fusee/fusee-secondary/src/sdmmc/sdmmc_core.c @@ -1314,9 +1314,9 @@ static int sdmmc_wait_busy(sdmmc_t *sdmmc) static void sdmmc_intr_enable(sdmmc_t *sdmmc) { - /* Set all error bits and enable the relevant interrupts. */ - sdmmc->regs->int_enable |= 0x017F0000; + /* Enable the relevant interrupts and set all error bits. */ sdmmc->regs->int_enable |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT); + sdmmc->regs->int_enable |= 0x017F0000; /* Refresh status. */ sdmmc->regs->int_status = sdmmc->regs->int_status; @@ -1324,34 +1324,35 @@ static void sdmmc_intr_enable(sdmmc_t *sdmmc) static void sdmmc_intr_disable(sdmmc_t *sdmmc) { - /* Clear all error bits and the interrupts. */ + /* Clear all error bits and disable the relevant interrupts. */ sdmmc->regs->int_enable &= ~(0x017F0000); sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT); - - /* Refresh status. */ - sdmmc->regs->int_status = sdmmc->regs->int_status; } -static bool sdmmc_intr_check_status(sdmmc_t *sdmmc, uint16_t status_mask) +static int sdmmc_intr_check(sdmmc_t *sdmmc, uint16_t *status_out, uint16_t status_mask) { - bool is_masked = (sdmmc->regs->int_status & status_mask); + uint32_t int_status = sdmmc->regs->int_status; - /* Mask status. */ - if (is_masked) - sdmmc->regs->int_status &= status_mask; + sdmmc_debug(sdmmc, "INTSTS: %08X", int_status); - return is_masked; -} - -static bool sdmmc_intr_check_error(sdmmc_t *sdmmc) -{ - bool is_error = (sdmmc->regs->int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT); + /* Return the status, if necessary. */ + if (status_out) + *status_out = (int_status & 0xFFFF); - /* Refresh status. */ - if (is_error) - sdmmc->regs->int_status = sdmmc->regs->int_status; + if (int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) + { + /* Acknowledge error by refreshing status. */ + sdmmc->regs->int_status = int_status; + return -1; + } + else if (int_status & status_mask) + { + /* Mask the status. */ + sdmmc->regs->int_status = (int_status & status_mask); + return 1; + } - return is_error; + return 0; } static int sdmmc_dma_init(sdmmc_t *sdmmc, sdmmc_request_t *req) @@ -1442,15 +1443,23 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc) /* Watch over the DMA transfer. */ while (!is_timeout) { + /* Check interrupts. */ + uint16_t intr_status = 0; + int intr_res = sdmmc_intr_check(sdmmc, &intr_status, TEGRA_MMC_NORINTSTS_XFER_COMPLETE | TEGRA_MMC_NORINTSTS_DMA_INTERRUPT); + /* An error has been raised. Reset. */ - if (sdmmc_intr_check_error(sdmmc)) + if (intr_res < 0) { sdmmc_do_sw_reset(sdmmc); return 0; } + /* Transfer is over. */ + if (intr_status & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) + return 1; + /* We have a DMA interrupt. Restart the transfer where it was interrupted. */ - if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_DMA_INTERRUPT)) + if (intr_status & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) { if (sdmmc->use_adma) { @@ -1467,10 +1476,6 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc) sdmmc->next_dma_addr += 0x80000; } - /* Transfer is over. */ - if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_XFER_COMPLETE)) - return 1; - /* Keep checking if timeout expired. */ is_timeout = (get_time_since(timebase) > 2000000); } @@ -1526,12 +1531,15 @@ static int sdmmc_wait_for_cmd(sdmmc_t *sdmmc) /* Wait for CMD to finish. */ while (!is_err && !is_timeout) { + /* Check interrupts. */ + int intr_res = sdmmc_intr_check(sdmmc, 0, TEGRA_MMC_NORINTSTS_CMD_COMPLETE); + /* Command is done. */ - if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_CMD_COMPLETE)) + if (intr_res > 0) return 1; /* Check for any raised errors. */ - is_err = sdmmc_intr_check_error(sdmmc); + is_err = (intr_res < 0); /* Keep checking if timeout expired. */ is_timeout = (get_time_since(timebase) > 2000000); @@ -1642,9 +1650,12 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u is_dma = true; dma_blkcnt = sdmmc_dma_init(sdmmc, req); - /* Warn in case initialization failed. This could indicate hardware failure. */ + /* Abort in case initialization failed. */ if (!dma_blkcnt) - sdmmc_warn(sdmmc, "Failed to initialize the DMA transfer!"); + { + sdmmc_error(sdmmc, "Failed to initialize the DMA transfer!"); + return 0; + } /* If this is a SDMA write operation, copy the data into our bounce buffer. */ if (!sdmmc->use_adma && !req->is_read) @@ -1667,12 +1678,16 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u /* Save response, if necessary. */ sdmmc_save_response(sdmmc, cmd->flags); - /* Process the DMA request. */ + /* Update the DMA request. */ if (req) { - /* Warn in case updating failed. This could indicate hardware failure. */ + /* Disable interrupts and abort in case updating failed. */ if (!sdmmc_dma_update(sdmmc)) - sdmmc_warn(sdmmc, "Failed to process the DMA transfer!"); + { + sdmmc_warn(sdmmc, "Failed to update the DMA transfer!"); + sdmmc_intr_disable(sdmmc); + return 0; + } /* If this is a SDMA read operation, copy the data from our bounce buffer. */ if (!sdmmc->use_adma && req->is_read) @@ -1836,7 +1851,7 @@ static int sdmmc_send_tuning(sdmmc_t *sdmmc, uint32_t opcode) while (!is_timeout) { /* Buffer Read Ready was asserted. */ - if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY)) + if (sdmmc_intr_check(sdmmc, 0, TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY) > 0) { /* Manually disable the Buffer Read Ready interrupt. */ sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY); diff --git a/sept/sept-secondary/src/sdmmc/sdmmc_core.c b/sept/sept-secondary/src/sdmmc/sdmmc_core.c index e0805d991..9a0eb4d2c 100644 --- a/sept/sept-secondary/src/sdmmc/sdmmc_core.c +++ b/sept/sept-secondary/src/sdmmc/sdmmc_core.c @@ -1314,9 +1314,9 @@ static int sdmmc_wait_busy(sdmmc_t *sdmmc) static void sdmmc_intr_enable(sdmmc_t *sdmmc) { - /* Set all error bits and enable the relevant interrupts. */ - sdmmc->regs->int_enable |= 0x017F0000; + /* Enable the relevant interrupts and set all error bits. */ sdmmc->regs->int_enable |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT); + sdmmc->regs->int_enable |= 0x017F0000; /* Refresh status. */ sdmmc->regs->int_status = sdmmc->regs->int_status; @@ -1324,34 +1324,35 @@ static void sdmmc_intr_enable(sdmmc_t *sdmmc) static void sdmmc_intr_disable(sdmmc_t *sdmmc) { - /* Clear all error bits and the interrupts. */ + /* Clear all error bits and disable the relevant interrupts. */ sdmmc->regs->int_enable &= ~(0x017F0000); sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT); - - /* Refresh status. */ - sdmmc->regs->int_status = sdmmc->regs->int_status; } -static bool sdmmc_intr_check_status(sdmmc_t *sdmmc, uint16_t status_mask) +static int sdmmc_intr_check(sdmmc_t *sdmmc, uint16_t *status_out, uint16_t status_mask) { - bool is_masked = (sdmmc->regs->int_status & status_mask); + uint32_t int_status = sdmmc->regs->int_status; - /* Mask status. */ - if (is_masked) - sdmmc->regs->int_status &= status_mask; + sdmmc_debug(sdmmc, "INTSTS: %08X", int_status); - return is_masked; -} - -static bool sdmmc_intr_check_error(sdmmc_t *sdmmc) -{ - bool is_error = (sdmmc->regs->int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT); + /* Return the status, if necessary. */ + if (status_out) + *status_out = (int_status & 0xFFFF); - /* Refresh status. */ - if (is_error) - sdmmc->regs->int_status = sdmmc->regs->int_status; + if (int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) + { + /* Acknowledge error by refreshing status. */ + sdmmc->regs->int_status = int_status; + return -1; + } + else if (int_status & status_mask) + { + /* Mask the status. */ + sdmmc->regs->int_status = (int_status & status_mask); + return 1; + } - return is_error; + return 0; } static int sdmmc_dma_init(sdmmc_t *sdmmc, sdmmc_request_t *req) @@ -1442,15 +1443,23 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc) /* Watch over the DMA transfer. */ while (!is_timeout) { + /* Check interrupts. */ + uint16_t intr_status = 0; + int intr_res = sdmmc_intr_check(sdmmc, &intr_status, TEGRA_MMC_NORINTSTS_XFER_COMPLETE | TEGRA_MMC_NORINTSTS_DMA_INTERRUPT); + /* An error has been raised. Reset. */ - if (sdmmc_intr_check_error(sdmmc)) + if (intr_res < 0) { sdmmc_do_sw_reset(sdmmc); return 0; } + /* Transfer is over. */ + if (intr_status & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) + return 1; + /* We have a DMA interrupt. Restart the transfer where it was interrupted. */ - if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_DMA_INTERRUPT)) + if (intr_status & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) { if (sdmmc->use_adma) { @@ -1467,10 +1476,6 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc) sdmmc->next_dma_addr += 0x80000; } - /* Transfer is over. */ - if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_XFER_COMPLETE)) - return 1; - /* Keep checking if timeout expired. */ is_timeout = (get_time_since(timebase) > 2000000); } @@ -1526,12 +1531,15 @@ static int sdmmc_wait_for_cmd(sdmmc_t *sdmmc) /* Wait for CMD to finish. */ while (!is_err && !is_timeout) { + /* Check interrupts. */ + int intr_res = sdmmc_intr_check(sdmmc, 0, TEGRA_MMC_NORINTSTS_CMD_COMPLETE); + /* Command is done. */ - if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_CMD_COMPLETE)) + if (intr_res > 0) return 1; /* Check for any raised errors. */ - is_err = sdmmc_intr_check_error(sdmmc); + is_err = (intr_res < 0); /* Keep checking if timeout expired. */ is_timeout = (get_time_since(timebase) > 2000000); @@ -1642,9 +1650,12 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u is_dma = true; dma_blkcnt = sdmmc_dma_init(sdmmc, req); - /* Warn in case initialization failed. This could indicate hardware failure. */ + /* Abort in case initialization failed. */ if (!dma_blkcnt) - sdmmc_warn(sdmmc, "Failed to initialize the DMA transfer!"); + { + sdmmc_error(sdmmc, "Failed to initialize the DMA transfer!"); + return 0; + } /* If this is a SDMA write operation, copy the data into our bounce buffer. */ if (!sdmmc->use_adma && !req->is_read) @@ -1667,12 +1678,16 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u /* Save response, if necessary. */ sdmmc_save_response(sdmmc, cmd->flags); - /* Process the DMA request. */ + /* Update the DMA request. */ if (req) { - /* Warn in case updating failed. This could indicate hardware failure. */ + /* Disable interrupts and abort in case updating failed. */ if (!sdmmc_dma_update(sdmmc)) - sdmmc_warn(sdmmc, "Failed to process the DMA transfer!"); + { + sdmmc_warn(sdmmc, "Failed to update the DMA transfer!"); + sdmmc_intr_disable(sdmmc); + return 0; + } /* If this is a SDMA read operation, copy the data from our bounce buffer. */ if (!sdmmc->use_adma && req->is_read) @@ -1836,7 +1851,7 @@ static int sdmmc_send_tuning(sdmmc_t *sdmmc, uint32_t opcode) while (!is_timeout) { /* Buffer Read Ready was asserted. */ - if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY)) + if (sdmmc_intr_check(sdmmc, 0, TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY) > 0) { /* Manually disable the Buffer Read Ready interrupt. */ sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY);