From 06bf1320221c07fcab457c0bba6b66862e900f58 Mon Sep 17 00:00:00 2001 From: "Kate J. Temkin" Date: Sat, 21 Apr 2018 19:38:55 -0600 Subject: [PATCH] skeleton SDMMC --- fusee/fusee-primary/src/lib/printk.c | 12 +- fusee/fusee-primary/src/lib/printk.h | 7 + fusee/fusee-primary/src/lib/vsprintf.h | 3 + fusee/fusee-primary/src/sdmmc.c | 720 ++++++++++++++++++++++++- fusee/fusee-primary/src/sdmmc.h | 154 ++---- fusee/fusee-primary/src/timers.h | 16 + 6 files changed, 779 insertions(+), 133 deletions(-) diff --git a/fusee/fusee-primary/src/lib/printk.c b/fusee/fusee-primary/src/lib/printk.c index 84925fce1..8e990757b 100644 --- a/fusee/fusee-primary/src/lib/printk.c +++ b/fusee/fusee-primary/src/lib/printk.c @@ -16,9 +16,15 @@ void printk(char *fmt, ...) { va_list list; - char buf[512]; va_start(list, fmt); - vsnprintf(buf, sizeof(buf), fmt, list); - video_puts(buf); + vprintk(fmt, list); va_end(list); } + + +void vprintk(char *fmt, va_list args) +{ + char buf[512]; + vsnprintf(buf, sizeof(buf), fmt, args); + video_puts(buf); +} diff --git a/fusee/fusee-primary/src/lib/printk.h b/fusee/fusee-primary/src/lib/printk.h index 006985a52..aaad8f1b4 100644 --- a/fusee/fusee-primary/src/lib/printk.h +++ b/fusee/fusee-primary/src/lib/printk.h @@ -1,2 +1,9 @@ +#ifndef __PRINTK_H__ +#define __PRINTK_H__ + +#include void printk(char *fmt, ...); +void vprintk(char *fmt, va_list args); + +#endif diff --git a/fusee/fusee-primary/src/lib/vsprintf.h b/fusee/fusee-primary/src/lib/vsprintf.h index 9399969bd..7b7fea7aa 100644 --- a/fusee/fusee-primary/src/lib/vsprintf.h +++ b/fusee/fusee-primary/src/lib/vsprintf.h @@ -19,6 +19,9 @@ struct va_format { unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base); +int sprintf(char *buf, const char *fmt, ...); +int scnprintf(char *buf, size_t size, const char *fmt, ...); +int snprintf(char *buf, size_t size, const char *fmt, ...); int vsnprintf(char *buf, size_t size, const char *fmt, va_list args); int sscanf(const char *buf, const char *fmt, ...); diff --git a/fusee/fusee-primary/src/sdmmc.c b/fusee/fusee-primary/src/sdmmc.c index 3a46d91cb..91d7fe1f7 100644 --- a/fusee/fusee-primary/src/sdmmc.c +++ b/fusee/fusee-primary/src/sdmmc.c @@ -1,27 +1,721 @@ #include +#include +#include #include "sdmmc.h" +#include "car.h" +#include "timers.h" +#include "apb_misc.h" +#include "lib/printk.h" -/* Initialize the SDMMC1 (SD card) controller */ -void sdmmc1_init(void) +#define TEGRA_SDMMC_BASE (0x700B0000) +#define TEGRA_SDMMC_SIZE (0x200) + +/** + * Map of tegra SDMMC registers + */ +struct PACKED tegra_sdmmc { + + /* SDHCI standard registers */ + uint32_t dma_address; + uint16_t block_size; + uint16_t block_count; + uint32_t argument; + uint16_t transfer_mode; + uint16_t command; + uint16_t response[0x8]; + uint32_t buffer; + uint32_t present_state; + uint8_t host_control; + uint8_t power_control; + uint8_t block_gap_control; + uint8_t wake_up_control; + uint16_t clock_control; + uint8_t timeout_control; + uint8_t software_reset; + uint32_t int_status; + uint32_t int_enable; + uint32_t signal_enable; + uint16_t acmd12_err; + uint16_t host_control2; + uint32_t capabilities; + uint32_t capabilities_1; + uint32_t max_current; + uint32_t _0x4c; + uint16_t set_acmd12_error; + uint16_t set_int_error; + uint16_t adma_error; + uint8_t _0x55[0x3]; + uint32_t adma_address; + uint32_t upper_adma_address; + uint16_t preset_for_init; + uint16_t preset_for_default; + uint16_t preset_for_high; + uint16_t preset_for_sdr12; + uint16_t preset_for_sdr25; + uint16_t preset_for_sdr50; + uint16_t preset_for_sdr104; + uint16_t preset_for_ddr50; + uint8_t _0x70[0x3]; + uint32_t _0x74[0x22]; + uint16_t slot_int_status; + uint16_t host_version; + + /* vendor specific registers */ + uint32_t vendor_clock_cntrl; + uint32_t vendor_sys_sw_cntrl; + uint32_t vendor_err_intr_status; + uint32_t vendor_cap_overrides; + uint32_t vendor_boot_cntrl; + uint32_t vendor_boot_ack_timeout; + uint32_t vendor_boot_dat_timeout; + uint32_t vendor_debounce_count; + uint32_t vendor_misc_cntrl; + uint32_t max_current_override; + uint32_t max_current_override_hi; + uint32_t _0x12c[0x21]; + uint32_t vendor_io_trim_cntrl; + + /* start of sdmmc2/sdmmc4 only */ + uint32_t vendor_dllcal_cfg; + uint32_t vendor_dll_ctrl0; + uint32_t vendor_dll_ctrl1; + uint32_t vendor_dllcal_cfg_sta; + /* end of sdmmc2/sdmmc4 only */ + + uint32_t vendor_tuning_cntrl0; + uint32_t vendor_tuning_cntrl1; + uint32_t vendor_tuning_status0; + uint32_t vendor_tuning_status1; + uint32_t vendor_clk_gate_hysteresis_count; + uint32_t vendor_preset_val0; + uint32_t vendor_preset_val1; + uint32_t vendor_preset_val2; + uint32_t sdmemcomppadctrl; + uint32_t auto_cal_config; + uint32_t auto_cal_interval; + uint32_t auto_cal_status; + uint32_t io_spare; + uint32_t sdmmca_mccif_fifoctrl; + uint32_t timeout_wcoal_sdmmca; + uint32_t _0x1fc; +}; + + +/** + * SDMMC response lengths + */ +enum sdmmc_response_type { + MMC_RESPONSE_NONE = 0, + MMC_RESPONSE_LEN136 = 1, + MMC_RESPONSE_LEN48 = 2, + MMC_RESPONSE_LEN48_CHK_BUSY = 3, +}; + +/** + * General masks for SDMMC registers. + */ +enum sdmmc_register_bits { + + /* Present state register */ + MMC_COMMAND_INHIBIT = 1 << 0, + + /* Command register */ + MMC_COMMAND_NUMBER_SHIFT = 8, + MMC_COMMAND_RESPONSE_TYPE_SHIFT = 0, + MMC_COMMAND_HAS_DATA = 1 << 5, + MMC_COMMAND_TYPE_ABORT = 3 << 6, + MMC_COMMAND_CHECK_NUMBER = 1 << 4, + + /* Transfer mode arguments */ + MMC_TRANSFER_DMA_ENABLE = (1 << 0), + MMC_TRANSFER_LIMIT_BLOCK_COUNT = (1 << 1), + MMC_TRANSFER_MULTIPLE_BLOCKS = (1 << 5), + MMC_TRANSFER_HOST_TO_CARD = (1 << 4), + + /* Interrupt status */ + MMC_STATUS_COMMAND_COMPLETE = (1 << 0), + MMC_STATUS_TRANSFER_COMPLETE = (1 << 1), + MMC_STATUS_ERROR_MASK = (0xF << 16), + + /* Host control */ + MMC_DMA_SELECT_MASK = (0x3 << 3), +}; + + +/** + * SDMMC commands + */ +enum sdmmc_command { + CMD_GO_IDLE_OR_INIT = 0, + CMD_SEND_OPERATING_CONDITIONS = 1, + CMD_ALL_SEND_CID = 2, + CMD_SET_RELATIVE_ADDR = 3, + CMD_SET_DSR = 4, + CMD_TOGGLE_SLEEP_AWAKE = 5, + CMD_SWITCH_MODE = 6, + CMD_TOGGLE_CARD_SELECT = 7, + CMD_SEND_EXT_CSD = 8, + CMD_SEND_CSD = 9, + CMD_SEND_CID = 10, + CMD_STOP_TRANSMISSION = 12, + CMD_READ_STATUS = 13, + CMD_BUS_TEST = 14, + CMD_GO_INACTIVE = 15, + CMD_SET_BLKLEN = 16, + CMD_READ_SINGLE_BLOCK = 17, + CMD_READ_MULTIPLE_BLOCK = 18, +}; + + +/** + * Page-aligned bounce buffer to target with SDMMC DMA. + */ +static uint8_t ALIGN(4096) sdmmc_bounce_buffer[4096 * 4]; + +/** + * Debug print for SDMMC information. + */ +void mmc_print(struct mmc *mmc, char *fmt, ...) { - /* TODO */ + va_list list; + + // TODO: check SDMMC log level before printing + + va_start(list, fmt); + printk("%s: ", mmc->name); + vprintk(fmt, list); + printk("\n"); + va_end(list); } -/* Initialize the SDMMC2 (GC asic) controller */ -void sdmmc2_init(void) + +/** + * Retreives the SDMMC register range for the given controller. + */ +static struct tegra_sdmmc *sdmmc_get_regs(enum sdmmc_controller controller) { - /* TODO */ + // Start with the base addresss of the SDMMC_BLOCK + uintptr_t addr = TEGRA_SDMMC_BASE; + + // Offset our address by the controller number. + addr += (controller * TEGRA_SDMMC_SIZE); + + // Return the controller. + return (struct tegra_sdmmc *)addr; } -/* Initialize the SDMMC3 (unused) controller */ -void sdmmc3_init(void) + +/** + * + */ +static int sdmmc_hardware_init(struct mmc *mmc) { - /* TODO */ + volatile struct tegra_car *car = car_get_regs(); + volatile struct tegra_sdmmc *regs = mmc->regs; + + uint32_t timebase; + bool is_timeout; + + /* XXX fixme XXX */ + bool is_hs400_hs667 = false; + + mmc_print(mmc, "initializing in %s-speed mode...", is_hs400_hs667 ? "high" : "low"); + + + // FIXME: set up clock and reset to fetch the relevant clock register offsets + + // Put SDMMC4 in reset + car->rst_dev_l_set |= 0x8000; + + // Set SDMMC4 clock source (PLLP_OUT0) and divisor (1) + car->clk_src[CLK_SOURCE_SDMMC4] = CLK_SOURCE_FIRST | CLK_DIVIDER_UNITY; + + // Set SDMMC4 clock enable + car->clk_dev_l_set |= 0x8000; + + // host_clk_delay(0x64, clk_freq) -> Delay 100 host clock cycles + udelay(5000); + + // Take SDMMC4 out of reset + car->rst_dev_l_clr |= 0x8000; + + // Set IO_SPARE[19] (one cycle delay) + regs->io_spare |= 0x80000; + + // Clear SEL_VREG + regs->vendor_io_trim_cntrl &= ~(0x04); + + // Set trimmer value to 0x08 (SDMMC4) + regs->vendor_clock_cntrl &= ~(0x1F000000); + regs->vendor_clock_cntrl |= 0x08000000; + + // Set SDMMC2TMC_CFG_SDMEMCOMP_VREF_SEL to 0x07 + regs->sdmemcomppadctrl &= ~(0x0F); + regs->sdmemcomppadctrl |= 0x07; + + // Set auto-calibration PD/PU offsets + regs->auto_cal_config = ((regs->auto_cal_config & ~(0x7F)) | 0x05); + regs->auto_cal_config = ((regs->auto_cal_config & ~(0x7F00)) | 0x05); + + // Set PAD_E_INPUT_OR_E_PWRD (relevant for eMMC only) + regs->sdmemcomppadctrl |= 0x80000000; + + // Wait one milisecond + udelay(1000); + + // Set AUTO_CAL_START and AUTO_CAL_ENABLE + regs->auto_cal_config |= 0xA0000000; + + // Wait one second + udelay(1); + + // Program a timeout of 10ms + is_timeout = false; + timebase = get_time(); + + // Wait for AUTO_CAL_ACTIVE to be cleared + mmc_print(mmc, "initialing autocal..."); + while((regs->auto_cal_status & 0x80000000) && !is_timeout) { + // Keep checking if timeout expired + is_timeout = get_time_since(timebase) > 10000; + } + + // AUTO_CAL_ACTIVE was not cleared in time + if (is_timeout) + { + mmc_print(mmc, "autocal timed out!"); + + // Set CFG2TMC_EMMC4_PAD_DRVUP_COMP and CFG2TMC_EMMC4_PAD_DRVDN_COMP + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 = ((APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 & ~(0x3F00)) | 0x1000); + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 = ((APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 & ~(0xFC)) | 0x40); + + // Clear AUTO_CAL_ENABLE + regs->auto_cal_config &= ~(0x20000000); + } + + mmc_print(mmc, "autocal complete."); + + // Clear PAD_E_INPUT_OR_E_PWRD (relevant for eMMC only) + regs->sdmemcomppadctrl &= ~(0x80000000); + + // Set SDHCI_CLOCK_INT_EN + regs->clock_control |= 0x01; + + // Program a timeout of 2000ms + timebase = get_time(); + is_timeout = false; + + // Wait for SDHCI_CLOCK_INT_STABLE to be set + mmc_print(mmc, "waiting for internal clock to stabalize..."); + while(!(regs->clock_control & 0x02) && !is_timeout) { + // Keep checking if timeout expired + is_timeout = get_time_since(timebase) > 2000000; + } + + // Clock failed to stabilize + if (is_timeout) { + mmc_print(mmc, "clock never stabalized!"); + return -1; + } else { + mmc_print(mmc, "clock stabalized."); + } + + // Clear upper 17 bits + regs->host_control2 &= ~(0xFFFE0000); + + // Clear SDHCI_PROG_CLOCK_MODE + regs->clock_control &= ~(0x20); + + // Set SDHCI_CTRL2_HOST_V4_ENABLE + regs->host_control2 |= 0x1000; + + // SDHCI_CAN_64BIT must be set + if (!(regs->capabilities & 0x10000000)) { + mmc_print(mmc, "missing CAN_64bit capability"); + return -1; + } + + // Set SDHCI_CTRL2_64BIT_ENABLE + regs->host_control2 |= 0x2000; + + // Clear SDHCI_CTRL_SDMA and SDHCI_CTRL_ADMA2 + regs->host_control &= 0xE7; + + // Set the timeout to be the maximum value + regs->timeout_control &= ~(0x0F); + regs->timeout_control |= 0x0E; + + // Clear SDHCI_CTRL_4BITBUS and SDHCI_CTRL_8BITBUS + regs->host_control &= 0xFD; + regs->host_control &= 0xDF; + + // Set SDHCI_POWER_180 + regs->power_control &= 0xF1; + regs->power_control |= 0x0A; + + regs->power_control |= 0x01; + + if (is_hs400_hs667) + { + // Set DQS_TRIM_VAL + regs->vendor_cap_overrides &= ~(0x3F00); + regs->vendor_cap_overrides |= 0x2800; + } + + // Clear TAP_VAL_UPDATED_BY_HW + regs->vendor_tuning_cntrl0 &= ~(0x20000); + + // Software tap value should be 0 for SDMMC4, but HS400/HS667 modes + // must take this value from the tuning procedure + uint32_t tap_value = is_hs400_hs667 ? 1 : 0; + + // Set TAP_VAL + regs->vendor_clock_cntrl &= ~(0xFF0000); + regs->vendor_clock_cntrl |= (tap_value << 16); + + // Clear SDHCI_CTRL_HISPD + regs->host_control &= 0xFB; + + // Clear SDHCI_CTRL_VDD_180 + regs->host_control2 &= ~(0x08); + + // Set SDHCI_DIVIDER and SDHCI_DIVIDER_HI + // FIXME: divider SD if necessary + regs->clock_control &= ~(0xFFC0); + regs->clock_control |= (0x80 << 8); // XXX wtf is this + //regs->clock_control |= ((sd_divider_lo << 0x08) | (sd_divider_hi << 0x06)); + + // HS400/HS667 modes require additional DLL calibration + if (is_hs400_hs667) + { + // Set CALIBRATE + regs->vendor_dllcal_cfg |= 0x80000000; + + // Program a timeout of 5ms + timebase = get_time(); + is_timeout = false; + + // Wait for CALIBRATE to be cleared + mmc_print(mmc, "starting calibration..."); + while(regs->vendor_dllcal_cfg & 0x80000000 && !is_timeout) { + // Keep checking if timeout expired + is_timeout = get_time_since(timebase) > 5000; + } + + // Failed to calibrate in time + if (is_timeout) { + mmc_print(mmc, "calibration failed!"); + return -1; + } + + mmc_print(mmc, "calibration okay."); + + // Program a timeout of 10ms + timebase = get_time(); + is_timeout = false; + + // Wait for DLL_CAL_ACTIVE to be cleared + mmc_print(mmc, "waiting for calibration to finalize.... "); + while((regs->vendor_dllcal_cfg_sta & 0x80000000) && !is_timeout) { + // Keep checking if timeout expired + is_timeout = get_time_since(timebase) > 10000; + } + + // Failed to calibrate in time + if (is_timeout) { + mmc_print(mmc, "calibration failed to finalize!"); + return -1; + } + + mmc_print(mmc, "calibration complete!"); + } + + // Set SDHCI_CLOCK_CARD_EN + regs->clock_control |= 0x04; + + mmc_print(mmc, "initialized."); + return 0; } -/* Initialize the SDMMC4 (eMMC) controller */ -void sdmmc4_init(void) +/** + * Blocks until the SD driver is ready for a command, + * or the MMC controller's timeout interval is met. + * + * @param mmc The MMC controller + */ +static int sdmmc_wait_for_command_readiness(struct mmc *mmc) { - /* TODO */ -} \ No newline at end of file + uint32_t timebase = get_time(); + + // Wait until we either wind up ready, or until we've timed out. + while(true) { + if (get_time_since(timebase) > mmc->timeout) { + return -ETIMEDOUT; + } + + // Wait until we're not inhibited from sending commands... + if (!(mmc->regs->present_state & MMC_COMMAND_INHIBIT)) + return 0; + } +} + + +/** + * Blocks until the SD driver has completed issuing a command. + * + * @param mmc The MMC controller + */ +static int sdmmc_wait_for_command_completion(struct mmc *mmc) +{ + uint32_t timebase = get_time(); + + // Wait until we either wind up ready, or until we've timed out. + while(true) { + if (get_time_since(timebase) > mmc->timeout) + return -ETIMEDOUT; + + // If the command completes, return that. + if (mmc->regs->int_status & MMC_STATUS_COMMAND_COMPLETE) + return 0; + + // If the command's no longer active, and we don't have an error, use that. + // This is what the bootrom does? + if (!(mmc->regs->present_state & MMC_COMMAND_INHIBIT)) + return 0; + + // If an error occurs, return it. + if (mmc->regs->int_status & MMC_STATUS_ERROR_MASK) + return (mmc->regs->int_status & MMC_STATUS_ERROR_MASK) >> 16; + } +} + + +/** + * Blocks until the SD driver has completed issuing a command. + * + * @param mmc The MMC controller + */ +static int sdmmc_wait_for_transfer_completion(struct mmc *mmc) +{ + uint32_t timebase = get_time(); + + // Wait until we either wind up ready, or until we've timed out. + while(true) { + + if (get_time_since(timebase) > mmc->timeout) + return -ETIMEDOUT; + + // If the command completes, return that. + if (mmc->regs->int_status & MMC_STATUS_TRANSFER_COMPLETE) + return 0; + + // If an error occurs, return it. + if (mmc->regs->int_status & MMC_STATUS_ERROR_MASK) + return (mmc->regs->int_status & MMC_STATUS_ERROR_MASK) >> 16; + } +} + + +/** + * Prepare the data-related registers for command transmission. + * + * @param mmc The device to be used to transmit. + * @param blocks The total number of blocks to be transferred. + * @param is_write True iff we're sending data _to_ the card. + */ +static void sdmmc_prepare_command_data(struct mmc *mmc, uint16_t blocks, bool is_write) +{ + // Ensure we're targeting our bounce buffer. + mmc->regs->dma_address = (uint32_t)sdmmc_bounce_buffer; + + // Ensure we're using System DMA mode for DMA. + mmc->regs->host_control &= ~MMC_DMA_SELECT_MASK; + + // Set up the DMA block size and count. + // FIXME: implement! + mmc_print(mmc, "WARNING: block size and count register needs to be set up, but CSD code isnt done yet!"); + mmc->regs->block_size = 0; + mmc->regs->block_count = 0; + + // Always use DMA mode for data, as that's what Nintendo does. :) + mmc->regs->transfer_mode = MMC_TRANSFER_DMA_ENABLE | MMC_TRANSFER_LIMIT_BLOCK_COUNT; + + // If this is a multi-block datagram, indicate so. + if (blocks > 1) + mmc->regs->transfer_mode |= MMC_TRANSFER_MULTIPLE_BLOCKS; + + // If this is a write, set the WRITE mode. + if (is_write) + mmc->regs->transfer_mode |= MMC_TRANSFER_HOST_TO_CARD; + +} + + +/** + * Prepare the command-related registers for command transmission. + * + * @param mmc The device to be used to transmit. + * @param blocks_to_xfer The total number of blocks to be transferred. + * @param command The command number to issue. + * @param response_type The type of response we'll expect. + */ +static void sdmmc_prepare_command_registers(struct mmc *mmc, int blocks_to_xfer, + enum sdmmc_command command, enum sdmmc_response_type response_type) +{ + // Populate the command number + uint16_t to_write = (command << MMC_COMMAND_NUMBER_SHIFT) | (response_type << MMC_COMMAND_RESPONSE_TYPE_SHIFT) | MMC_COMMAND_CHECK_NUMBER; + + // If this is a "stop transmitting" command, set the abort flag. + if (command == CMD_STOP_TRANSMISSION) + to_write |= MMC_COMMAND_TYPE_ABORT; + + // TODO: do we want to support CRC or index checks? + if (blocks_to_xfer) + to_write |= MMC_COMMAND_HAS_DATA; + + // Write our command to the given register. + // This must be all done at once, as writes to this register have semantic meaning. + mmc->regs->command = to_write; +} + + + +/** + * Sends a command to the SD card, and awaits a response. + */ +static int sdmmc_send_command(struct mmc *mmc, enum sdmmc_command command, + enum sdmmc_response_type response_type, uint32_t argument, int blocks_to_transfer, bool is_write) +{ + int rc; + + // Wait until we can issue commands to the device. + mmc_print(mmc, "waiting for command readiness..."); + rc = sdmmc_wait_for_command_readiness(mmc); + if(rc) { + mmc_print(mmc, "card not willing to accept commands (%d / %08x)", rc, mmc->regs->present_state); + return -EBUSY; + } + + // Populate the command argument. + mmc_print(mmc, "populating argument..."); + mmc->regs->argument = argument; + + // If we have data to send, prepare it. + if (blocks_to_transfer) { + mmc_print(mmc, "preparing data..."); + sdmmc_prepare_command_data(mmc, blocks_to_transfer, is_write); + } + + // Configure the controller to send the command. + mmc_print(mmc, "preparing command..."); + sdmmc_prepare_command_registers(mmc, blocks_to_transfer, command, response_type); + + // Wait for the command to be completed. + mmc_print(mmc, "waiting for command completion..."); + rc = sdmmc_wait_for_command_completion(mmc); + if(rc) { + mmc_print(mmc, "failed to issue CMD%d (%d / %08x)", command, rc, mmc->regs->int_status); + return rc; + } + + // TODO: copy response to an out argument, if it we have one? + + // If we had a data stage, handle it. + if(blocks_to_transfer) { + + // Wait for the transfer to be complete... + mmc_print(mmc, "waiting for transfer completion..."); + rc = sdmmc_wait_for_transfer_completion(mmc); + if(rc) { + mmc_print(mmc, "failed to complete CMD%d data stage (%d)", command, rc); + return rc; + } + + // TODO: copy data from the bounce buffer to the output buffer, if this was a read + } + + mmc_print(mmc, "command success!"); + return 0; +} + + +/** + * Retrieves information about the card, and populates the MMC structure accordingly. + * Used as part of the SDMMC initialization process. + */ +static int sdmmc_card_init(struct mmc *mmc) +{ + int rc; + + rc = sdmmc_send_command(mmc, CMD_GO_IDLE_OR_INIT, 0, 0, 0, 0); + rc = sdmmc_send_command(mmc, CMD_SEND_OPERATING_CONDITIONS, MMC_RESPONSE_LEN48, 0, 0, 0); + + mmc_print(mmc, "response: %08x %08x %08x %08x %08x %08x %08x %08x", + mmc->regs->response[0], mmc->regs->response[1], + mmc->regs->response[2], mmc->regs->response[2], + mmc->regs->response[4], mmc->regs->response[5], + mmc->regs->response[6], mmc->regs->response[7]); + + + return rc; +} + + + + +/** + * Set up a new SDMMC driver. + * FIXME: clean up! + * + * @param mmc The SDMMC structure to be initiailized with the device state. + * @param controler The controller description to be used; usually SWITCH_EMMC + * or SWTICH_MICROSD. + */ +int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller) +{ + int rc; + + // Get a reference to the registers for the relevant SDMMC controller. + mmc->regs = sdmmc_get_regs(controller); + mmc->name = "eMMC"; + + // Default to a timeout of 1S. + // FIXME: lower + // FIXME: abstract + mmc->timeout = 1000000; + + // Initialize the raw SDMMC controller. + mmc_print(mmc, "setting up hardware"); + rc = sdmmc_hardware_init(mmc); + if (rc) { + mmc_print(mmc, "failed to set up controller! (%d)", rc); + return rc; + } + + // Initialize the SDMMC card. + mmc_print(mmc, "setting up card"); + rc = sdmmc_card_init(mmc); + if (rc) { + mmc_print(mmc, "failed to set up card! (%d)", rc); + return rc; + } + + + + return rc; +} + + +/** + * Reads a sector or sectors from a given SD card. + * + * @param mmc The MMC device to work with. + * @param buffer The output buffer to target. + * @param sector The sector number to read. + * @param count The number of sectors to read. + */ +int sdmmc_read(struct mmc *mmc, void *buffer, uint32_t sector, unsigned int count) +{ + return -1; +} diff --git a/fusee/fusee-primary/src/sdmmc.h b/fusee/fusee-primary/src/sdmmc.h index a3d39cd64..3ee037a6e 100644 --- a/fusee/fusee-primary/src/sdmmc.h +++ b/fusee/fusee-primary/src/sdmmc.h @@ -1,134 +1,54 @@ -#ifndef FUSEE_SDMMC_H -#define FUSEE_SDMMC_H +#ifndef __FUSEE_SDMMC_H__ +#define __FUSEE_SDMMC_H__ #include #include +#include "utils.h" -typedef struct { - uint32_t SDHCI_DMA_ADDRESS; - uint16_t SDHCI_BLOCK_SIZE; - uint16_t SDHCI_BLOCK_COUNT; - uint32_t SDHCI_ARGUMENT; - uint16_t SDHCI_TRANSFER_MODE; - uint16_t SDHCI_COMMAND; - uint16_t SDHCI_RESPONSE[0x8]; - uint32_t SDHCI_BUFFER; - uint32_t SDHCI_PRESENT_STATE; - uint8_t SDHCI_HOST_CONTROL; - uint8_t SDHCI_POWER_CONTROL; - uint8_t SDHCI_BLOCK_GAP_CONTROL; - uint8_t SDHCI_WAKE_UP_CONTROL; - uint16_t SDHCI_CLOCK_CONTROL; - uint8_t SDHCI_TIMEOUT_CONTROL; - uint8_t SDHCI_SOFTWARE_RESET; - uint32_t SDHCI_INT_STATUS; - uint32_t SDHCI_INT_ENABLE; - uint32_t SDHCI_SIGNAL_ENABLE; - uint16_t SDHCI_ACMD12_ERR; - uint16_t SDHCI_HOST_CONTROL2; - uint32_t SDHCI_CAPABILITIES; - uint32_t SDHCI_CAPABILITIES_1; - uint32_t SDHCI_MAX_CURRENT; - uint32_t _0x4C; - uint16_t SDHCI_SET_ACMD12_ERROR; - uint16_t SDHCI_SET_INT_ERROR; - uint16_t SDHCI_ADMA_ERROR; - uint8_t _0x55[0x3]; - uint32_t SDHCI_ADMA_ADDRESS; - uint32_t SDHCI_UPPER_ADMA_ADDRESS; - uint16_t SDHCI_PRESET_FOR_INIT; - uint16_t SDHCI_PRESET_FOR_DEFAULT; - uint16_t SDHCI_PRESET_FOR_HIGH; - uint16_t SDHCI_PRESET_FOR_SDR12; - uint16_t SDHCI_PRESET_FOR_SDR25; - uint16_t SDHCI_PRESET_FOR_SDR50; - uint16_t SDHCI_PRESET_FOR_SDR104; - uint16_t SDHCI_PRESET_FOR_DDR50; - uint8_t _0x70[0x3]; - uint32_t _0x74[0x22]; - uint16_t SDHCI_SLOT_INT_STATUS; - uint16_t SDHCI_HOST_VERSION; -} sdhci_registers_t; -typedef struct { - sdhci_registers_t standard_regs; - uint32_t SDMMC_VENDOR_CLOCK_CNTRL; - uint32_t SDMMC_VENDOR_SYS_SW_CNTRL; - uint32_t SDMMC_VENDOR_ERR_INTR_STATUS; - uint32_t SDMMC_VENDOR_CAP_OVERRIDES; - uint32_t SDMMC_VENDOR_BOOT_CNTRL; - uint32_t SDMMC_VENDOR_BOOT_ACK_TIMEOUT; - uint32_t SDMMC_VENDOR_BOOT_DAT_TIMEOUT; - uint32_t SDMMC_VENDOR_DEBOUNCE_COUNT; - uint32_t SDMMC_VENDOR_MISC_CNTRL; - uint32_t SDMMC_MAX_CURRENT_OVERRIDE; - uint32_t SDMMC_MAX_CURRENT_OVERRIDE_HI; - uint32_t _0x12C[0x21]; - uint32_t SDMMC_VENDOR_IO_TRIM_CNTRL; - /* Start of SDMMC2/SDMMC4 only */ - uint32_t SDMMC_VENDOR_DLLCAL_CFG; - uint32_t SDMMC_VENDOR_DLL_CTRL0; - uint32_t SDMMC_VENDOR_DLL_CTRL1; - uint32_t SDMMC_VENDOR_DLLCAL_CFG_STA; - /* End of SDMMC2/SDMMC4 only */ - uint32_t SDMMC_VENDOR_TUNING_CNTRL0; - uint32_t SDMMC_VENDOR_TUNING_CNTRL1; - uint32_t SDMMC_VENDOR_TUNING_STATUS0; - uint32_t SDMMC_VENDOR_TUNING_STATUS1; - uint32_t SDMMC_VENDOR_CLK_GATE_HYSTERESIS_COUNT; - uint32_t SDMMC_VENDOR_PRESET_VAL0; - uint32_t SDMMC_VENDOR_PRESET_VAL1; - uint32_t SDMMC_VENDOR_PRESET_VAL2; - uint32_t SDMMC_SDMEMCOMPPADCTRL; - uint32_t SDMMC_AUTO_CAL_CONFIG; - uint32_t SDMMC_AUTO_CAL_INTERVAL; - uint32_t SDMMC_AUTO_CAL_STATUS; - uint32_t SDMMC_IO_SPARE; - uint32_t SDMMC_SDMMCA_MCCIF_FIFOCTRL; - uint32_t SDMMC_TIMEOUT_WCOAL_SDMMCA; - uint32_t _0x1FC; -} sdmmc_registers_t; +/* Opaque pointer to the Tegra SDMMC registers */ +struct tegra_sdmmc; -static inline volatile sdmmc_registers_t *get_sdmmc1_regs(void) { - return (volatile sdmmc_registers_t *)(0x700B0000); -} +/** + * Primary data structure describing a Fusée MMC driver. + */ +struct mmc { + char *name; -static inline volatile sdmmc_registers_t *get_sdmmc1b_regs(void) { - return (volatile sdmmc_registers_t *)(0x700B0000 + 0x1000); -} + volatile struct tegra_sdmmc *regs; -static inline volatile sdmmc_registers_t *get_sdmmc2_regs(void) { - return (volatile sdmmc_registers_t *)(0x700B0200); -} + unsigned int timeout; +}; -static inline volatile sdmmc_registers_t *get_sdmmc2b_regs(void) { - return (volatile sdmmc_registers_t *)(0x700B0200 + 0x2000); -} -static inline volatile sdmmc_registers_t *get_sdmmc3_regs(void) { - return (volatile sdmmc_registers_t *)(0x700B0400); -} +/** + * SDMMC controllers + */ +enum sdmmc_controller { + SWITCH_MICROSD = 0, + SWITCH_EMMC = 3 +}; -static inline volatile sdmmc_registers_t *get_sdmmc3b_regs(void) { - return (volatile sdmmc_registers_t *)(0x700B0400 + 0x3000); -} -static inline volatile sdmmc_registers_t *get_sdmmc4_regs(void) { - return (volatile sdmmc_registers_t *)(0x700B0600); -} -static inline volatile sdmmc_registers_t *get_sdmmc4b_regs(void) { - return (volatile sdmmc_registers_t *)(0x700B0600 + 0x4000); -} +/** + * Initiailzes an SDMMC controller for use with an eMMC or SD card device. + * + * @param mmc An (uninitialized) structure for the MMC device. + * @param controller The controller number to be initialized. Either SWITCH_MICROSD or SWITCH_EMMC. + */ +int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller); -#define SDMMC1_REGS (get_sdmmc1_regs()) -#define SDMMC2_REGS (get_sdmmc2_regs()) -#define SDMMC3_REGS (get_sdmmc3_regs()) -#define SDMMC4_REGS (get_sdmmc4_regs()) -void sdmmc1_init(void); -void sdmmc2_init(void); -void sdmmc3_init(void); -void sdmmc4_init(void); +/** + * Reads a sector or sectors from a given SD card. + * + * @param mmc The MMC device to work with. + * @param buffer The output buffer to target. + * @param sector The sector number to read. + * @param count The number of sectors to read. + */ +int sdmmc_read(struct mmc *mmc, void *buffer, uint32_t sector, unsigned int count); + #endif diff --git a/fusee/fusee-primary/src/timers.h b/fusee/fusee-primary/src/timers.h index 3c19a7f2c..3c314e449 100644 --- a/fusee/fusee-primary/src/timers.h +++ b/fusee/fusee-primary/src/timers.h @@ -26,6 +26,22 @@ static inline uint32_t get_time(void) { return TIMERUS_CNTR_1US_0; } +/** + * 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; +} + +/** + * Delays for a given number of microseconds. + */ +static inline void udelay(unsigned usecs) +{ + uint32_t start = get_time(); + while (get_time() - start < usecs) ; +} + __attribute__ ((noreturn)) void watchdog_reboot(void); #endif