mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-06-03 08:08:39 -04:00
Fusee: Deployed new SDMMC driver in fusee-secondary. All stages boot now.
Fusee: Fixed wrong argument in se.c function. Fusee: Improved timers.
This commit is contained in:
parent
49ba91a8f3
commit
3db9ce32fa
44 changed files with 5247 additions and 4802 deletions
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include "gpio.h"
|
||||
#include "utils.h"
|
||||
#include "lib/printk.h"
|
||||
|
||||
/**
|
||||
* Returns a GPIO bank object that corresponds to the given GPIO pin,
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "hwinit.h"
|
||||
#include "fuse.h"
|
||||
#include "se.h"
|
||||
#include "timers.h"
|
||||
#include "fs_utils.h"
|
||||
#include "stage2.h"
|
||||
#include "chainloader.h"
|
||||
|
@ -122,14 +123,7 @@ int main(void) {
|
|||
/* Say hello. */
|
||||
printk("Welcome to Atmosph\xe8re Fus\xe9" "e!\n");
|
||||
printk("Using color linear framebuffer at 0x%p!\n", g_framebuffer);
|
||||
|
||||
#ifndef I_KNOW_WHAT_I_AM_DOING
|
||||
#error "Fusee is a work-in-progress bootloader, and is not ready for usage yet. If you want to play with it anyway, please #define I_KNOW_WHAT_I_AM_DOING -- and recognize that we will be unable to provide support until it is ready for general usage :)"
|
||||
|
||||
printk("Warning: Fus\xe9" "e is not yet completed, and not ready for general testing!\n");
|
||||
fatal_error("Please do not seek support for it until it is done.\n");
|
||||
#endif
|
||||
|
||||
|
||||
/* Load the BCT0 configuration ini off of the SD. */
|
||||
bct0 = load_config();
|
||||
|
||||
|
@ -142,10 +136,12 @@ int main(void) {
|
|||
stage2_args = (stage2_args_t *)(g_chainloader_arg_data + strlen(stage2_path) + 1); /* May be unaligned. */
|
||||
memcpy(&stage2_args->version, &stage2_version, 4);
|
||||
stage2_args->display_initialized = false;
|
||||
memcpy(&stage2_args->sd_sdmmc, &g_sd_sdmmc, sizeof(g_sd_sdmmc));
|
||||
strcpy(stage2_args->bct0, bct0);
|
||||
g_chainloader_argc = 2;
|
||||
|
||||
/* Wait a while. */
|
||||
mdelay(1000);
|
||||
|
||||
/* Deinitialize the display, console, etc. */
|
||||
cleanup_env();
|
||||
|
||||
|
|
|
@ -189,7 +189,7 @@ static int sdmmc_device_rw(sdmmc_device_t *device, uint32_t sector, uint32_t num
|
|||
sdmmc_device_send_status(device);
|
||||
|
||||
/* Wait for a while. */
|
||||
udelay(100000);
|
||||
mdelay(100);
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
@ -504,8 +504,8 @@ static int sdmmc_sd_send_op_cond(sdmmc_device_t *device, bool is_sd_ver2, bool i
|
|||
/* Keep checking if timeout expired. */
|
||||
is_timeout = (get_time_since(timebase) > 2000000);
|
||||
|
||||
/* Delay for an appropriate period. */
|
||||
udelay(10000);
|
||||
/* Delay for a minimum of 10 milliseconds. */
|
||||
mdelay(10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1161,8 +1161,8 @@ static int sdmmc_mmc_send_op_cond(sdmmc_device_t *device, SdmmcBusVoltage bus_vo
|
|||
/* Keep checking if timeout expired. */
|
||||
is_timeout = (get_time_since(timebase) > 2000000);
|
||||
|
||||
/* Delay for an appropriate period. */
|
||||
udelay(10000);
|
||||
/* Delay for a minimum of 10 milliseconds. */
|
||||
mdelay(10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1357,11 +1357,23 @@ static int sdmmc_mmc_select_bkops(sdmmc_device_t *device)
|
|||
return sdmmc_device_send_status(device);
|
||||
}
|
||||
|
||||
int sdmmc_mmc_select_partition(sdmmc_device_t *device, SdmmcPartitionNum partition)
|
||||
{
|
||||
uint32_t arg = (((MMC_SWITCH_MODE_WRITE_BYTE) << 24) | ((EXT_CSD_PART_CONFIG) << 16) | ((partition) << 8));
|
||||
|
||||
/* Try to change the active partition. */
|
||||
if (!sdmmc_mmc_switch(device, arg))
|
||||
return 0;
|
||||
|
||||
/* Peek the current status. */
|
||||
return sdmmc_device_send_status(device);
|
||||
}
|
||||
|
||||
int sdmmc_device_mmc_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth bus_width, SdmmcBusSpeed bus_speed)
|
||||
{
|
||||
uint32_t cid[4] = {0};
|
||||
uint32_t csd[4] = {0};
|
||||
uint8_t ext_csd[512] = {0};
|
||||
uint8_t *ext_csd = (uint8_t *)SDMMC_BOUNCE_BUFFER_ADDRESS; // TODO: Better way to do this.
|
||||
|
||||
/* Initialize our device's struct. */
|
||||
memset(device, 0, sizeof(sdmmc_device_t));
|
||||
|
@ -1376,6 +1388,9 @@ int sdmmc_device_mmc_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth
|
|||
/* Bind the underlying driver. */
|
||||
device->sdmmc = sdmmc;
|
||||
|
||||
/* Set RCA. */
|
||||
device->rca = 0x01;
|
||||
|
||||
sdmmc_info(sdmmc, "SDMMC driver was successfully initialized for eMMC!");
|
||||
|
||||
/* Apply at least 74 clock cycles. eMMC should be ready afterwards. */
|
||||
|
@ -1408,14 +1423,14 @@ int sdmmc_device_mmc_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth
|
|||
|
||||
sdmmc_info(sdmmc, "Got CID from eMMC!");
|
||||
|
||||
/* Get the eMMC's RCA. */
|
||||
/* Set the eMMC's RCA. */
|
||||
if (!sdmmc_mmc_set_relative_addr(device))
|
||||
{
|
||||
sdmmc_error(sdmmc, "Failed to get RCA!");
|
||||
sdmmc_error(sdmmc, "Failed to set RCA!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sdmmc_info(sdmmc, "Got RCA (0x%08x) from eMMC!", device->rca);
|
||||
sdmmc_info(sdmmc, "RCA is now set in eMMC!");
|
||||
|
||||
/* Get the eMMC card's CSD. */
|
||||
if (!sdmmc_device_send_csd(device, csd))
|
||||
|
@ -1484,7 +1499,7 @@ int sdmmc_device_mmc_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth
|
|||
|
||||
/* Decode and save the CID. */
|
||||
sdmmc_mmc_decode_cid(device, cid);
|
||||
|
||||
|
||||
/* TODO: Handle automatic BKOPS properly. Leave it disabled for now. */
|
||||
if (false && device->ext_csd.bkops && !(device->ext_csd.auto_bkops_en & EXT_CSD_AUTO_BKOPS_MASK))
|
||||
{
|
||||
|
@ -1507,4 +1522,4 @@ int sdmmc_device_mmc_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth
|
|||
sdmmc_adjust_sd_clock(sdmmc);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -156,5 +156,6 @@ int sdmmc_device_mmc_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth
|
|||
int sdmmc_device_read(sdmmc_device_t *device, uint32_t sector, uint32_t num_sectors, void *data);
|
||||
int sdmmc_device_write(sdmmc_device_t *device, uint32_t sector, uint32_t num_sectors, void *data);
|
||||
int sdmmc_device_finish(sdmmc_device_t *device);
|
||||
int sdmmc_mmc_select_partition(sdmmc_device_t *device, SdmmcPartitionNum partition);
|
||||
|
||||
#endif
|
|
@ -14,8 +14,6 @@
|
|||
#include "../lib/driver_utils.h"
|
||||
#include "../hwinit/max7762x.h"
|
||||
|
||||
#define SDMMC_BOUNCE_BUFFER_ADDRESS 0x90000000
|
||||
|
||||
static SdmmcLogLevel g_sdmmc_log_level = SDMMC_LOG_NONE;
|
||||
|
||||
void sdmmc_set_log_level(SdmmcLogLevel log_level)
|
||||
|
@ -116,7 +114,7 @@ void sdmmc_dump_regs(sdmmc_t *sdmmc)
|
|||
sdmmc_debug(sdmmc, "max_current: 0x%08" PRIX32, sdmmc->regs->max_current);
|
||||
sdmmc_debug(sdmmc, "set_acmd12_error: 0x%04" PRIX16, sdmmc->regs->set_acmd12_error);
|
||||
sdmmc_debug(sdmmc, "set_int_error: 0x%04" PRIX16, sdmmc->regs->set_int_error);
|
||||
sdmmc_debug(sdmmc, "adma_error: 0x%02" PRIX16, sdmmc->regs->adma_error);
|
||||
sdmmc_debug(sdmmc, "adma_error: 0x%02" PRIX8, sdmmc->regs->adma_error);
|
||||
sdmmc_debug(sdmmc, "adma_address: 0x%08" PRIX32, sdmmc->regs->adma_address);
|
||||
sdmmc_debug(sdmmc, "upper_adma_address: 0x%08" PRIX32, sdmmc->regs->upper_adma_address);
|
||||
sdmmc_debug(sdmmc, "preset_for_init: 0x%04" PRIX16, sdmmc->regs->preset_for_init);
|
||||
|
@ -700,10 +698,13 @@ static int sdmmc_int_clk_enable(sdmmc_t *sdmmc)
|
|||
|
||||
/* Change to ADMA if requested. */
|
||||
if (sdmmc->use_adma && (sdmmc->regs->capabilities & SDHCI_CAN_DO_ADMA2)) {
|
||||
// TODO: Setting the ADMA flags breaks ADMA...
|
||||
/*
|
||||
if (sdmmc->regs->capabilities & SDHCI_CAN_64BIT)
|
||||
sdmmc->regs->host_control |= SDHCI_CTRL_ADMA64;
|
||||
else
|
||||
sdmmc->regs->host_control |= SDHCI_CTRL_ADMA32;
|
||||
*/
|
||||
}
|
||||
|
||||
/* Set the timeout to be the maximum value. */
|
||||
|
@ -1092,7 +1093,7 @@ static int sdmmc_init_controller(sdmmc_t *sdmmc, SdmmcControllerNum controller)
|
|||
sdmmc->is_clk_running = false;
|
||||
sdmmc->is_sd_clk_enabled = false;
|
||||
sdmmc->is_tuning_tap_val_set = false;
|
||||
sdmmc->use_adma = false;
|
||||
sdmmc->use_adma = true;
|
||||
sdmmc->dma_bounce_buf = (uint8_t*)SDMMC_BOUNCE_BUFFER_ADDRESS;
|
||||
sdmmc->tap_val = 0;
|
||||
sdmmc->internal_divider = 0;
|
||||
|
@ -1201,13 +1202,23 @@ void sdmmc_finish(sdmmc_t *sdmmc)
|
|||
/* Disable the SD clock. */
|
||||
sdmmc_disable_sd_clock(sdmmc);
|
||||
|
||||
/* Disable SD power. */
|
||||
/* Disable SDMMC power. */
|
||||
sdmmc_select_voltage(sdmmc, SDMMC_VOLTAGE_NONE);
|
||||
|
||||
/* Disable the SD card power. */
|
||||
if (sdmmc->controller == SDMMC_1)
|
||||
{
|
||||
/* Disable GPIO output. */
|
||||
gpio_configure_direction(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_DIRECTION_INPUT);
|
||||
|
||||
/* Power cycle for 100ms without power. */
|
||||
mdelay(100);
|
||||
}
|
||||
|
||||
/* Force a register read to refresh the clock control value. */
|
||||
sdmmc_get_sd_clock_control(sdmmc);
|
||||
|
||||
/* Stop the SD clock. */
|
||||
/* Stop the SDMMC clock. */
|
||||
sdmmc_clk_stop(sdmmc->controller);
|
||||
|
||||
/* Clock is no longer running by now. */
|
||||
|
@ -1307,7 +1318,8 @@ 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 | (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT));
|
||||
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;
|
||||
|
@ -1315,11 +1327,15 @@ static void sdmmc_intr_enable(sdmmc_t *sdmmc)
|
|||
|
||||
static void sdmmc_intr_disable(sdmmc_t *sdmmc)
|
||||
{
|
||||
/* Clear the interrupt bits. */
|
||||
sdmmc->regs->int_enable &= ~(0x017F0000 | (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT));
|
||||
/* Clear all error bits and the 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, u16 status_mask)
|
||||
static bool sdmmc_intr_check_status(sdmmc_t *sdmmc, uint16_t status_mask)
|
||||
{
|
||||
bool is_masked = (sdmmc->regs->int_status & status_mask);
|
||||
|
||||
|
@ -1353,8 +1369,8 @@ static int sdmmc_dma_init(sdmmc_t *sdmmc, sdmmc_request_t *req)
|
|||
if (blkcnt >= 0xFFFF)
|
||||
blkcnt = 0xFFFF;
|
||||
|
||||
/* Point to our bounce buffer. */
|
||||
uint32_t dma_base_addr = (uint32_t)sdmmc->dma_bounce_buf;
|
||||
/* Use our bounce buffer for SDMA or the request data buffer for ADMA. */
|
||||
uint32_t dma_base_addr = sdmmc->use_adma ? (uint32_t)req->data : (uint32_t)sdmmc->dma_bounce_buf;
|
||||
|
||||
/* DMA buffer address must be aligned to 4 bytes. */
|
||||
if ((4 - (dma_base_addr & 0x03)) & 0x03)
|
||||
|
@ -1408,7 +1424,7 @@ static int sdmmc_dma_init(sdmmc_t *sdmmc, sdmmc_request_t *req)
|
|||
|
||||
static int sdmmc_dma_update(sdmmc_t *sdmmc)
|
||||
{
|
||||
u16 blkcnt = 0;
|
||||
uint16_t blkcnt = 0;
|
||||
|
||||
/* Loop until all blocks have been consumed. */
|
||||
do
|
||||
|
@ -1464,7 +1480,7 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc)
|
|||
|
||||
static void sdmmc_set_cmd_flags(sdmmc_t *sdmmc, sdmmc_command_t *cmd, bool is_dma)
|
||||
{
|
||||
u16 cmd_reg_flags = 0;
|
||||
uint16_t cmd_reg_flags = 0;
|
||||
|
||||
/* Select length flags based on response type. */
|
||||
if (!(cmd->flags & SDMMC_RSP_PRESENT))
|
||||
|
@ -1629,8 +1645,8 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* If this is a write operation, copy the data into our bounce buffer. */
|
||||
if (!req->is_read)
|
||||
/* If this is a SDMA write operation, copy the data into our bounce buffer. */
|
||||
if (!sdmmc->use_adma && !req->is_read)
|
||||
memcpy((void *)sdmmc->dma_bounce_buf, (void *)req->data, req->blksz * req->num_blocks);
|
||||
}
|
||||
|
||||
|
@ -1659,8 +1675,8 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* If this is a read operation, copy the data from our bounce buffer. */
|
||||
if (req->is_read)
|
||||
/* If this is a SDMA read operation, copy the data from our bounce buffer. */
|
||||
if (!sdmmc->use_adma && req->is_read)
|
||||
{
|
||||
uint32_t dma_data_size = (sdmmc->regs->dma_address - (uint32_t)sdmmc->dma_bounce_buf);
|
||||
memcpy((void *)req->data, (void *)sdmmc->dma_bounce_buf, dma_data_size);
|
||||
|
@ -1853,7 +1869,7 @@ static int sdmmc_send_tuning(sdmmc_t *sdmmc, uint32_t opcode)
|
|||
|
||||
void sdmmc_set_tuning_tap_val(sdmmc_t *sdmmc)
|
||||
{
|
||||
sdmmc->tap_val = ((sdmmc->regs->vendor_clock_cntrl & 0xFF0000) >> 16);
|
||||
sdmmc->tap_val = (sdmmc->regs->vendor_clock_cntrl >> 16);
|
||||
sdmmc->is_tuning_tap_val_set = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
|
||||
#include "sdmmc_tegra.h"
|
||||
|
||||
/* Bounce buffer */
|
||||
#define SDMMC_BOUNCE_BUFFER_ADDRESS 0x90000000
|
||||
|
||||
/* Present state */
|
||||
#define SDHCI_CMD_INHIBIT 0x00000001
|
||||
#define SDHCI_DATA_INHIBIT 0x00000002
|
||||
|
@ -177,6 +180,14 @@ typedef enum {
|
|||
SDMMC_4 = 3
|
||||
} SdmmcControllerNum;
|
||||
|
||||
typedef enum {
|
||||
SDMMC_PARTITION_INVALID = -1,
|
||||
SDMMC_PARTITION_USER = 0,
|
||||
SDMMC_PARTITION_BOOT0 = 1,
|
||||
SDMMC_PARTITION_BOOT1 = 2,
|
||||
SDMMC_PARTITION_RPMB = 3
|
||||
} SdmmcPartitionNum;
|
||||
|
||||
typedef enum {
|
||||
SDMMC_VOLTAGE_NONE = 0,
|
||||
SDMMC_VOLTAGE_1V8 = 1,
|
||||
|
|
|
@ -21,7 +21,6 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
uint32_t version;
|
||||
sdmmc_t sd_sdmmc;
|
||||
bool display_initialized;
|
||||
char bct0[BCTO_MAX_SIZE];
|
||||
} stage2_args_t;
|
||||
|
|
|
@ -7,6 +7,11 @@
|
|||
#define MAKE_TIMERS_REG(n) MAKE_REG32(TIMERS_BASE + n)
|
||||
#define TIMERUS_CNTR_1US_0 MAKE_REG32(TIMERS_BASE + 0x10)
|
||||
|
||||
#define RTC_BASE 0x7000E000
|
||||
#define RTC_SECONDS MAKE_REG32(RTC_BASE + 0x08)
|
||||
#define RTC_SHADOW_SECONDS MAKE_REG32(RTC_BASE + 0x0C)
|
||||
#define RTC_MILLI_SECONDS MAKE_REG32(RTC_BASE + 0x10)
|
||||
|
||||
typedef struct {
|
||||
uint32_t CONFIG;
|
||||
uint32_t STATUS;
|
||||
|
@ -20,23 +25,46 @@ typedef struct {
|
|||
|
||||
void wait(uint32_t microseconds);
|
||||
|
||||
static inline uint32_t get_time(void) {
|
||||
static inline uint32_t get_time_s(void) {
|
||||
return RTC_SECONDS;
|
||||
}
|
||||
|
||||
static inline uint32_t get_time_ms(void) {
|
||||
return (RTC_MILLI_SECONDS | (RTC_SHADOW_SECONDS << 10));
|
||||
}
|
||||
|
||||
static inline uint32_t get_time_us(void) {
|
||||
return TIMERUS_CNTR_1US_0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time in microseconds.
|
||||
*/
|
||||
static inline uint32_t get_time(void) {
|
||||
return get_time_us();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of microseconds that have passed since a given get_time().
|
||||
*/
|
||||
static inline uint32_t get_time_since(uint32_t base) {
|
||||
return get_time() - base;
|
||||
return get_time_us() - base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delays for a given number of microseconds.
|
||||
*/
|
||||
static inline void udelay(unsigned usecs) {
|
||||
uint32_t start = get_time();
|
||||
while (get_time() - start < usecs);
|
||||
static inline void udelay(uint32_t usecs) {
|
||||
uint32_t start = get_time_us();
|
||||
while (get_time_us() - start < usecs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delays for a given number of milliseconds.
|
||||
*/
|
||||
static inline void mdelay(uint32_t msecs) {
|
||||
uint32_t start = get_time_ms();
|
||||
while (get_time_ms() - start < msecs);
|
||||
}
|
||||
|
||||
__attribute__ ((noreturn)) void watchdog_reboot(void);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue