mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-09 22:56:35 +00:00
Merge branch 'master' into mesosphere-dev
This commit is contained in:
commit
3c5efefb15
7 changed files with 245 additions and 122 deletions
|
@ -615,7 +615,7 @@ static void sdmmc_autocal_run(sdmmc_t *sdmmc, SdmmcBusVoltage voltage)
|
||||||
while ((sdmmc->regs->auto_cal_status & SDMMC_AUTOCAL_ACTIVE)) {
|
while ((sdmmc->regs->auto_cal_status & SDMMC_AUTOCAL_ACTIVE)) {
|
||||||
/* Ensure we haven't timed out. */
|
/* Ensure we haven't timed out. */
|
||||||
if (get_time_since(timebase) > SDMMC_AUTOCAL_TIMEOUT) {
|
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. */
|
/* Force a register read to refresh the clock control value. */
|
||||||
sdmmc_get_sd_clock_control(sdmmc);
|
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)
|
static void sdmmc_intr_enable(sdmmc_t *sdmmc)
|
||||||
{
|
{
|
||||||
/* Set all error bits and enable the relevant interrupts. */
|
/* Enable the relevant interrupts and set all error bits. */
|
||||||
sdmmc->regs->int_enable |= 0x017F0000;
|
|
||||||
sdmmc->regs->int_enable |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT);
|
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. */
|
/* Refresh status. */
|
||||||
sdmmc->regs->int_status = sdmmc->regs->int_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)
|
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 &= ~(0x017F0000);
|
||||||
sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT);
|
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. */
|
sdmmc_debug(sdmmc, "INTSTS: %08X", int_status);
|
||||||
if (is_masked)
|
|
||||||
sdmmc->regs->int_status &= status_mask;
|
|
||||||
|
|
||||||
return is_masked;
|
/* Return the status, if necessary. */
|
||||||
}
|
if (status_out)
|
||||||
|
*status_out = (int_status & 0xFFFF);
|
||||||
static bool sdmmc_intr_check_error(sdmmc_t *sdmmc)
|
|
||||||
{
|
|
||||||
bool is_error = (sdmmc->regs->int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT);
|
|
||||||
|
|
||||||
/* Refresh status. */
|
if (int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT)
|
||||||
if (is_error)
|
{
|
||||||
sdmmc->regs->int_status = sdmmc->regs->int_status;
|
/* 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)
|
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. */
|
/* Watch over the DMA transfer. */
|
||||||
while (!is_timeout)
|
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. */
|
/* An error has been raised. Reset. */
|
||||||
if (sdmmc_intr_check_error(sdmmc))
|
if (intr_res < 0)
|
||||||
{
|
{
|
||||||
sdmmc_do_sw_reset(sdmmc);
|
sdmmc_do_sw_reset(sdmmc);
|
||||||
return 0;
|
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. */
|
/* 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)
|
if (sdmmc->use_adma)
|
||||||
{
|
{
|
||||||
|
@ -1467,10 +1476,6 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc)
|
||||||
sdmmc->next_dma_addr += 0x80000;
|
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. */
|
/* Keep checking if timeout expired. */
|
||||||
is_timeout = (get_time_since(timebase) > 2000000);
|
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. */
|
/* Wait for CMD to finish. */
|
||||||
while (!is_err && !is_timeout) {
|
while (!is_err && !is_timeout) {
|
||||||
|
/* Check interrupts. */
|
||||||
|
int intr_res = sdmmc_intr_check(sdmmc, 0, TEGRA_MMC_NORINTSTS_CMD_COMPLETE);
|
||||||
|
|
||||||
/* Command is done. */
|
/* Command is done. */
|
||||||
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_CMD_COMPLETE))
|
if (intr_res > 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* Check for any raised errors. */
|
/* Check for any raised errors. */
|
||||||
is_err = sdmmc_intr_check_error(sdmmc);
|
is_err = (intr_res < 0);
|
||||||
|
|
||||||
/* Keep checking if timeout expired. */
|
/* Keep checking if timeout expired. */
|
||||||
is_timeout = (get_time_since(timebase) > 2000000);
|
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;
|
is_dma = true;
|
||||||
dma_blkcnt = sdmmc_dma_init(sdmmc, req);
|
dma_blkcnt = sdmmc_dma_init(sdmmc, req);
|
||||||
|
|
||||||
|
/* Abort in case initialization failed. */
|
||||||
if (!dma_blkcnt)
|
if (!dma_blkcnt)
|
||||||
{
|
{
|
||||||
sdmmc_error(sdmmc, "Failed to initialize the DMA transfer!");
|
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. */
|
/* Save response, if necessary. */
|
||||||
sdmmc_save_response(sdmmc, cmd->flags);
|
sdmmc_save_response(sdmmc, cmd->flags);
|
||||||
|
|
||||||
/* Process the DMA request. */
|
/* Update the DMA request. */
|
||||||
if (req)
|
if (req)
|
||||||
{
|
{
|
||||||
|
/* Disable interrupts and abort in case updating failed. */
|
||||||
if (!sdmmc_dma_update(sdmmc))
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1840,7 +1851,7 @@ static int sdmmc_send_tuning(sdmmc_t *sdmmc, uint32_t opcode)
|
||||||
while (!is_timeout)
|
while (!is_timeout)
|
||||||
{
|
{
|
||||||
/* Buffer Read Ready was asserted. */
|
/* 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. */
|
/* Manually disable the Buffer Read Ready interrupt. */
|
||||||
sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY);
|
sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY);
|
||||||
|
|
|
@ -615,7 +615,7 @@ static void sdmmc_autocal_run(sdmmc_t *sdmmc, SdmmcBusVoltage voltage)
|
||||||
while ((sdmmc->regs->auto_cal_status & SDMMC_AUTOCAL_ACTIVE)) {
|
while ((sdmmc->regs->auto_cal_status & SDMMC_AUTOCAL_ACTIVE)) {
|
||||||
/* Ensure we haven't timed out. */
|
/* Ensure we haven't timed out. */
|
||||||
if (get_time_since(timebase) > SDMMC_AUTOCAL_TIMEOUT) {
|
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. */
|
/* Force a register read to refresh the clock control value. */
|
||||||
sdmmc_get_sd_clock_control(sdmmc);
|
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)
|
static void sdmmc_intr_enable(sdmmc_t *sdmmc)
|
||||||
{
|
{
|
||||||
/* Set all error bits and enable the relevant interrupts. */
|
/* Enable the relevant interrupts and set all error bits. */
|
||||||
sdmmc->regs->int_enable |= 0x017F0000;
|
|
||||||
sdmmc->regs->int_enable |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT);
|
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. */
|
/* Refresh status. */
|
||||||
sdmmc->regs->int_status = sdmmc->regs->int_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)
|
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 &= ~(0x017F0000);
|
||||||
sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT);
|
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. */
|
sdmmc_debug(sdmmc, "INTSTS: %08X", int_status);
|
||||||
if (is_masked)
|
|
||||||
sdmmc->regs->int_status &= status_mask;
|
|
||||||
|
|
||||||
return is_masked;
|
/* Return the status, if necessary. */
|
||||||
}
|
if (status_out)
|
||||||
|
*status_out = (int_status & 0xFFFF);
|
||||||
static bool sdmmc_intr_check_error(sdmmc_t *sdmmc)
|
|
||||||
{
|
|
||||||
bool is_error = (sdmmc->regs->int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT);
|
|
||||||
|
|
||||||
/* Refresh status. */
|
if (int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT)
|
||||||
if (is_error)
|
{
|
||||||
sdmmc->regs->int_status = sdmmc->regs->int_status;
|
/* 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)
|
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. */
|
/* Watch over the DMA transfer. */
|
||||||
while (!is_timeout)
|
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. */
|
/* An error has been raised. Reset. */
|
||||||
if (sdmmc_intr_check_error(sdmmc))
|
if (intr_res < 0)
|
||||||
{
|
{
|
||||||
sdmmc_do_sw_reset(sdmmc);
|
sdmmc_do_sw_reset(sdmmc);
|
||||||
return 0;
|
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. */
|
/* 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)
|
if (sdmmc->use_adma)
|
||||||
{
|
{
|
||||||
|
@ -1467,10 +1476,6 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc)
|
||||||
sdmmc->next_dma_addr += 0x80000;
|
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. */
|
/* Keep checking if timeout expired. */
|
||||||
is_timeout = (get_time_since(timebase) > 2000000);
|
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. */
|
/* Wait for CMD to finish. */
|
||||||
while (!is_err && !is_timeout) {
|
while (!is_err && !is_timeout) {
|
||||||
|
/* Check interrupts. */
|
||||||
|
int intr_res = sdmmc_intr_check(sdmmc, 0, TEGRA_MMC_NORINTSTS_CMD_COMPLETE);
|
||||||
|
|
||||||
/* Command is done. */
|
/* Command is done. */
|
||||||
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_CMD_COMPLETE))
|
if (intr_res > 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* Check for any raised errors. */
|
/* Check for any raised errors. */
|
||||||
is_err = sdmmc_intr_check_error(sdmmc);
|
is_err = (intr_res < 0);
|
||||||
|
|
||||||
/* Keep checking if timeout expired. */
|
/* Keep checking if timeout expired. */
|
||||||
is_timeout = (get_time_since(timebase) > 2000000);
|
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;
|
is_dma = true;
|
||||||
dma_blkcnt = sdmmc_dma_init(sdmmc, req);
|
dma_blkcnt = sdmmc_dma_init(sdmmc, req);
|
||||||
|
|
||||||
|
/* Abort in case initialization failed. */
|
||||||
if (!dma_blkcnt)
|
if (!dma_blkcnt)
|
||||||
{
|
{
|
||||||
sdmmc_error(sdmmc, "Failed to initialize the DMA transfer!");
|
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. */
|
/* Save response, if necessary. */
|
||||||
sdmmc_save_response(sdmmc, cmd->flags);
|
sdmmc_save_response(sdmmc, cmd->flags);
|
||||||
|
|
||||||
/* Process the DMA request. */
|
/* Update the DMA request. */
|
||||||
if (req)
|
if (req)
|
||||||
{
|
{
|
||||||
|
/* Disable interrupts and abort in case updating failed. */
|
||||||
if (!sdmmc_dma_update(sdmmc))
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1840,7 +1851,7 @@ static int sdmmc_send_tuning(sdmmc_t *sdmmc, uint32_t opcode)
|
||||||
while (!is_timeout)
|
while (!is_timeout)
|
||||||
{
|
{
|
||||||
/* Buffer Read Ready was asserted. */
|
/* 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. */
|
/* Manually disable the Buffer Read Ready interrupt. */
|
||||||
sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY);
|
sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY);
|
||||||
|
|
|
@ -26,36 +26,29 @@
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
|
||||||
|
/* C++ headers. */
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <atomic>
|
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <atomic>
|
||||||
/* Stratosphere wants stdlib headers, others do not.. */
|
|
||||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
|
||||||
|
|
||||||
/* C++ headers. */
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <shared_mutex>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
|
/* Stratosphere wants additional libstdc++ headers, others do not. */
|
||||||
|
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <shared_mutex>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#endif /* ATMOSPHERE_IS_STRATOSPHERE */
|
|
||||||
|
|
||||||
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
|
|
||||||
|
|
||||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
|
||||||
|
|
||||||
/* Libnx. */
|
/* Libnx. */
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
|
|
||||||
|
@ -64,13 +57,7 @@
|
||||||
/* Non-EL0 code can't include libnx. */
|
/* Non-EL0 code can't include libnx. */
|
||||||
#include "types.hpp"
|
#include "types.hpp"
|
||||||
|
|
||||||
#endif
|
#endif /* ATMOSPHERE_IS_STRATOSPHERE */
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#error "Unsupported board"
|
|
||||||
|
|
||||||
#endif /* ATMOSPHERE_BOARD_NINTENDO_NX */
|
|
||||||
|
|
||||||
/* Atmosphere meta. */
|
/* Atmosphere meta. */
|
||||||
#include "ams_version.h"
|
#include "ams_version.h"
|
||||||
|
|
|
@ -52,6 +52,31 @@ typedef u32 Result; ///< Function error code result type.
|
||||||
#define BIT(n) (1U<<(n))
|
#define BIT(n) (1U<<(n))
|
||||||
#endif
|
#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.
|
/// Marks a function as not returning, for the purposes of compiler optimization.
|
||||||
#ifndef NORETURN
|
#ifndef NORETURN
|
||||||
#define NORETURN __attribute__((noreturn))
|
#define NORETURN __attribute__((noreturn))
|
||||||
|
|
|
@ -28,3 +28,4 @@
|
||||||
#include "util/util_intrusive_list.hpp"
|
#include "util/util_intrusive_list.hpp"
|
||||||
#include "util/util_intrusive_red_black_tree.hpp"
|
#include "util/util_intrusive_red_black_tree.hpp"
|
||||||
#include "util/util_tinymt.hpp"
|
#include "util/util_tinymt.hpp"
|
||||||
|
#include "util/util_bitutil.hpp"
|
77
libraries/libvapours/include/vapours/util/util_bitutil.hpp
Normal file
77
libraries/libvapours/include/vapours/util/util_bitutil.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../defines.hpp"
|
||||||
|
#include "../types.hpp"
|
||||||
|
|
||||||
|
namespace ams::util {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class BitsOf {
|
||||||
|
private:
|
||||||
|
static_assert(std::is_integral<T>::value);
|
||||||
|
|
||||||
|
static constexpr ALWAYS_INLINE int GetLsbPos(T v) {
|
||||||
|
return __builtin_ctzll(static_cast<u64>(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<typename T = u64, typename ...Args>
|
||||||
|
constexpr ALWAYS_INLINE T CombineBits(Args... args) {
|
||||||
|
return (... | (T(1u) << args));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -615,7 +615,7 @@ static void sdmmc_autocal_run(sdmmc_t *sdmmc, SdmmcBusVoltage voltage)
|
||||||
while ((sdmmc->regs->auto_cal_status & SDMMC_AUTOCAL_ACTIVE)) {
|
while ((sdmmc->regs->auto_cal_status & SDMMC_AUTOCAL_ACTIVE)) {
|
||||||
/* Ensure we haven't timed out. */
|
/* Ensure we haven't timed out. */
|
||||||
if (get_time_since(timebase) > SDMMC_AUTOCAL_TIMEOUT) {
|
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. */
|
/* Force a register read to refresh the clock control value. */
|
||||||
sdmmc_get_sd_clock_control(sdmmc);
|
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)
|
static void sdmmc_intr_enable(sdmmc_t *sdmmc)
|
||||||
{
|
{
|
||||||
/* Set all error bits and enable the relevant interrupts. */
|
/* Enable the relevant interrupts and set all error bits. */
|
||||||
sdmmc->regs->int_enable |= 0x017F0000;
|
|
||||||
sdmmc->regs->int_enable |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT);
|
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. */
|
/* Refresh status. */
|
||||||
sdmmc->regs->int_status = sdmmc->regs->int_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)
|
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 &= ~(0x017F0000);
|
||||||
sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT);
|
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. */
|
sdmmc_debug(sdmmc, "INTSTS: %08X", int_status);
|
||||||
if (is_masked)
|
|
||||||
sdmmc->regs->int_status &= status_mask;
|
|
||||||
|
|
||||||
return is_masked;
|
/* Return the status, if necessary. */
|
||||||
}
|
if (status_out)
|
||||||
|
*status_out = (int_status & 0xFFFF);
|
||||||
static bool sdmmc_intr_check_error(sdmmc_t *sdmmc)
|
|
||||||
{
|
|
||||||
bool is_error = (sdmmc->regs->int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT);
|
|
||||||
|
|
||||||
/* Refresh status. */
|
if (int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT)
|
||||||
if (is_error)
|
{
|
||||||
sdmmc->regs->int_status = sdmmc->regs->int_status;
|
/* 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)
|
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. */
|
/* Watch over the DMA transfer. */
|
||||||
while (!is_timeout)
|
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. */
|
/* An error has been raised. Reset. */
|
||||||
if (sdmmc_intr_check_error(sdmmc))
|
if (intr_res < 0)
|
||||||
{
|
{
|
||||||
sdmmc_do_sw_reset(sdmmc);
|
sdmmc_do_sw_reset(sdmmc);
|
||||||
return 0;
|
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. */
|
/* 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)
|
if (sdmmc->use_adma)
|
||||||
{
|
{
|
||||||
|
@ -1467,10 +1476,6 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc)
|
||||||
sdmmc->next_dma_addr += 0x80000;
|
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. */
|
/* Keep checking if timeout expired. */
|
||||||
is_timeout = (get_time_since(timebase) > 2000000);
|
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. */
|
/* Wait for CMD to finish. */
|
||||||
while (!is_err && !is_timeout) {
|
while (!is_err && !is_timeout) {
|
||||||
|
/* Check interrupts. */
|
||||||
|
int intr_res = sdmmc_intr_check(sdmmc, 0, TEGRA_MMC_NORINTSTS_CMD_COMPLETE);
|
||||||
|
|
||||||
/* Command is done. */
|
/* Command is done. */
|
||||||
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_CMD_COMPLETE))
|
if (intr_res > 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* Check for any raised errors. */
|
/* Check for any raised errors. */
|
||||||
is_err = sdmmc_intr_check_error(sdmmc);
|
is_err = (intr_res < 0);
|
||||||
|
|
||||||
/* Keep checking if timeout expired. */
|
/* Keep checking if timeout expired. */
|
||||||
is_timeout = (get_time_since(timebase) > 2000000);
|
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;
|
is_dma = true;
|
||||||
dma_blkcnt = sdmmc_dma_init(sdmmc, req);
|
dma_blkcnt = sdmmc_dma_init(sdmmc, req);
|
||||||
|
|
||||||
|
/* Abort in case initialization failed. */
|
||||||
if (!dma_blkcnt)
|
if (!dma_blkcnt)
|
||||||
{
|
{
|
||||||
sdmmc_error(sdmmc, "Failed to initialize the DMA transfer!");
|
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. */
|
/* Save response, if necessary. */
|
||||||
sdmmc_save_response(sdmmc, cmd->flags);
|
sdmmc_save_response(sdmmc, cmd->flags);
|
||||||
|
|
||||||
/* Process the DMA request. */
|
/* Update the DMA request. */
|
||||||
if (req)
|
if (req)
|
||||||
{
|
{
|
||||||
|
/* Disable interrupts and abort in case updating failed. */
|
||||||
if (!sdmmc_dma_update(sdmmc))
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1840,7 +1851,7 @@ static int sdmmc_send_tuning(sdmmc_t *sdmmc, uint32_t opcode)
|
||||||
while (!is_timeout)
|
while (!is_timeout)
|
||||||
{
|
{
|
||||||
/* Buffer Read Ready was asserted. */
|
/* 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. */
|
/* Manually disable the Buffer Read Ready interrupt. */
|
||||||
sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY);
|
sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY);
|
||||||
|
|
Loading…
Reference in a new issue