/* * Copyright (c) 2018 naehrwert * Copyright (c) 2018 CTCaer * 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/>. */ #ifndef FUSEE_SDMMC_CORE_H #define FUSEE_SDMMC_CORE_H #include "sdmmc_tegra.h" /* Bounce buffer */ #define SDMMC_BOUNCE_BUFFER_ADDRESS 0x90000000 /* Present state */ #define SDHCI_CMD_INHIBIT 0x00000001 #define SDHCI_DATA_INHIBIT 0x00000002 #define SDHCI_DOING_WRITE 0x00000100 #define SDHCI_DOING_READ 0x00000200 #define SDHCI_SPACE_AVAILABLE 0x00000400 #define SDHCI_DATA_AVAILABLE 0x00000800 #define SDHCI_CARD_PRESENT 0x00010000 #define SDHCI_WRITE_PROTECT 0x00080000 #define SDHCI_DATA_LVL_MASK 0x00F00000 #define SDHCI_DATA_LVL_SHIFT 20 #define SDHCI_DATA_0_LVL_MASK 0x00100000 #define SDHCI_CMD_LVL 0x01000000 /* SDHCI clock control */ #define SDHCI_DIVIDER_SHIFT 8 #define SDHCI_DIVIDER_HI_SHIFT 6 #define SDHCI_DIV_MASK 0xFF #define SDHCI_DIV_MASK_LEN 8 #define SDHCI_DIV_HI_MASK 0x300 #define SDHCI_PROG_CLOCK_MODE 0x0020 #define SDHCI_CLOCK_CARD_EN 0x0004 #define SDHCI_CLOCK_INT_STABLE 0x0002 #define SDHCI_CLOCK_INT_EN 0x0001 /* SDHCI host control */ #define SDHCI_CTRL_LED 0x01 #define SDHCI_CTRL_4BITBUS 0x02 #define SDHCI_CTRL_HISPD 0x04 #define SDHCI_CTRL_DMA_MASK 0x18 #define SDHCI_CTRL_SDMA 0x00 #define SDHCI_CTRL_ADMA1 0x08 #define SDHCI_CTRL_ADMA32 0x10 #define SDHCI_CTRL_ADMA64 0x18 #define SDHCI_CTRL_8BITBUS 0x20 #define SDHCI_CTRL_CDTEST_INS 0x40 #define SDHCI_CTRL_CDTEST_EN 0x80 /* SDHCI host control 2 */ #define SDHCI_CTRL_UHS_MASK 0x0007 #define SDHCI_CTRL_UHS_SDR12 0x0000 #define SDHCI_CTRL_UHS_SDR25 0x0001 #define SDHCI_CTRL_UHS_SDR50 0x0002 #define SDHCI_CTRL_UHS_SDR104 0x0003 #define SDHCI_CTRL_UHS_DDR50 0x0004 #define SDHCI_CTRL_HS400 0x0005 #define SDHCI_CTRL_VDD_180 0x0008 #define SDHCI_CTRL_DRV_TYPE_MASK 0x0030 #define SDHCI_CTRL_DRV_TYPE_B 0x0000 #define SDHCI_CTRL_DRV_TYPE_A 0x0010 #define SDHCI_CTRL_DRV_TYPE_C 0x0020 #define SDHCI_CTRL_DRV_TYPE_D 0x0030 #define SDHCI_CTRL_EXEC_TUNING 0x0040 #define SDHCI_CTRL_TUNED_CLK 0x0080 #define SDHCI_UHS2_IF_EN 0x0100 #define SDHCI_HOST_VERSION_4_EN 0x1000 #define SDHCI_ADDRESSING_64BIT_EN 0x2000 #define SDHCI_ASYNC_INTR_EN 0x4000 #define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000 /* SDHCI capabilities */ #define SDHCI_CAN_DO_8BIT 0x00040000 #define SDHCI_CAN_DO_ADMA2 0x00080000 #define SDHCI_CAN_DO_ADMA1 0x00100000 #define SDHCI_CAN_DO_HISPD 0x00200000 #define SDHCI_CAN_DO_SDMA 0x00400000 #define SDHCI_CAN_VDD_330 0x01000000 #define SDHCI_CAN_VDD_300 0x02000000 #define SDHCI_CAN_VDD_180 0x04000000 #define SDHCI_CAN_64BIT 0x10000000 #define SDHCI_ASYNC_INTR 0x20000000 /* Vendor clock control */ #define SDMMC_CLOCK_TAP_MASK (0xFF << 16) #define SDMMC_CLOCK_TAP_SDMMC1 (0x04 << 16) #define SDMMC_CLOCK_TAP_SDMMC2 (0x00 << 16) #define SDMMC_CLOCK_TAP_SDMMC3 (0x03 << 16) #define SDMMC_CLOCK_TAP_SDMMC4 (0x00 << 16) #define SDMMC_CLOCK_TRIM_MASK (0xFF << 24) #define SDMMC_CLOCK_TRIM_SDMMC1_ERISTA (0x02 << 24) #define SDMMC_CLOCK_TRIM_SDMMC1_MARIKO (0x0E << 24) #define SDMMC_CLOCK_TRIM_SDMMC2_ERISTA (0x08 << 24) #define SDMMC_CLOCK_TRIM_SDMMC2_MARIKO (0x0D << 24) #define SDMMC_CLOCK_TRIM_SDMMC3 (0x03 << 24) #define SDMMC_CLOCK_TRIM_SDMMC4_ERISTA (0x08 << 24) #define SDMMC_CLOCK_TRIM_SDMMC4_MARIKO (0x0D << 24) #define SDMMC_CLOCK_SPI_MODE_CLKEN_OVERRIDE (1 << 2) #define SDMMC_CLOCK_PADPIPE_CLKEN_OVERRIDE (1 << 3) /* Autocal configuration */ #define SDMMC_AUTOCAL_PDPU_CONFIG_MASK 0x7F7F #define SDMMC_AUTOCAL_PDPU_SDMMC1_1V8_ERISTA 0x7B7B #define SDMMC_AUTOCAL_PDPU_SDMMC1_1V8_MARIKO 0x0606 #define SDMMC_AUTOCAL_PDPU_SDMMC1_3V3_ERISTA 0x7D00 #define SDMMC_AUTOCAL_PDPU_SDMMC1_3V3_MARIKO 0x0000 #define SDMMC_AUTOCAL_PDPU_SDMMC4_1V8 0x0505 #define SDMMC_AUTOCAL_START (1 << 31) #define SDMMC_AUTOCAL_ENABLE (1 << 29) /* Autocal status */ #define SDMMC_AUTOCAL_ACTIVE (1 << 31) /* Vendor tuning control 0*/ #define SDMMC_VENDOR_TUNING_TRIES_MASK (0x7 << 13) #define SDMMC_VENDOR_TUNING_TRIES_SHIFT 13 #define SDMMC_VENDOR_TUNING_MULTIPLIER_MASK (0x7F << 6) #define SDMMC_VENDOR_TUNING_MULTIPLIER_UNITY (1 << 6) #define SDMMC_VENDOR_TUNING_DIVIDER_MASK (0x7 << 3) #define SDMMC_VENDOR_TUNING_SET_BY_HW (1 << 17) /* Vendor tuning control 1*/ #define SDMMC_VENDOR_TUNING_STEP_SIZE_SDR50_DEFAULT (0 << 0) #define SDMMC_VENDOR_TUNING_STEP_SIZE_SDR104_DEFAULT (0 << 4) /* Vendor capability overrides */ #define SDMMC_VENDOR_CAPABILITY_DQS_TRIM_MASK (0x3F << 8) #define SDMMC_VENDOR_CAPABILITY_DQS_TRIM_HS400 (0x11 << 8) /* Timeouts */ #define SDMMC_AUTOCAL_TIMEOUT (10 * 1000) #define SDMMC_TUNING_TIMEOUT (150 * 1000) /* Command response flags */ #define SDMMC_RSP_PRESENT (1 << 0) #define SDMMC_RSP_136 (1 << 1) #define SDMMC_RSP_CRC (1 << 2) #define SDMMC_RSP_BUSY (1 << 3) #define SDMMC_RSP_OPCODE (1 << 4) /* Command types */ #define SDMMC_CMD_MASK (3 << 5) #define SDMMC_CMD_AC (0 << 5) #define SDMMC_CMD_ADTC (1 << 5) #define SDMMC_CMD_BC (2 << 5) #define SDMMC_CMD_BCR (3 << 5) /* SPI command response flags */ #define SDMMC_RSP_SPI_S1 (1 << 7) #define SDMMC_RSP_SPI_S2 (1 << 8) #define SDMMC_RSP_SPI_B4 (1 << 9) #define SDMMC_RSP_SPI_BUSY (1 << 10) /* Native response types for commands */ #define SDMMC_RSP_NONE (0) #define SDMMC_RSP_R1 (SDMMC_RSP_PRESENT|SDMMC_RSP_CRC|SDMMC_RSP_OPCODE) #define SDMMC_RSP_R1B (SDMMC_RSP_PRESENT|SDMMC_RSP_CRC|SDMMC_RSP_OPCODE|SDMMC_RSP_BUSY) #define SDMMC_RSP_R2 (SDMMC_RSP_PRESENT|SDMMC_RSP_136|SDMMC_RSP_CRC) #define SDMMC_RSP_R3 (SDMMC_RSP_PRESENT) #define SDMMC_RSP_R4 (SDMMC_RSP_PRESENT) #define SDMMC_RSP_R5 (SDMMC_RSP_PRESENT|SDMMC_RSP_CRC|SDMMC_RSP_OPCODE) #define SDMMC_RSP_R6 (SDMMC_RSP_PRESENT|SDMMC_RSP_CRC|SDMMC_RSP_OPCODE) #define SDMMC_RSP_R7 (SDMMC_RSP_PRESENT|SDMMC_RSP_CRC|SDMMC_RSP_OPCODE) #define SDMMC_RSP_R1_NO_CRC (SDMMC_RSP_PRESENT|SDMMC_RSP_OPCODE) /* SPI response types for commands */ #define SDMMC_RSP_SPI_R1 (SDMMC_RSP_SPI_S1) #define SDMMC_RSP_SPI_R1B (SDMMC_RSP_SPI_S1|SDMMC_RSP_SPI_BUSY) #define SDMMC_RSP_SPI_R2 (SDMMC_RSP_SPI_S1|SDMMC_RSP_SPI_S2) #define SDMMC_RSP_SPI_R3 (SDMMC_RSP_SPI_S1|SDMMC_RSP_SPI_B4) #define SDMMC_RSP_SPI_R4 (SDMMC_RSP_SPI_S1|SDMMC_RSP_SPI_B4) #define SDMMC_RSP_SPI_R5 (SDMMC_RSP_SPI_S1|SDMMC_RSP_SPI_S2) #define SDMMC_RSP_SPI_R7 (SDMMC_RSP_SPI_S1|SDMMC_RSP_SPI_B4) /* SDMMC controllers */ typedef enum { SDMMC_1 = 0, SDMMC_2 = 1, SDMMC_3 = 2, 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, SDMMC_VOLTAGE_3V3 = 2 } SdmmcBusVoltage; typedef enum { SDMMC_BUS_WIDTH_1BIT = 0, SDMMC_BUS_WIDTH_4BIT = 1, SDMMC_BUS_WIDTH_8BIT = 2 } SdmmcBusWidth; typedef enum { SDMMC_SPEED_MMC_IDENT = 0, SDMMC_SPEED_MMC_LEGACY = 1, SDMMC_SPEED_MMC_HS = 2, SDMMC_SPEED_MMC_HS200 = 3, SDMMC_SPEED_MMC_HS400 = 4, SDMMC_SPEED_SD_IDENT = 5, SDMMC_SPEED_SD_DS = 6, SDMMC_SPEED_SD_HS = 7, SDMMC_SPEED_SD_SDR12 = 8, SDMMC_SPEED_SD_SDR25 = 9, SDMMC_SPEED_SD_SDR50 = 10, SDMMC_SPEED_SD_SDR104 = 11, SDMMC_SPEED_SD_DDR50 = 12, SDMMC_SPEED_GC_ASIC_FPGA = 13, SDMMC_SPEED_GC_ASIC = 14, SDMMC_SPEED_EMU_SDR104 = 255, /* Custom speed mode. Prevents low voltage switch in MMC emulation. */ } SdmmcBusSpeed; typedef enum { SDMMC_CAR_DIVIDER_MMC_LEGACY = 30, /* (16 * 2) - 2 */ SDMMC_CAR_DIVIDER_MMC_HS = 14, /* (8 * 2) - 2 */ SDMMC_CAR_DIVIDER_MMC_HS200 = 3, /* (2.5 * 2) - 2 (for PLLP_OUT0, same as HS400) */ SDMMC_CAR_DIVIDER_SD_SDR12 = 31, /* (16.5 * 2) - 2 */ SDMMC_CAR_DIVIDER_SD_SDR25 = 15, /* (8.5 * 2) - 2 */ SDMMC_CAR_DIVIDER_SD_SDR50 = 7, /* (4.5 * 2) - 2 */ SDMMC_CAR_DIVIDER_SD_SDR104 = 2, /* (2 * 2) - 2 */ SDMMC_CAR_DIVIDER_GC_ASIC_FPGA = 18, /* (5 * 2 * 2) - 2 */ } SdmmcCarDivider; /* Structure for describing a SDMMC device. */ typedef struct { /* Controller number */ SdmmcControllerNum controller; /* Backing register space */ volatile tegra_sdmmc_t *regs; /* Controller properties */ const char *name; bool has_sd; bool is_clk_running; bool is_sd_clk_enabled; bool is_tuning_tap_val_set; bool use_adma; uint32_t tap_val; uint32_t internal_divider; uint32_t resp[4]; uint32_t resp_auto_cmd12; uint32_t next_dma_addr; uint8_t* dma_bounce_buf; SdmmcBusVoltage bus_voltage; SdmmcBusWidth bus_width; /* Per-controller operations. */ int (*sdmmc_config)(); } sdmmc_t; /* Structure for describing a SDMMC command. */ typedef struct { uint32_t opcode; uint32_t arg; uint32_t resp[4]; uint32_t flags; /* Expected response type. */ } sdmmc_command_t; /* Structure for describing a SDMMC request. */ typedef struct { void* data; uint32_t blksz; uint32_t num_blocks; bool is_multi_block; bool is_read; bool is_auto_cmd12; } sdmmc_request_t; int sdmmc_init(sdmmc_t *sdmmc, SdmmcControllerNum controller, SdmmcBusVoltage bus_voltage, SdmmcBusWidth bus_width, SdmmcBusSpeed bus_speed); void sdmmc_finish(sdmmc_t *sdmmc); int sdmmc_select_speed(sdmmc_t *sdmmc, SdmmcBusSpeed bus_speed); void sdmmc_select_bus_width(sdmmc_t *sdmmc, SdmmcBusWidth width); void sdmmc_select_voltage(sdmmc_t *sdmmc, SdmmcBusVoltage voltage); void sdmmc_adjust_sd_clock(sdmmc_t *sdmmc); int sdmmc_switch_voltage(sdmmc_t *sdmmc); void sdmmc_set_tuning_tap_val(sdmmc_t *sdmmc); int sdmmc_execute_tuning(sdmmc_t *sdmmc, SdmmcBusSpeed bus_speed, uint32_t opcode); int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, uint32_t *num_blocks_out); int sdmmc_load_response(sdmmc_t *sdmmc, uint32_t flags, uint32_t *resp); int sdmmc_abort(sdmmc_t *sdmmc, uint32_t opcode); void sdmmc_error(sdmmc_t *sdmmc, char *fmt, ...); void sdmmc_warn(sdmmc_t *sdmmc, char *fmt, ...); void sdmmc_info(sdmmc_t *sdmmc, char *fmt, ...); void sdmmc_debug(sdmmc_t *sdmmc, char *fmt, ...); void sdmmc_dump_regs(sdmmc_t *sdmmc); #endif