mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-05 19:51:45 +00:00
stage1 -> stage2 again
This commit is contained in:
parent
50047dffaa
commit
75169790ff
6 changed files with 123 additions and 58 deletions
|
@ -4,7 +4,6 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "sdmmc.h"
|
#include "sdmmc.h"
|
||||||
|
|
||||||
int initialize_sd(void);
|
|
||||||
size_t read_sd_file(void *dst, size_t dst_size, const char *filename);
|
size_t read_sd_file(void *dst, size_t dst_size, const char *filename);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -7,12 +7,23 @@
|
||||||
/* storage control modules to the FatFs module with a defined API. */
|
/* storage control modules to the FatFs module with a defined API. */
|
||||||
/*-----------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "diskio.h" /* FatFs lower layer API */
|
#include "diskio.h" /* FatFs lower layer API */
|
||||||
#include "../../sdmmc.h"
|
#include "../../sdmmc.h"
|
||||||
|
#include "../../hwinit.h"
|
||||||
|
|
||||||
/* Global sd struct. */
|
/* Global sd struct. */
|
||||||
extern struct mmc sd_mmc;
|
struct mmc g_sd_mmc = {0};
|
||||||
|
static bool g_sd_initialized = false;
|
||||||
|
|
||||||
|
static bool g_ahb_redirect_enabled = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Uncomment if needed:
|
||||||
|
struct mmc nand_mmc = {0};
|
||||||
|
static bool g_nand_initialized = false;
|
||||||
|
*/
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------*/
|
||||||
/* Get Drive Status */
|
/* Get Drive Status */
|
||||||
|
@ -35,7 +46,28 @@ DSTATUS disk_initialize (
|
||||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
if (!g_ahb_redirect_enabled) {
|
||||||
|
mc_enable_ahb_redirect();
|
||||||
|
g_ahb_redirect_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (pdrv) {
|
||||||
|
case 0: {
|
||||||
|
if (!g_sd_initialized) {
|
||||||
|
int rc = sdmmc_init(&g_sd_mmc, SWITCH_MICROSD);
|
||||||
|
if (rc == 0) {
|
||||||
|
g_sd_initialized = true;
|
||||||
return 0;
|
return 0;
|
||||||
|
} else {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return STA_NODISK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,12 +83,12 @@ DRESULT disk_read (
|
||||||
UINT count /* Number of sectors to read */
|
UINT count /* Number of sectors to read */
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < count; i++) {
|
switch (pdrv) {
|
||||||
if (sdmmc_read(&sd_mmc, buff + 0x200 * i, sector + i, 1) != 0) {
|
case 0:
|
||||||
return RES_ERROR;
|
return sdmmc_read(&g_sd_mmc, buff, sector, count) == 0 ? RES_OK : RES_ERROR;
|
||||||
|
default:
|
||||||
|
return RES_PARERR;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return RES_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,7 +104,12 @@ DRESULT disk_write (
|
||||||
UINT count /* Number of sectors to write */
|
UINT count /* Number of sectors to write */
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return RES_ERROR;
|
switch (pdrv) {
|
||||||
|
case 0:
|
||||||
|
return sdmmc_write(&g_sd_mmc, buff, sector, count) == 0 ? RES_OK : RES_ERROR;
|
||||||
|
default:
|
||||||
|
return RES_PARERR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,6 +124,6 @@ DRESULT disk_ioctl (
|
||||||
void *buff /* Buffer to send/receive control data */
|
void *buff /* Buffer to send/receive control data */
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return RES_OK;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,6 @@ static void setup_env(void) {
|
||||||
generic_panic();
|
generic_panic();
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize_sd();
|
|
||||||
if(fsdev_mount_all() == -1) {
|
if(fsdev_mount_all() == -1) {
|
||||||
perror("Failed to mount at least one FAT parition");
|
perror("Failed to mount at least one FAT parition");
|
||||||
generic_panic();
|
generic_panic();
|
||||||
|
|
|
@ -3,25 +3,6 @@
|
||||||
#include "hwinit.h"
|
#include "hwinit.h"
|
||||||
#include "sdmmc.h"
|
#include "sdmmc.h"
|
||||||
|
|
||||||
/* This is used by diskio.h. */
|
|
||||||
struct mmc sd_mmc;
|
|
||||||
static int initialized_sd = 0;
|
|
||||||
|
|
||||||
int initialize_sd(void) {
|
|
||||||
if (initialized_sd) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
mc_enable_ahb_redirect();
|
|
||||||
if (sdmmc_init(&sd_mmc, SWITCH_MICROSD) == 0) {
|
|
||||||
printf("Initialized SD card!\n");
|
|
||||||
initialized_sd = 1;
|
|
||||||
} else {
|
|
||||||
printf("Failed to initialize the SD card!\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return initialized_sd;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t read_sd_file(void *dst, size_t dst_size, const char *filename) {
|
size_t read_sd_file(void *dst, size_t dst_size, const char *filename) {
|
||||||
FILE *file = fopen(filename, "rb");
|
FILE *file = fopen(filename, "rb");
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
|
|
|
@ -155,9 +155,13 @@ enum sdmmc_register_bits {
|
||||||
MMC_DAT0_LINE_STATE = (1 << 20),
|
MMC_DAT0_LINE_STATE = (1 << 20),
|
||||||
|
|
||||||
/* Block size register */
|
/* Block size register */
|
||||||
MMC_DMA_BOUNDARY_MAXIMUM = (0x3 << 12),
|
MMC_DMA_BOUNDARY_MAXIMUM = (0x7 << 12),
|
||||||
MMC_DMA_BOUNDARY_512K = (0x3 << 12),
|
MMC_DMA_BOUNDARY_512K = (0x7 << 12),
|
||||||
|
MMC_DMA_BOUNDARY_64K = (0x4 << 12),
|
||||||
|
MMC_DMA_BOUNDARY_32K = (0x3 << 12),
|
||||||
MMC_DMA_BOUNDARY_16K = (0x2 << 12),
|
MMC_DMA_BOUNDARY_16K = (0x2 << 12),
|
||||||
|
MMC_DMA_BOUNDARY_8K = (0x1 << 12),
|
||||||
|
MMC_DMA_BOUNDARY_4K = (0x0 << 12),
|
||||||
MMC_TRANSFER_BLOCK_512B = (0x200 << 0),
|
MMC_TRANSFER_BLOCK_512B = (0x200 << 0),
|
||||||
|
|
||||||
/* Command register */
|
/* Command register */
|
||||||
|
@ -396,6 +400,9 @@ struct PACKED sdmmc_scr {
|
||||||
uint8_t scr_version : 4;
|
uint8_t scr_version : 4;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Callback function typedefs */
|
||||||
|
typedef int (*fault_handler_t)(struct mmc *mmc);
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
static int sdmmc_send_simple_command(struct mmc *mmc, enum sdmmc_command command,
|
static int sdmmc_send_simple_command(struct mmc *mmc, enum sdmmc_command command,
|
||||||
enum sdmmc_response_type response_type, uint32_t argument, void *response_buffer);
|
enum sdmmc_response_type response_type, uint32_t argument, void *response_buffer);
|
||||||
|
@ -407,8 +414,8 @@ static int sdmmc_loglevel = 0;
|
||||||
* Page-aligned bounce buffer to target with SDMMC DMA.
|
* Page-aligned bounce buffer to target with SDMMC DMA.
|
||||||
* If the size of this buffer is changed, the block_size
|
* If the size of this buffer is changed, the block_size
|
||||||
*/
|
*/
|
||||||
static uint8_t ALIGN(4096) sdmmc_bounce_buffer[4096 * 4];
|
static uint8_t ALIGN(4096) sdmmc_bounce_buffer[1024 * 8];
|
||||||
static const uint16_t sdmmc_bounce_dma_boundary = MMC_DMA_BOUNDARY_16K;
|
static const uint16_t sdmmc_bounce_dma_boundary = MMC_DMA_BOUNDARY_8K;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1081,6 +1088,31 @@ static int sdmmc_wait_until_no_longer_busy(struct mmc *mmc)
|
||||||
return sdmmc_wait_for_physical_state(mmc, MMC_DAT0_LINE_STATE, false);
|
return sdmmc_wait_for_physical_state(mmc, MMC_DAT0_LINE_STATE, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles an event in which the given SDMMC controller's DMA buffers have
|
||||||
|
* become full, and must be emptied again before they can be used.
|
||||||
|
*
|
||||||
|
* @param mmc The MMC controller that has suffered a full buffer.
|
||||||
|
*/
|
||||||
|
static int sdmmc_flush_bounce_buffer(struct mmc *mmc)
|
||||||
|
{
|
||||||
|
// Determine the total amount copied by subtracting the current pointer from
|
||||||
|
// its starting address-- effectively by figuring out how far we got in the bounce buffer.
|
||||||
|
uint32_t total_copied = mmc->regs->dma_address - (uint32_t)sdmmc_bounce_buffer;
|
||||||
|
|
||||||
|
// If we have a DMA buffer we're copying to, empty it out.
|
||||||
|
if (mmc->active_data_buffer) {
|
||||||
|
|
||||||
|
// Copy the data to the user buffer, and advance in the user buffer
|
||||||
|
// by the amount coppied.
|
||||||
|
memcpy((void *)mmc->active_data_buffer, sdmmc_bounce_buffer, total_copied);
|
||||||
|
mmc->active_data_buffer += total_copied;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the DMA to point at the beginning of our bounce buffer for another interation.
|
||||||
|
mmc->regs->dma_address = (uint32_t)sdmmc_bounce_buffer;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Blocks until the SD driver has completed issuing a command.
|
* Blocks until the SD driver has completed issuing a command.
|
||||||
|
@ -1089,23 +1121,43 @@ static int sdmmc_wait_until_no_longer_busy(struct mmc *mmc)
|
||||||
* @param target_irq A bitmask that specifies the bits that
|
* @param target_irq A bitmask that specifies the bits that
|
||||||
* will make this function return success
|
* will make this function return success
|
||||||
* @param fault_conditions A bitmask that specifies the bits that
|
* @param fault_conditions A bitmask that specifies the bits that
|
||||||
* will make this function return EFAULT.
|
* will make this function trigger its fault handler.
|
||||||
|
* @param fault_handler A function that's called to handle DMA faults.
|
||||||
|
* If it returns nonzero, this method will abort immediately; if it
|
||||||
|
* returns zero, it'll clear the error and continue.
|
||||||
*
|
*
|
||||||
* @return 0 on sucess, EFAULT if a fault condition occurs,
|
* @return 0 on sucess, EFAULT if a fault condition occurs,
|
||||||
* or an error code if a transfer failure occurs
|
* or an error code if a transfer failure occurs
|
||||||
*/
|
*/
|
||||||
static int sdmmc_wait_for_interrupt(struct mmc *mmc,
|
static int sdmmc_wait_for_interrupt(struct mmc *mmc,
|
||||||
uint32_t target_irq, uint32_t fault_conditions)
|
uint32_t target_irq, uint32_t fault_conditions, fault_handler_t fault_handler)
|
||||||
{
|
{
|
||||||
uint32_t timebase = get_time();
|
uint32_t timebase = get_time();
|
||||||
|
int rc;
|
||||||
|
|
||||||
// Wait until we either wind up ready, or until we've timed out.
|
// Wait until we either wind up ready, or until we've timed out.
|
||||||
while (true) {
|
while (true) {
|
||||||
if (get_time_since(timebase) > mmc->timeout)
|
if (get_time_since(timebase) > mmc->timeout)
|
||||||
return ETIMEDOUT;
|
return ETIMEDOUT;
|
||||||
|
|
||||||
if (mmc->regs->int_status & fault_conditions)
|
if (mmc->regs->int_status & fault_conditions) {
|
||||||
|
|
||||||
|
// If we don't have a handler, fault.
|
||||||
|
if (!fault_handler) {
|
||||||
|
mmc_print(mmc, "ERROR: unhandled DMA fault!\n");
|
||||||
return EFAULT;
|
return EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the DMA fault handler.
|
||||||
|
rc = fault_handler(mmc);
|
||||||
|
if (rc) {
|
||||||
|
mmc_print(mmc, "ERROR: unhandled DMA fault!\n (%d)", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, EOI the relevant interrupt.
|
||||||
|
mmc->regs->int_status |= fault_conditions;
|
||||||
|
}
|
||||||
|
|
||||||
if (mmc->regs->int_status & target_irq)
|
if (mmc->regs->int_status & target_irq)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1123,10 +1175,11 @@ static int sdmmc_wait_for_interrupt(struct mmc *mmc,
|
||||||
*/
|
*/
|
||||||
static int sdmmc_wait_for_command_completion(struct mmc *mmc)
|
static int sdmmc_wait_for_command_completion(struct mmc *mmc)
|
||||||
{
|
{
|
||||||
return sdmmc_wait_for_interrupt(mmc, MMC_STATUS_COMMAND_COMPLETE, 0);
|
return sdmmc_wait_for_interrupt(mmc, MMC_STATUS_COMMAND_COMPLETE, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Blocks until the SD driver has completed issuing a command.
|
* Blocks until the SD driver has completed issuing a command.
|
||||||
*
|
*
|
||||||
|
@ -1134,7 +1187,8 @@ static int sdmmc_wait_for_command_completion(struct mmc *mmc)
|
||||||
*/
|
*/
|
||||||
static int sdmmc_wait_for_transfer_completion(struct mmc *mmc)
|
static int sdmmc_wait_for_transfer_completion(struct mmc *mmc)
|
||||||
{
|
{
|
||||||
return sdmmc_wait_for_interrupt(mmc, MMC_STATUS_TRANSFER_COMPLETE, MMC_STATUS_DMA_INTERRUPT);
|
return sdmmc_wait_for_interrupt(mmc, MMC_STATUS_TRANSFER_COMPLETE,
|
||||||
|
MMC_STATUS_DMA_INTERRUPT, sdmmc_flush_bounce_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1441,16 +1495,13 @@ static int sdmmc_send_command(struct mmc *mmc, enum sdmmc_command command,
|
||||||
uint32_t total_data_to_xfer = sdmmc_get_block_size(mmc, is_write) * blocks_to_transfer;
|
uint32_t total_data_to_xfer = sdmmc_get_block_size(mmc, is_write) * blocks_to_transfer;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
// If this transfer would have us send more than we can, fail out.
|
// Store user data buffer for use by future DMA operations.
|
||||||
if (total_data_to_xfer > sizeof(sdmmc_bounce_buffer)) {
|
mmc->active_data_buffer = (uint32_t)data_buffer;
|
||||||
mmc_print(mmc, "ERROR: transfer is larger than our maximum DMA transfer size!");
|
|
||||||
return -E2BIG;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity check: if this is a data transfer, make sure we have a data buffer...
|
// Sanity check: if this is a data transfer, make sure we have a data buffer...
|
||||||
if (blocks_to_transfer && !data_buffer) {
|
if (blocks_to_transfer && !data_buffer) {
|
||||||
mmc_print(mmc, "ERROR: no data buffer provided, but this is a data transfer!");
|
mmc_print(mmc, "WARNING: no data buffer provided, but this is a data transfer!");
|
||||||
return -EINVAL;
|
mmc_print(mmc, "this does nothing; but is supported for debug");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait until we can issue commands to the device.
|
// Wait until we can issue commands to the device.
|
||||||
|
@ -1474,9 +1525,8 @@ static int sdmmc_send_command(struct mmc *mmc, enum sdmmc_command command,
|
||||||
|
|
||||||
// If this is a write and we have data, we'll need to populate the bounce buffer before
|
// If this is a write and we have data, we'll need to populate the bounce buffer before
|
||||||
// issuing the command.
|
// issuing the command.
|
||||||
if (blocks_to_transfer && is_write && mmc->use_dma) {
|
if (blocks_to_transfer && is_write && mmc->use_dma && data_buffer)
|
||||||
memcpy(sdmmc_bounce_buffer, data_buffer, total_data_to_xfer);
|
memcpy(sdmmc_bounce_buffer, (void *)mmc->active_data_buffer, total_data_to_xfer);
|
||||||
}
|
|
||||||
|
|
||||||
// Configure the controller to send the command.
|
// Configure the controller to send the command.
|
||||||
sdmmc_prepare_command_registers(mmc, blocks_to_transfer, command, response_type, checks);
|
sdmmc_prepare_command_registers(mmc, blocks_to_transfer, command, response_type, checks);
|
||||||
|
@ -1517,9 +1567,8 @@ static int sdmmc_send_command(struct mmc *mmc, enum sdmmc_command command,
|
||||||
|
|
||||||
// If this is a read, and we've just finished a transfer, copy the data from
|
// If this is a read, and we've just finished a transfer, copy the data from
|
||||||
// our bounce buffer to the target data buffer.
|
// our bounce buffer to the target data buffer.
|
||||||
if (!is_write) {
|
if (!is_write && data_buffer)
|
||||||
memcpy(data_buffer, sdmmc_bounce_buffer, total_data_to_xfer);
|
sdmmc_flush_bounce_buffer(mmc);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Otherwise, perform the transfer using the CPU.
|
// Otherwise, perform the transfer using the CPU.
|
||||||
else {
|
else {
|
||||||
|
@ -1913,7 +1962,7 @@ static int sdmmc_optimize_transfer_mode(struct mmc *mmc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: step up into high speed modes
|
// TODO: step up into high speed modes
|
||||||
mmc_print(mmc, "now operating with a wider bus width");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1950,9 +1999,6 @@ static int sdmmc_get_relative_address(struct mmc *mmc)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
uint32_t response;
|
uint32_t response;
|
||||||
//uint32_t timebase = get_time();
|
|
||||||
|
|
||||||
// TODO: do we need to repeatedly retry this? other codebases do
|
|
||||||
|
|
||||||
// Set up the card's relative address.
|
// Set up the card's relative address.
|
||||||
rc = sdmmc_send_simple_command(mmc, CMD_GET_RELATIVE_ADDR, MMC_RESPONSE_LEN48, 0, &response);
|
rc = sdmmc_send_simple_command(mmc, CMD_GET_RELATIVE_ADDR, MMC_RESPONSE_LEN48, 0, &response);
|
||||||
|
|
|
@ -171,6 +171,9 @@ struct mmc {
|
||||||
uint8_t write_block_order;
|
uint8_t write_block_order;
|
||||||
bool uses_block_addressing;
|
bool uses_block_addressing;
|
||||||
|
|
||||||
|
/* Current operation status flags */
|
||||||
|
uint32_t active_data_buffer;
|
||||||
|
|
||||||
/* Pointers to hardware structures */
|
/* Pointers to hardware structures */
|
||||||
volatile struct tegra_sdmmc *regs;
|
volatile struct tegra_sdmmc *regs;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue