diff --git a/fusee/fusee-primary/src/sdmmc/sdmmc_core.c b/fusee/fusee-primary/src/sdmmc/sdmmc_core.c index 8d70676cd..9a0eb4d2c 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); @@ -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,6 +1650,7 @@ 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); + /* Abort in case initialization failed. */ if (!dma_blkcnt) { sdmmc_error(sdmmc, "Failed to initialize the DMA transfer!"); @@ -1669,12 +1678,14 @@ 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) { + /* Disable interrupts and abort in case updating failed. */ if (!sdmmc_dma_update(sdmmc)) { - sdmmc_error(sdmmc, "Failed to process the DMA transfer!"); + sdmmc_warn(sdmmc, "Failed to update the DMA transfer!"); + sdmmc_intr_disable(sdmmc); return 0; } @@ -1840,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 8d70676cd..9a0eb4d2c 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); @@ -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,6 +1650,7 @@ 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); + /* Abort in case initialization failed. */ if (!dma_blkcnt) { sdmmc_error(sdmmc, "Failed to initialize the DMA transfer!"); @@ -1669,12 +1678,14 @@ 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) { + /* Disable interrupts and abort in case updating failed. */ if (!sdmmc_dma_update(sdmmc)) { - sdmmc_error(sdmmc, "Failed to process the DMA transfer!"); + sdmmc_warn(sdmmc, "Failed to update the DMA transfer!"); + sdmmc_intr_disable(sdmmc); return 0; } @@ -1840,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/libraries/libvapours/include/vapours/includes.hpp b/libraries/libvapours/include/vapours/includes.hpp index 64ac45ee2..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 #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_NX - -#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_NX */ +#endif /* ATMOSPHERE_IS_STRATOSPHERE */ /* Atmosphere meta. */ #include "ams_version.h" 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)) diff --git a/libraries/libvapours/include/vapours/util.hpp b/libraries/libvapours/include/vapours/util.hpp index 94013a1a9..2f1035ecc 100644 --- a/libraries/libvapours/include/vapours/util.hpp +++ b/libraries/libvapours/include/vapours/util.hpp @@ -28,3 +28,4 @@ #include "util/util_intrusive_list.hpp" #include "util/util_intrusive_red_black_tree.hpp" #include "util/util_tinymt.hpp" +#include "util/util_bitutil.hpp" \ No newline at end of file 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..ade7193b9 --- /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 + constexpr ALWAYS_INLINE T CombineBits(Args... args) { + return (... | (T(1u) << args)); + } + +} diff --git a/sept/sept-secondary/src/sdmmc/sdmmc_core.c b/sept/sept-secondary/src/sdmmc/sdmmc_core.c index 8d70676cd..9a0eb4d2c 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); @@ -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,6 +1650,7 @@ 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); + /* Abort in case initialization failed. */ if (!dma_blkcnt) { sdmmc_error(sdmmc, "Failed to initialize the DMA transfer!"); @@ -1669,12 +1678,14 @@ 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) { + /* Disable interrupts and abort in case updating failed. */ if (!sdmmc_dma_update(sdmmc)) { - sdmmc_error(sdmmc, "Failed to process the DMA transfer!"); + sdmmc_warn(sdmmc, "Failed to update the DMA transfer!"); + sdmmc_intr_disable(sdmmc); return 0; } @@ -1840,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);