diff --git a/fusee/fusee-primary/Makefile b/fusee/fusee-primary/Makefile index 16fcd8c07..975160cb0 100644 --- a/fusee/fusee-primary/Makefile +++ b/fusee/fusee-primary/Makefile @@ -18,7 +18,7 @@ include $(DEVKITARM)/base_rules #--------------------------------------------------------------------------------- TARGET := $(notdir $(CURDIR)) BUILD := build -SOURCES := src src/lib src/lib/fatfs src/display src/hwinit +SOURCES := src src/sdmmc src/hwinit src/lib src/lib/fatfs src/display DATA := data INCLUDES := include diff --git a/fusee/fusee-primary/src/apb_misc.h b/fusee/fusee-primary/src/apb_misc.h index 278d2362f..114b7496a 100644 --- a/fusee/fusee-primary/src/apb_misc.h +++ b/fusee/fusee-primary/src/apb_misc.h @@ -1,15 +1,5 @@ -#ifndef __APB_MISC_H__ -#define __APB_MISC_H__ - -/* FIXME: clean up */ - -#define MISC_BASE (0x70000000UL) -#define PINMUX_BASE (MISC_BASE + 0x3000) -#define PINMUX_AUX_GPIO_PZ1_0 (*(volatile uint32_t *)(PINMUX_BASE + 0x280)) - -#define APB_MISC_GP_VGPIO_GPIO_MUX_SEL_0 MAKE_REG32(MISC_BASE + 0xb74) -#define APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL_0 MAKE_REG32(MISC_BASE + 0xa98) -#define APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 MAKE_REG32(MISC_BASE + 0xab4) +#ifndef FUSEE_APB_MISC_H +#define FUSEE_APB_MISC_H #define SDMMC1_PAD_CAL_DRVUP_SHIFT (20) #define SDMMC1_PAD_CAL_DRVDN_SHIFT (12) @@ -21,4 +11,44 @@ #define CFG2TMC_EMMC4_PAD_DRVUP_COMP_MASK (0x3Fu << CFG2TMC_EMMC4_PAD_DRVUP_COMP_SHIFT) #define CFG2TMC_EMMC4_PAD_DRVDN_COMP_MASK (0x3Fu << CFG2TMC_EMMC4_PAD_DRVDN_COMP_SHIFT) +#define PADCTL_SDMMC1_DEEP_LOOPBACK (1 << 0) +#define PADCTL_SDMMC3_DEEP_LOOPBACK (1 << 0) +#define PADCTL_SDMMC2_ENABLE_DATA_IN (0xFF << 8) +#define PADCTL_SDMMC2_ENABLE_CLK_IN (0x3 << 4) +#define PADCTL_SDMMC2_DEEP_LOOPBACK (1 << 0) +#define PADCTL_SDMMC4_ENABLE_DATA_IN (0xFF << 8) +#define PADCTL_SDMMC4_ENABLE_CLK_IN (0x3 << 4) +#define PADCTL_SDMMC4_DEEP_LOOPBACK (1 << 0) +#define PADCTL_SDMMC1_CD_SOURCE (1 << 0) +#define PADCTL_SDMMC1_WP_SOURCE (1 << 1) +#define PADCTL_SDMMC3_CD_SOURCE (1 << 2) +#define PADCTL_SDMMC3_WP_SOURCE (1 << 3) + +typedef struct { + uint32_t asdbgreg; /* 0x810 */ + uint32_t reserved0[0x31]; + uint32_t sdmmc1_clk_lpbk_control; /* 0x8D4 */ + uint32_t sdmmc3_clk_lpbk_control; /* 0x8D8 */ + uint32_t emmc2_pad_cfg_control; /* 0x8DC */ + uint32_t emmc4_pad_cfg_control; /* 0x8E0 */ + uint32_t _todo0[0x6E]; + uint32_t sdmmc1_pad_cfgpadctrl; /* 0xA98 */ + uint32_t emmc2_pad_cfgpadctrl; /* 0xA9C */ + uint32_t emmc2_pad_drv_type_cfgpadctrl; /* 0xAA0 */ + uint32_t emmc2_pad_pupd_cfgpadctrl; /* 0xAA4 */ + uint32_t _todo1[0x03]; + uint32_t sdmmc3_pad_cfgpadctrl; /* 0xAB0 */ + uint32_t emmc4_pad_cfgpadctrl; /* 0xAB4 */ + uint32_t emmc4_pad_drv_type_cfgpadctrl; /* 0xAB8 */ + uint32_t emmc4_pad_pupd_cfgpadctrl; /* 0xABC */ + uint32_t _todo2[0x2E]; + uint32_t vgpio_gpio_mux_sel; /* 0xB74 */ + uint32_t qspi_sck_lpbk_control; /* 0xB78 */ +} tegra_padctl_t; + +static inline volatile tegra_padctl_t *padctl_get_regs(void) +{ + return (volatile tegra_padctl_t *)0x70000810; +} + #endif diff --git a/fusee/fusee-primary/src/car.h b/fusee/fusee-primary/src/car.h index 593c7a78a..8cc0fb139 100644 --- a/fusee/fusee-primary/src/car.h +++ b/fusee/fusee-primary/src/car.h @@ -1,95 +1,68 @@ +#ifndef FUSEE_CAR_H +#define FUSEE_CAR_H -#ifndef __FUSEE_CLOCK_H__ -#define __FUSEE_CLOCK_H__ +#define CLK_SOURCE_SDMMC1 20 +#define CLK_SOURCE_SDMMC2 21 +#define CLK_SOURCE_SDMMC3 47 +#define CLK_SOURCE_SDMMC4 25 +#define CLK_SOURCE_SDMMC_LEGACY 0 -#include "utils.h" +#define CLK_L_SDMMC1 (1 << 14) +#define CLK_L_SDMMC2 (1 << 9) +#define CLK_U_SDMMC3 (1 << 5) +#define CLK_L_SDMMC4 (1 << 15) -/** - * Struct definition yanked from u-boot. - */ +#define TEGRA_CLK_PLLS 6 /* Number of normal PLLs */ +#define TEGRA_CLK_SIMPLE_PLLS 3 /* Number of simple PLLs */ +#define TEGRA_CLK_SOURCES 64 /* Number of ppl clock sources L/H/U */ +#define TEGRA_CLK_SOURCES_VW 32 /* Number of ppl clock sources V/W */ +#define TEGRA_CLK_SOURCES_X 32 /* Number of ppl clock sources X */ +#define TEGRA_CLK_SOURCES_Y 18 /* Number of ppl clock sources Y */ + +#define CLK_SOURCE_MASK (0b111 << 29) +#define CLK_SOURCE_FIRST (0b000 << 29) +#define CLK_DIVIDER_MASK (0xff << 0) +#define CLK_DIVIDER_UNITY (0x00 << 0) + +#define CAR_CONTROL_SDMMC1 (1 << 14) +#define CAR_CONTROL_SDMMC4 (1 << 15) +#define CAR_CONTROL_SDMMC_LEGACY (1 << 1) /* PLL registers - there are several PLLs in the clock controller */ -struct clk_pll { +typedef struct { uint32_t pll_base; /* the control register */ + /* pll_out[0] is output A control, pll_out[1] is output B control */ uint32_t pll_out[2]; uint32_t pll_misc; /* other misc things */ -}; +} clk_pll_t; /* PLL registers - there are several PLLs in the clock controller */ -struct clk_pll_simple { +typedef struct { uint32_t pll_base; /* the control register */ uint32_t pll_misc; /* other misc things */ -}; +} clk_pll_simple_t; -struct clk_pllm { +typedef struct { uint32_t pllm_base; /* the control register */ uint32_t pllm_out; /* output control */ uint32_t pllm_misc1; /* misc1 */ uint32_t pllm_misc2; /* misc2 */ -}; - - -/* - * Most PLLs use the clk_pll structure, but some have a simpler two-member - * structure for which we use clk_pll_simple. The reason for this non- - * othogonal setup is not stated. - */ -enum { - TEGRA_CLK_PLLS = 6, /* Number of normal PLLs */ - TEGRA_CLK_SIMPLE_PLLS = 3, /* Number of simple PLLs */ - TEGRA_CLK_SOURCES = 64, /* Number of ppl clock sources L/H/U */ - TEGRA_CLK_SOURCES_VW = 32, /* Number of ppl clock sources V/W */ - TEGRA_CLK_SOURCES_X = 32, /* Number of ppl clock sources X */ - TEGRA_CLK_SOURCES_Y = 18, /* Number of ppl clock sources Y */ -}; - -/* - * Masks for TEGRA_CLK_SOURCE elements. - */ -enum { - CLK_SOURCE_MASK = (0b111 << 29), - CLK_SOURCE_SDMMC1_PLLP_OUT0 = (0b000 << 29), /* Fixed 408 MHz */ - CLK_SOURCE_SDMMC4_PLLP_OUT0 = (0b000 << 29), /* Fixed 408 MHz */ - CLK_SOURCE_SDMMC4_PLLC4_OUT2_LJ = (0b001 << 29), /* 199.68 MHz */ - CLK_SOURCE_SDMMC_LEGACY_PLLP_OUT0 = (0b100 << 29), /* Fixed 408 MHz */ - - CLK_DIVIDER_MASK = (0xff << 0), - CLK_DIVIDER_UNITY = (0x00 << 0), -}; - - -/** - * Reset bits for relevant registers. - */ -enum { - CAR_CONTROL_SDMMC1 = (1 << 14), - CAR_CONTROL_SDMMC4 = (1 << 15), - CAR_CONTROL_SDMMC_LEGACY = (1 << 1), -}; - - -enum { - CLK_SOURCE_SDMMC1 = 20, - CLK_SOURCE_SDMMC4 = 25, /* 0x54 into the the main source block */ - - CLK_SOURCE_SDMMC_LEGACY = 0, /* first in block Y */ -}; - +} clk_pllm_t; /* Clock/Reset Controller (CLK_RST_CONTROLLER_) regs */ -struct tegra_car { +typedef struct { uint32_t rst_src; /* _RST_SOURCE_0,0x00 */ uint32_t rst_dev_l; uint32_t rst_dev_h; uint32_t rst_dev_u; - uint32_t clk_out_dev_l; - uint32_t clk_out_dev_h; - uint32_t clk_out_dev_u; + uint32_t clk_out_enb_l; + uint32_t clk_out_enb_h; + uint32_t clk_out_enb_u; - uint32_t reserved0; /* reserved_0, 0x1C */ + uint32_t reserved0; /* reserved_0, 0x1C */ uint32_t cclk_brst_pol; /* _CCLK_BURST_POLICY_0, 0x20 */ uint32_t super_cclk_div; /* _SUPER_CCLK_DIVIDER_0,0x24 */ uint32_t sclk_brst_pol; /* _SCLK_BURST_POLICY_0, 0x28 */ @@ -97,21 +70,21 @@ struct tegra_car { uint32_t clk_sys_rate; /* _CLK_SYSTEM_RATE_0, 0x30 */ uint32_t prog_dly_clk; /* _PROG_DLY_CLK_0, 0x34 */ uint32_t aud_sync_clk_rate; /* _AUDIO_SYNC_CLK_RATE_0,0x38 */ - uint32_t reserved1; /* reserved_1, 0x3C */ + uint32_t reserved1; /* reserved_1, 0x3C */ uint32_t cop_clk_skip_plcy; /* _COP_CLK_SKIP_POLICY_0,0x40 */ uint32_t clk_mask_arm; /* _CLK_MASK_ARM_0, 0x44 */ uint32_t misc_clk_enb; /* _MISC_CLK_ENB_0, 0x48 */ uint32_t clk_cpu_cmplx; /* _CLK_CPU_CMPLX_0, 0x4C */ - uint32_t osc_ctrl; /* _OSC_CTRL_0, 0x50 */ - uint32_t pll_lfsr; /* _PLL_LFSR_0, 0x54 */ + uint32_t osc_ctrl; /* _OSC_CTRL_0, 0x50 */ + uint32_t pll_lfsr; /* _PLL_LFSR_0, 0x54 */ uint32_t osc_freq_det; /* _OSC_FREQ_DET_0, 0x58 */ uint32_t osc_freq_det_stat; /* _OSC_FREQ_DET_STATUS_0,0x5C */ uint32_t reserved2[8]; /* reserved_2[8], 0x60-7C */ - struct clk_pll pll[TEGRA_CLK_PLLS]; /* PLLs from 0x80 to 0xdc */ + clk_pll_t pll[TEGRA_CLK_PLLS]; /* PLLs from 0x80 to 0xdc */ /* PLLs from 0xe0 to 0xf4 */ - struct clk_pll_simple pll_simple[TEGRA_CLK_SIMPLE_PLLS]; + clk_pll_simple_t pll_simple[TEGRA_CLK_SIMPLE_PLLS]; uint32_t reserved10; /* _reserved_10, 0xF8 */ uint32_t reserved11; /* _reserved_11, 0xFC */ @@ -171,11 +144,11 @@ struct tegra_car { uint32_t reserved32[2]; /* _reserved_32, 0x350,0x354 */ - uint32_t rst_dev_v; /* _RST_DEVICES_V/W_0 */ - uint32_t rst_dev_w; /* _RST_DEVICES_V/W_0 */ + uint32_t rst_dev_v; /* _RST_DEVICES_V/W_0 */ + uint32_t rst_dev_w; /* _RST_DEVICES_V/W_0 */ - uint32_t clk_out_enb_v; /* _CLK_OUT_ENB_V/W_0 */ - uint32_t clk_out_enb_w; /* _CLK_OUT_ENB_V/W_0 */ + uint32_t clk_out_enb_v; /* _CLK_OUT_ENB_V/W_0 */ + uint32_t clk_out_enb_w; /* _CLK_OUT_ENB_V/W_0 */ uint32_t cclkg_brst_pol; /* _CCLKG_BURST_POLICY_0, 0x368 */ uint32_t super_cclkg_div; /* _SUPER_CCLKG_DIVIDER_0, 0x36C */ uint32_t cclklp_brst_pol; /* _CCLKLP_BURST_POLICY_0, 0x370 */ @@ -186,8 +159,8 @@ struct tegra_car { uint32_t cpu_softrst_ctrl1; /* _CPU_SOFTRST_CTRL1_0, 0x384 */ uint32_t cpu_softrst_ctrl2; /* _CPU_SOFTRST_CTRL2_0, 0x388 */ uint32_t reserved33[9]; /* _reserved_33, 0x38c-3ac */ - uint32_t clk_src_v; /* 0x3B0-0x42C */ - uint32_t clk_src_w; /* 0x3B0-0x42C */ + uint32_t clk_src_v; /* 0x3B0-0x42C */ + uint32_t clk_src_w; /* 0x3B0-0x42C */ /* _RST_DEV_V/W_SET_0 0x430 ~ 0x43c */ uint32_t rst_dev_v_set; @@ -210,18 +183,18 @@ struct tegra_car { uint32_t clk_cpug_cmplx_clr; /* _CLK_CPUG_CMPLX_CLR_0, 0x464 */ uint32_t clk_cpulp_cmplx_set; /* _CLK_CPULP_CMPLX_SET_0, 0x468 */ uint32_t clk_cpulp_cmplx_clr; /* _CLK_CPULP_CMPLX_CLR_0, 0x46C */ - uint32_t cpu_cmplx_status; /* _CPU_CMPLX_STATUS_0, 0x470 */ - uint32_t reserved40[1]; /* _reserved_40, 0x474 */ - uint32_t intstatus; /* __INTSTATUS_0, 0x478 */ - uint32_t intmask; /* __INTMASK_0, 0x47C */ - uint32_t utmip_pll_cfg0; /* _UTMIP_PLL_CFG0_0, 0x480 */ - uint32_t utmip_pll_cfg1; /* _UTMIP_PLL_CFG1_0, 0x484 */ - uint32_t utmip_pll_cfg2; /* _UTMIP_PLL_CFG2_0, 0x488 */ + uint32_t cpu_cmplx_status; /* _CPU_CMPLX_STATUS_0, 0x470 */ + uint32_t reserved40[1]; /* _reserved_40, 0x474 */ + uint32_t intstatus; /* __INTSTATUS_0, 0x478 */ + uint32_t intmask; /* __INTMASK_0, 0x47C */ + uint32_t utmip_pll_cfg0; /* _UTMIP_PLL_CFG0_0, 0x480 */ + uint32_t utmip_pll_cfg1; /* _UTMIP_PLL_CFG1_0, 0x484 */ + uint32_t utmip_pll_cfg2; /* _UTMIP_PLL_CFG2_0, 0x488 */ - uint32_t plle_aux; /* _PLLE_AUX_0, 0x48C */ - uint32_t sata_pll_cfg0; /* _SATA_PLL_CFG0_0, 0x490 */ - uint32_t sata_pll_cfg1; /* _SATA_PLL_CFG1_0, 0x494 */ - uint32_t pcie_pll_cfg0; /* _PCIE_PLL_CFG0_0, 0x498 */ + uint32_t plle_aux; /* _PLLE_AUX_0, 0x48C */ + uint32_t sata_pll_cfg0; /* _SATA_PLL_CFG0_0, 0x490 */ + uint32_t sata_pll_cfg1; /* _SATA_PLL_CFG1_0, 0x494 */ + uint32_t pcie_pll_cfg0; /* _PCIE_PLL_CFG0_0, 0x498 */ uint32_t prog_audio_dly_clk; /* _PROG_AUDIO_DLY_CLK_0, 0x49C */ uint32_t audio_sync_clk_i2s0; /* _AUDIO_SYNC_CLK_I2S0_0, 0x4A0 */ @@ -231,68 +204,65 @@ struct tegra_car { uint32_t audio_sync_clk_i2s4; /* _AUDIO_SYNC_CLK_I2S4_0, 0x4B0 */ uint32_t audio_sync_clk_spdif; /* _AUDIO_SYNC_CLK_SPDIF_0, 0x4B4 */ - uint32_t plld2_base; /* _PLLD2_BASE_0, 0x4B8 */ - uint32_t plld2_misc; /* _PLLD2_MISC_0, 0x4BC */ - uint32_t utmip_pll_cfg3; /* _UTMIP_PLL_CFG3_0, 0x4C0 */ - uint32_t pllrefe_base; /* _PLLREFE_BASE_0, 0x4C4 */ - uint32_t pllrefe_misc; /* _PLLREFE_MISC_0, 0x4C8 */ + uint32_t plld2_base; /* _PLLD2_BASE_0, 0x4B8 */ + uint32_t plld2_misc; /* _PLLD2_MISC_0, 0x4BC */ + uint32_t utmip_pll_cfg3; /* _UTMIP_PLL_CFG3_0, 0x4C0 */ + uint32_t pllrefe_base; /* _PLLREFE_BASE_0, 0x4C4 */ + uint32_t pllrefe_misc; /* _PLLREFE_MISC_0, 0x4C8 */ uint32_t crs_reserved_50[7]; /* _reserved_50, 0x4CC-0x4E4 */ - uint32_t pllc2_base; /* _PLLC2_BASE_0, 0x4E8 */ - uint32_t pllc2_misc0; /* _PLLC2_MISC_0_0, 0x4EC */ - uint32_t pllc2_misc1; /* _PLLC2_MISC_1_0, 0x4F0 */ - uint32_t pllc2_misc2; /* _PLLC2_MISC_2_0, 0x4F4 */ - uint32_t pllc2_misc3; /* _PLLC2_MISC_3_0, 0x4F8 */ - uint32_t pllc3_base; /* _PLLC3_BASE_0, 0x4FC */ - uint32_t pllc3_misc0; /* _PLLC3_MISC_0_0, 0x500 */ - uint32_t pllc3_misc1; /* _PLLC3_MISC_1_0, 0x504 */ - uint32_t pllc3_misc2; /* _PLLC3_MISC_2_0, 0x508 */ - uint32_t pllc3_misc3; /* _PLLC3_MISC_3_0, 0x50C */ - uint32_t pllx_misc1; /* _PLLX_MISC_1_0, 0x510 */ - uint32_t pllx_misc2; /* _PLLX_MISC_2_0, 0x514 */ - uint32_t pllx_misc3; /* _PLLX_MISC_3_0, 0x518 */ - uint32_t xusbio_pll_cfg0; /* _XUSBIO_PLL_CFG0_0, 0x51C */ - uint32_t xusbio_pll_cfg1; /* _XUSBIO_PLL_CFG0_1, 0x520 */ - uint32_t plle_aux1; /* _PLLE_AUX1_0, 0x524 */ - uint32_t pllp_reshift; /* _PLLP_RESHIFT_0, 0x528 */ + uint32_t pllc2_base; /* _PLLC2_BASE_0, 0x4E8 */ + uint32_t pllc2_misc0; /* _PLLC2_MISC_0_0, 0x4EC */ + uint32_t pllc2_misc1; /* _PLLC2_MISC_1_0, 0x4F0 */ + uint32_t pllc2_misc2; /* _PLLC2_MISC_2_0, 0x4F4 */ + uint32_t pllc2_misc3; /* _PLLC2_MISC_3_0, 0x4F8 */ + uint32_t pllc3_base; /* _PLLC3_BASE_0, 0x4FC */ + uint32_t pllc3_misc0; /* _PLLC3_MISC_0_0, 0x500 */ + uint32_t pllc3_misc1; /* _PLLC3_MISC_1_0, 0x504 */ + uint32_t pllc3_misc2; /* _PLLC3_MISC_2_0, 0x508 */ + uint32_t pllc3_misc3; /* _PLLC3_MISC_3_0, 0x50C */ + uint32_t pllx_misc1; /* _PLLX_MISC_1_0, 0x510 */ + uint32_t pllx_misc2; /* _PLLX_MISC_2_0, 0x514 */ + uint32_t pllx_misc3; /* _PLLX_MISC_3_0, 0x518 */ + uint32_t xusbio_pll_cfg0; /* _XUSBIO_PLL_CFG0_0, 0x51C */ + uint32_t xusbio_pll_cfg1; /* _XUSBIO_PLL_CFG0_1, 0x520 */ + uint32_t plle_aux1; /* _PLLE_AUX1_0, 0x524 */ + uint32_t pllp_reshift; /* _PLLP_RESHIFT_0, 0x528 */ uint32_t utmipll_hw_pwrdn_cfg0; /* _UTMIPLL_HW_PWRDN_CFG0_0, 0x52C */ uint32_t pllu_hw_pwrdn_cfg0; /* _PLLU_HW_PWRDN_CFG0_0, 0x530 */ - uint32_t xusb_pll_cfg0; /* _XUSB_PLL_CFG0_0, 0x534 */ - uint32_t reserved51[1]; /* _reserved_51, 0x538 */ - uint32_t clk_cpu_misc; /* _CLK_CPU_MISC_0, 0x53C */ - uint32_t clk_cpug_misc; /* _CLK_CPUG_MISC_0, 0x540 */ - uint32_t clk_cpulp_misc; /* _CLK_CPULP_MISC_0, 0x544 */ - uint32_t pllx_hw_ctrl_cfg; /* _PLLX_HW_CTRL_CFG_0, 0x548 */ - uint32_t pllx_sw_ramp_cfg; /* _PLLX_SW_RAMP_CFG_0, 0x54C */ + uint32_t xusb_pll_cfg0; /* _XUSB_PLL_CFG0_0, 0x534 */ + uint32_t reserved51[1]; /* _reserved_51, 0x538 */ + uint32_t clk_cpu_misc; /* _CLK_CPU_MISC_0, 0x53C */ + uint32_t clk_cpug_misc; /* _CLK_CPUG_MISC_0, 0x540 */ + uint32_t clk_cpulp_misc; /* _CLK_CPULP_MISC_0, 0x544 */ + uint32_t pllx_hw_ctrl_cfg; /* _PLLX_HW_CTRL_CFG_0, 0x548 */ + uint32_t pllx_sw_ramp_cfg; /* _PLLX_SW_RAMP_CFG_0, 0x54C */ uint32_t pllx_hw_ctrl_status; /* _PLLX_HW_CTRL_STATUS_0, 0x550 */ - uint32_t reserved52[1]; /* _reserved_52, 0x554 */ + uint32_t reserved52[1]; /* _reserved_52, 0x554 */ uint32_t super_gr3d_clk_div; /* _SUPER_GR3D_CLK_DIVIDER_0, 0x558 */ - uint32_t spare_reg0; /* _SPARE_REG0_0, 0x55C */ - uint32_t _rsv32[4]; /* 0x560-0x56c */ - uint32_t plld2_ss_cfg; /* _PLLD2_SS_CFG 0x570 */ - uint32_t _rsv32_1[7]; /* 0x574-58c */ - struct clk_pll_simple plldp; /* _PLLDP_BASE, 0x590 _PLLDP_MISC */ - uint32_t plldp_ss_cfg; /* _PLLDP_SS_CFG, 0x598 */ + uint32_t spare_reg0; /* _SPARE_REG0_0, 0x55C */ + uint32_t _rsv32[4]; /* 0x560-0x56c */ + uint32_t plld2_ss_cfg; /* _PLLD2_SS_CFG 0x570 */ + uint32_t _rsv32_1[7]; /* 0x574-58c */ + clk_pll_simple_t plldp; /* _PLLDP_BASE, 0x590 _PLLDP_MISC */ + uint32_t plldp_ss_cfg; /* _PLLDP_SS_CFG, 0x598 */ /* Tegra124+ - skip to 0x600 here for new CLK_SOURCE_ regs */ - uint32_t _rsrv32_2[25]; /* _0x59C - 0x5FC */ - uint32_t clk_src_x[TEGRA_CLK_SOURCES_X]; /* XUSB, etc, 0x600-0x67C */ + uint32_t _rsrv32_2[25]; /* _0x59C - 0x5FC */ + uint32_t clk_src_x[TEGRA_CLK_SOURCES_X]; /* XUSB, etc, 0x600-0x67C */ /* Tegra210 - skip to 0x694 here for new CLK_SOURCE_ regs */ - uint32_t reserved61[5]; /* _reserved_61, 0x680 - 0x690 */ + uint32_t reserved61[5]; /* _reserved_61, 0x680 - 0x690 */ + /* * NOTE: PLLA1 regs are in the middle of this Y region. Break this in * two later if PLLA1 is needed, but for now this is cleaner. */ uint32_t clk_src_y[TEGRA_CLK_SOURCES_Y]; /* SPARE1, etc, 0x694-0x6D8 */ -}; +} tegra_car_t; - -/** - * Utility function that grabs the Tegra CAR registers. - */ -static inline struct tegra_car *car_get_regs(void) +static inline volatile tegra_car_t *car_get_regs(void) { - return (struct tegra_car *)0x60006000UL; + return (volatile tegra_car_t *)0x60006000; } #endif diff --git a/fusee/fusee-primary/src/fs_utils.c b/fusee/fusee-primary/src/fs_utils.c index ecc5a0d20..c3ee71d4c 100644 --- a/fusee/fusee-primary/src/fs_utils.c +++ b/fusee/fusee-primary/src/fs_utils.c @@ -1,36 +1,114 @@ #include "fs_utils.h" #include "hwinit.h" -#include "sdmmc.h" #include "lib/printk.h" #include "lib/fatfs/ff.h" FATFS sd_fs; -static int mounted_sd = 0; +static bool g_sd_mounted = false; +static bool g_sd_initialized = false; +static bool g_ahb_redirect_enabled = false; -int mount_sd(void) { - if (mounted_sd) { - return 1; +bool mount_sd(void) +{ + /* Already mounted. */ + if (g_sd_mounted) + return true; + + /* Enable AHB redirection if necessary. */ + if (!g_ahb_redirect_enabled) { + mc_enable_ahb_redirect(); + g_ahb_redirect_enabled = true; } - if (f_mount(&sd_fs, "", 1) == FR_OK) { - printk("Mounted SD card!\n"); - mounted_sd = 1; + + if (!g_sd_initialized) { + /* Initialize SD. */ + if (sdmmc_device_sd_init(&g_sd_device, &g_sd_sdmmc, SDMMC_BUS_WIDTH_4BIT, SDMMC_SPEED_SDR104)) + { + g_sd_initialized = true; + + /* Mount SD. */ + if (f_mount(&sd_fs, "", 1) == FR_OK) { + printk("Mounted SD card!\n"); + g_sd_mounted = true; + } + } + else + fatal_error("Failed to initialize the SD card!.\n"); } - return mounted_sd; + + return g_sd_mounted; } -size_t read_from_file(void *dst, size_t dst_size, const char *filename) { - if (!mounted_sd && mount_sd() == 0) { - return 0; +void unmount_sd(void) +{ + if (g_sd_mounted) + { + f_mount(NULL, "", 1); + sdmmc_device_finish(&g_sd_device); + g_sd_mounted = false; } +} +uint32_t get_file_size(const char *filename) +{ + /* SD card hasn't been mounted yet. */ + if (!g_sd_mounted) + return 0; + + /* Open the file for reading. */ FIL f; - if (f_open(&f, filename, FA_READ) != FR_OK) { + if (f_open(&f, filename, FA_READ) != FR_OK) return 0; - } + + /* Get the file size. */ + uint32_t file_size = f_size(&f); + + /* Close the file. */ + f_close(&f); + + return file_size; +} - UINT br; +int read_from_file(void *dst, uint32_t dst_size, const char *filename) +{ + /* SD card hasn't been mounted yet. */ + if (!g_sd_mounted) + return 0; + + /* Open the file for reading. */ + FIL f; + if (f_open(&f, filename, FA_READ) != FR_OK) + return 0; + + /* Sync. */ + f_sync(&f); + + /* Read from file. */ + UINT br = 0; int res = f_read(&f, dst, dst_size, &br); f_close(&f); - return res == FR_OK ? (int)br : 0; + return (res == FR_OK) ? (int)br : 0; } + +int write_to_file(void *src, uint32_t src_size, const char *filename) +{ + /* SD card hasn't been mounted yet. */ + if (!g_sd_mounted) + return 0; + + /* Open the file for writing. */ + FIL f; + if (f_open(&f, filename, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) + return 0; + + /* Sync. */ + f_sync(&f); + + /* Write to file. */ + UINT bw = 0; + int res = f_write(&f, src, src_size, &bw); + f_close(&f); + + return (res == FR_OK) ? (int)bw : 0; +} \ No newline at end of file diff --git a/fusee/fusee-primary/src/fs_utils.h b/fusee/fusee-primary/src/fs_utils.h index 2f71275ff..322e8b9a8 100644 --- a/fusee/fusee-primary/src/fs_utils.h +++ b/fusee/fusee-primary/src/fs_utils.h @@ -1,9 +1,19 @@ #ifndef FUSEE_FS_UTILS_H #define FUSEE_FS_UTILS_H -#include "utils.h" -#include "sdmmc.h" +#include +#include -size_t read_from_file(void *dst, size_t dst_size, const char *filename); +#include "sdmmc/sdmmc.h" +#include "utils.h" + +sdmmc_t g_sd_sdmmc; +sdmmc_device_t g_sd_device; + +bool mount_sd(void); +void unmount_sd(void); +uint32_t get_file_size(const char *filename); +int read_from_file(void *dst, uint32_t dst_size, const char *filename); +int write_to_file(void *src, uint32_t src_size, const char *filename); #endif diff --git a/fusee/fusee-primary/src/fuse.c b/fusee/fusee-primary/src/fuse.c index 0999fc672..2bdfa3740 100644 --- a/fusee/fusee-primary/src/fuse.c +++ b/fusee/fusee-primary/src/fuse.c @@ -1,3 +1,5 @@ +#include +#include #include #include "hwinit.h" diff --git a/fusee/fusee-primary/src/fuse.h b/fusee/fusee-primary/src/fuse.h index 1dcbc8d60..989f564bf 100644 --- a/fusee/fusee-primary/src/fuse.h +++ b/fusee/fusee-primary/src/fuse.h @@ -1,9 +1,6 @@ #ifndef FUSEE_FUSE_H #define FUSEE_FUSE_H -#include -#include - typedef struct { uint32_t FUSE_CTRL; uint32_t FUSE_REG_ADDR; diff --git a/fusee/fusee-primary/src/gpio.c b/fusee/fusee-primary/src/gpio.c index fd44a0f67..cfa2f38be 100644 --- a/fusee/fusee-primary/src/gpio.c +++ b/fusee/fusee-primary/src/gpio.c @@ -3,18 +3,9 @@ #include #include "gpio.h" +#include "utils.h" #include "lib/printk.h" -enum tegra_gpio_shifts { - GPIO_BANK_SHIFT = 5, - GPIO_PORT_SHIFT = 3, -}; - -enum tegra_gpio_masks { - GPIO_PORT_MASK = 0x3, - GPIO_PIN_MASK = 0x7, -}; - /** * Returns a GPIO bank object that corresponds to the given GPIO pin, * which can be created using the TEGRA_GPIO macro or passed from the name macro. @@ -22,35 +13,31 @@ enum tegra_gpio_masks { * @param pin The GPIO to get the bank for. * @return The GPIO bank object to use for working with the given bank. */ -static volatile struct tegra_gpio_bank *gpio_get_bank(enum tegra_named_gpio pin) +static volatile tegra_gpio_bank_t *gpio_get_bank(uint32_t pin) { - volatile struct tegra_gpio *gpio = gpio_get_regs(); - int bank_number = pin >> GPIO_BANK_SHIFT; + volatile tegra_gpio_t *gpio = gpio_get_regs(); + uint32_t bank_number = pin >> GPIO_BANK_SHIFT; return &gpio->bank[bank_number]; } - /** * @return the port number for working with the given GPIO. */ -static volatile int gpio_get_port(enum tegra_named_gpio pin) +static volatile uint32_t gpio_get_port(uint32_t pin) { return (pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK; } - /** * @return a mask to be used to work with the given GPIO */ -static volatile uint32_t gpio_get_mask(enum tegra_named_gpio pin) +static volatile uint32_t gpio_get_mask(uint32_t pin) { uint32_t pin_number = pin & GPIO_PIN_MASK; return (1 << pin_number); } - - /** * Performs a simple GPIO configuration operation. * @@ -58,7 +45,7 @@ static volatile uint32_t gpio_get_mask(enum tegra_named_gpio pin) * @param should_be_set True iff the relevant bit should be set; or false if it should be cleared. * @param offset The offset into a gpio_bank structure */ -static void gpio_simple_register_set(enum tegra_named_gpio pin, bool should_be_set, size_t offset) +static void gpio_simple_register_set(uint32_t pin, bool should_be_set, uint32_t offset) { // Retrieve the register set that corresponds to the given pin and offset. uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset; @@ -66,10 +53,9 @@ static void gpio_simple_register_set(enum tegra_named_gpio pin, bool should_be_s // Figure out the offset into the cluster, // and the mask to be used. - int port = gpio_get_port(pin); + uint32_t port = gpio_get_port(pin); uint32_t mask = gpio_get_mask(pin); - // Set or clear the bit, as appropriate. if (should_be_set) cluster[port] |= mask; @@ -77,7 +63,6 @@ static void gpio_simple_register_set(enum tegra_named_gpio pin, bool should_be_s cluster[port] &= ~mask; } - /** * Performs a simple GPIO configuration operation. * @@ -85,7 +70,7 @@ static void gpio_simple_register_set(enum tegra_named_gpio pin, bool should_be_s * @param should_be_set True iff the relevant bit should be set; or false if it should be cleared. * @param offset The offset into a gpio_bank structure */ -static bool gpio_simple_register_get(enum tegra_named_gpio pin, size_t offset) +static bool gpio_simple_register_get(uint32_t pin, uint32_t offset) { // Retrieve the register set that corresponds to the given pin and offset. uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset; @@ -93,57 +78,53 @@ static bool gpio_simple_register_get(enum tegra_named_gpio pin, size_t offset) // Figure out the offset into the cluster, // and the mask to be used. - int port = gpio_get_port(pin); + uint32_t port = gpio_get_port(pin); uint32_t mask = gpio_get_mask(pin); // Convert the given value to a boolean. return !!(cluster[port] & mask); } - /** * Configures a given pin as either GPIO or SFIO. * * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. * @param mode The relevant mode. */ -void gpio_configure_mode(enum tegra_named_gpio pin, enum tegra_gpio_mode mode) +void gpio_configure_mode(uint32_t pin, uint32_t mode) { - gpio_simple_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(struct tegra_gpio_bank, config)); + gpio_simple_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(tegra_gpio_bank_t, config)); } - /** * Configures a given pin as either INPUT or OUPUT. * * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. * @param direction The relevant direction. */ -void gpio_configure_direction(enum tegra_named_gpio pin, enum tegra_gpio_direction dir) +void gpio_configure_direction(uint32_t pin, uint32_t dir) { - gpio_simple_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(struct tegra_gpio_bank, direction)); + gpio_simple_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(tegra_gpio_bank_t, direction)); } - /** * Drives a relevant GPIO pin as either HIGH or LOW. * * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. * @param mode The relevant mode. */ -void gpio_write(enum tegra_named_gpio pin, enum tegra_gpio_value value) +void gpio_write(uint32_t pin, uint32_t value) { - gpio_simple_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(struct tegra_gpio_bank, out)); + gpio_simple_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(tegra_gpio_bank_t, out)); } - /** * Drives a relevant GPIO pin as either HIGH or LOW. * * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. * @param mode The relevant mode. */ -enum tegra_gpio_value gpio_read(enum tegra_named_gpio pin) +uint32_t gpio_read(uint32_t pin) { - return gpio_simple_register_get(pin, offsetof(struct tegra_gpio_bank, in)); + return gpio_simple_register_get(pin, offsetof(tegra_gpio_bank_t, in)); } diff --git a/fusee/fusee-primary/src/gpio.h b/fusee/fusee-primary/src/gpio.h index 581906c8b..b3da7dc56 100644 --- a/fusee/fusee-primary/src/gpio.h +++ b/fusee/fusee-primary/src/gpio.h @@ -1,19 +1,14 @@ -/* - * Struct defintiions lifted from NVIDIA sample code. - * (C) Copyright 2013-2015 NVIDIA Corporation - * - * adapted for Fusée by Kate Temkin -#include -#include "utils.h" - -enum tegra_gpio_port { +#define TEGRA_GPIO_PORTS 4 +#define TEGRA_GPIO_BANKS 8 +#define GPIO_BANK_SHIFT 5 +#define GPIO_PORT_SHIFT 3 +#define GPIO_PORT_MASK 0x03 +#define GPIO_PIN_MASK 0x07 + +typedef enum { TEGRA_GPIO_PORT_A = 0, TEGRA_GPIO_PORT_B = 1, TEGRA_GPIO_PORT_C = 2, @@ -46,25 +41,9 @@ enum tegra_gpio_port { TEGRA_GPIO_PORT_DD = 29, TEGRA_GPIO_PORT_EE = 30, TEGRA_GPIO_PORT_FF = 31, -}; +} tegra_gpio_port; -/** - * Convenince macro for computing a GPIO port number. - */ -#define TEGRA_GPIO(port, offset) \ - ((TEGRA_GPIO_PORT_##port * 8) + offset) - -/* - * The Tegra210 GPIO controller has 256 GPIOS in 8 banks of 4 ports, - * each with 8 GPIOs. - */ -enum { - TEGRA_GPIO_PORTS = 4, /* number of ports per bank */ - TEGRA_GPIO_BANKS = 8, /* number of banks */ -}; - -/* GPIO Controller registers for a single bank */ -struct tegra_gpio_bank { +typedef struct { uint32_t config[TEGRA_GPIO_PORTS]; uint32_t direction[TEGRA_GPIO_PORTS]; uint32_t out[TEGRA_GPIO_PORTS]; @@ -81,94 +60,40 @@ struct tegra_gpio_bank { uint32_t masked_int_enable[TEGRA_GPIO_PORTS]; uint32_t masked_int_level[TEGRA_GPIO_PORTS]; uint32_t masked_int_clear[TEGRA_GPIO_PORTS]; -}; +} tegra_gpio_bank_t; +typedef struct { + tegra_gpio_bank_t bank[TEGRA_GPIO_BANKS]; +} tegra_gpio_t; -/** - * Representation of Tegra GPIO controllers. - */ -struct tegra_gpio { - struct tegra_gpio_bank bank[TEGRA_GPIO_BANKS]; -}; - -/** - * GPIO pins that have a more detailed functional name, - * specialized for the Switch. - */ -enum tegra_named_gpio { - GPIO_MICROSD_CARD_DETECT = TEGRA_GPIO(Z, 1), - GPIO_MICROSD_WRITE_PROTECT = TEGRA_GPIO(Z, 4), - GPIO_MICROSD_SUPPLY_ENABLE = TEGRA_GPIO(E, 4), -}; - - -/** - * Mode select for GPIO or SFIO. - */ -enum tegra_gpio_mode { - GPIO_MODE_GPIO = 0, - GPIO_MODE_SFIO = 1 -}; - - -/** - * GPIO direction values - */ -enum tegra_gpio_direction { - GPIO_DIRECTION_INPUT = 0, - GPIO_DIRECTION_OUTPUT = 1 -}; - - -/** - * Active-high GPIO logic - */ -enum tegra_gpio_value { - GPIO_LEVEL_LOW = 0, - GPIO_LEVEL_HIGH = 1 -}; - - -/** - * Utility function that grabs the Tegra pinmux registers. - */ -static inline struct tegra_gpio *gpio_get_regs(void) +static inline volatile tegra_gpio_t *gpio_get_regs(void) { - return (struct tegra_gpio *)0x6000d000; + return (volatile tegra_gpio_t *)0x6000D000; } -/** - * Configures a given pin as either GPIO or SFIO. - * - * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. - * @param mode The relevant mode. - */ -void gpio_configure_mode(enum tegra_named_gpio pin, enum tegra_gpio_mode mode); +#define TEGRA_GPIO(port, offset) \ + ((TEGRA_GPIO_PORT_##port * 8) + offset) +/* Mode select */ +#define GPIO_MODE_GPIO 0 +#define GPIO_MODE_SFIO 1 -/** - * Configures a given pin as either INPUT or OUPUT. - * - * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. - * @param direction The relevant direction. - */ -void gpio_configure_direction(enum tegra_named_gpio pin, enum tegra_gpio_direction dir); +/* Direction */ +#define GPIO_DIRECTION_INPUT 0 +#define GPIO_DIRECTION_OUTPUT 1 +/* Level */ +#define GPIO_LEVEL_LOW 0 +#define GPIO_LEVEL_HIGH 1 -/** - * Drives a relevant GPIO pin as either HIGH or LOW. - * - * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. - * @param mode The relevant value. - */ -void gpio_write(enum tegra_named_gpio pin, enum tegra_gpio_value value); +/* Named GPIOs */ +#define GPIO_MICROSD_CARD_DETECT TEGRA_GPIO(Z, 1) +#define GPIO_MICROSD_WRITE_PROTECT TEGRA_GPIO(Z, 4) +#define GPIO_MICROSD_SUPPLY_ENABLE TEGRA_GPIO(E, 4) -/** - * Drives a relevant GPIO pin as either HIGH or LOW. - * - * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. - * @param mode The relevant mode. - */ -enum tegra_gpio_value gpio_read(enum tegra_named_gpio pin); +void gpio_configure_mode(uint32_t pin, uint32_t mode); +void gpio_configure_direction(uint32_t pin, uint32_t dir); +void gpio_write(uint32_t pin, uint32_t value); +uint32_t gpio_read(uint32_t pin); #endif diff --git a/fusee/fusee-primary/src/lib/fatfs/diskio.c b/fusee/fusee-primary/src/lib/fatfs/diskio.c index 3b32b59ee..c2840973b 100644 --- a/fusee/fusee-primary/src/lib/fatfs/diskio.c +++ b/fusee/fusee-primary/src/lib/fatfs/diskio.c @@ -9,70 +9,18 @@ #include #include -#include "diskio.h" /* FatFs lower layer API */ -#include "../../sdmmc.h" -#include "../../hwinit.h" - -static bool g_ahb_redirect_enabled = false; - -/* Global sd struct. */ -struct mmc g_sd_mmc = {0}; -static bool g_sd_initialized = false; - -int initialize_sd_mmc(void) { - if (!g_ahb_redirect_enabled) { - mc_enable_ahb_redirect(); - g_ahb_redirect_enabled = true; - } - - if (!g_sd_initialized) { - int rc = sdmmc_init(&g_sd_mmc, SWITCH_MICROSD, false); - if (rc == 0) { - g_sd_initialized = true; - return 0; - } else { - return rc; - } - } else { - return 0; - } -} - -/* -Uncomment if needed: -static struct mmc nand_mmc = {0}; -static bool g_nand_initialized = false; - -int initialize_nand_mmc(void) { - if (!g_ahb_redirect_enabled) { - mc_enable_ahb_redirect(); - g_ahb_redirect_enabled = true; - } - - if (!g_nand_initialized) { - int rc = sdmmc_init(&g_sd_mmc, SWITCH_EMMC); - if (rc == 0) { - g_nand_initialized = true; - return 0; - } else { - return rc; - } - } else { - return 0; - } -} - -*/ +#include "diskio.h" /* FatFs lower layer API */ +#include "../../fs_utils.h" /*-----------------------------------------------------------------------*/ /* Get Drive Status */ /*-----------------------------------------------------------------------*/ DSTATUS disk_status ( - BYTE pdrv /* Physical drive nmuber to identify the drive */ + BYTE pdrv /* Physical drive nmuber to identify the drive */ ) { - return 0; + return 0; } @@ -82,15 +30,10 @@ DSTATUS disk_status ( /*-----------------------------------------------------------------------*/ DSTATUS disk_initialize ( - BYTE pdrv /* Physical drive nmuber to identify the drive */ + BYTE pdrv /* Physical drive nmuber to identify the drive */ ) { - switch (pdrv) { - case 0: - return initialize_sd_mmc() == 0 ? 0 : STA_NOINIT; - default: - return STA_NODISK; - } + return 0; } @@ -100,18 +43,18 @@ DSTATUS disk_initialize ( /*-----------------------------------------------------------------------*/ DRESULT disk_read ( - BYTE pdrv, /* Physical drive nmuber to identify the drive */ - BYTE *buff, /* Data buffer to store read data */ - DWORD sector, /* Start sector in LBA */ - UINT count /* Number of sectors to read */ + BYTE pdrv, /* Physical drive nmuber to identify the drive */ + BYTE *buff, /* Data buffer to store read data */ + DWORD sector, /* Start sector in LBA */ + UINT count /* Number of sectors to read */ ) { - switch (pdrv) { - case 0: - return sdmmc_read(&g_sd_mmc, buff, sector, count) == 0 ? RES_OK : RES_ERROR; - default: - return RES_PARERR; - } + switch (pdrv) { + case 0: + return sdmmc_device_read(&g_sd_device, sector, count, (void *)buff) ? RES_OK : RES_ERROR; + default: + return RES_PARERR; + } } @@ -121,18 +64,18 @@ DRESULT disk_read ( /*-----------------------------------------------------------------------*/ DRESULT disk_write ( - BYTE pdrv, /* Physical drive nmuber to identify the drive */ - const BYTE *buff, /* Data to be written */ - DWORD sector, /* Start sector in LBA */ - UINT count /* Number of sectors to write */ + BYTE pdrv, /* Physical drive nmuber to identify the drive */ + const BYTE *buff, /* Data to be written */ + DWORD sector, /* Start sector in LBA */ + UINT count /* Number of sectors to write */ ) { - switch (pdrv) { - case 0: - return sdmmc_write(&g_sd_mmc, buff, sector, count) == 0 ? RES_OK : RES_ERROR; - default: - return RES_PARERR; - } + switch (pdrv) { + case 0: + return sdmmc_device_write(&g_sd_device, sector, count, (void *)buff) ? RES_OK : RES_ERROR; + default: + return RES_PARERR; + } } @@ -142,11 +85,11 @@ DRESULT disk_write ( /*-----------------------------------------------------------------------*/ DRESULT disk_ioctl ( - BYTE pdrv, /* Physical drive nmuber (0..) */ - BYTE cmd, /* Control code */ - void *buff /* Buffer to send/receive control data */ + BYTE pdrv, /* Physical drive nmuber (0..) */ + BYTE cmd, /* Control code */ + void *buff /* Buffer to send/receive control data */ ) { - return 0; + return RES_OK; } diff --git a/fusee/fusee-primary/src/main.c b/fusee/fusee-primary/src/main.c index 239251ad9..42b1c7255 100644 --- a/fusee/fusee-primary/src/main.c +++ b/fusee/fusee-primary/src/main.c @@ -7,7 +7,7 @@ #include "fs_utils.h" #include "stage2.h" #include "chainloader.h" -#include "sdmmc.h" +#include "sdmmc/sdmmc.h" #include "lib/fatfs/ff.h" #include "lib/printk.h" #include "display/video_fb.h" @@ -89,10 +89,14 @@ static void setup_env(void) { /* Set up the exception handlers. */ setup_exception_handlers(); + + /* Mount the SD card. */ + mount_sd(); } static void cleanup_env(void) { - f_unmount(""); + /* Unmount the SD card. */ + unmount_sd(); display_enable_backlight(false); display_end(); @@ -108,12 +112,13 @@ int main(void) { const char *stage2_path; stage2_args_t *stage2_args; uint32_t stage2_version = 0; - extern struct mmc g_sd_mmc; - sdmmc_set_loglevel(2); + /* Set the SDMMC's driver logging level. */ + sdmmc_set_log_level(SDMMC_LOG_INFO); + /* Initialize the display, console, etc. */ setup_env(); - + /* Say hello. */ printk("Welcome to Atmosph\xe8re Fus\xe9" "e!\n"); printk("Using color linear framebuffer at 0x%p!\n", g_framebuffer); @@ -137,7 +142,7 @@ 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_mmc, &g_sd_mmc, sizeof(g_sd_mmc)); + memcpy(&stage2_args->sd_sdmmc, &g_sd_sdmmc, sizeof(g_sd_sdmmc)); strcpy(stage2_args->bct0, bct0); g_chainloader_argc = 2; diff --git a/fusee/fusee-primary/src/pad_control.h b/fusee/fusee-primary/src/pad_control.h deleted file mode 100644 index a1ba93ae8..000000000 --- a/fusee/fusee-primary/src/pad_control.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Fusée pad control code - * ~ktemkin - */ - -#ifndef __FUSEE_PADCTL_H__ -#define __FUSEE_PADCTL_H__ - -#include "utils.h" - -/** - * Registers in the Misc Pad control region - */ -struct tegra_padctl { - /* TODO: support registers before? */ - uint32_t sdmmc1_control; - uint32_t sdmmc3_control; - uint32_t sdmmc2_control; - uint32_t sdmmc4_control; - - /* TODO: support registers after? */ - uint8_t _todo[656]; - - uint32_t vgpio_gpio_mux_sel; -}; - -/** - * Masks for Pad Control registers - */ -enum tegra_padctl_masks { - - /* SDMMC1 */ - PADCTL_SDMMC1_DEEP_LOOPBACK = (1 << 0), - - /* SDMMC3 */ - PADCTL_SDMMC3_DEEP_LOOPBACK = (1 << 0), - - /* SDMMC2 */ - PADCTL_SDMMC2_ENABLE_DATA_IN = (0xFF << 8), - PADCTL_SDMMC2_ENABLE_CLK_IN = (0x3 << 4), - PADCTL_SDMMC2_DEEP_LOOPBACK = (1 << 0), - - /* SDMMC4 */ - PADCTL_SDMMC4_ENABLE_DATA_IN = (0xFF << 8), - PADCTL_SDMMC4_ENABLE_CLK_IN = (0x3 << 4), - PADCTL_SDMMC4_DEEP_LOOPBACK = (1 << 0), - - /* VGPIO/GPIO */ - PADCTL_SDMMC1_CD_SOURCE = (1 << 0), - PADCTL_SDMMC1_WP_SOURCE = (1 << 1), - PADCTL_SDMMC3_CD_SOURCE = (1 << 2), - PADCTL_SDMMC3_WP_SOURCE = (1 << 3), - - -}; - - -/** - * Utility function that grabs the Tegra PADCTL registers. - */ -static inline struct tegra_padctl *padctl_get_regs(void) -{ - return (struct tegra_padctl *)0x700008d4; -} - - -#endif diff --git a/fusee/fusee-primary/src/panic_color.h b/fusee/fusee-primary/src/panic_color.h index a310b139b..a87cfdeb6 100644 --- a/fusee/fusee-primary/src/panic_color.h +++ b/fusee/fusee-primary/src/panic_color.h @@ -1,5 +1,5 @@ -#ifndef EXOSPHERE_PANIC_COLOR_H -#define EXOSPHERE_PANIC_COLOR_H +#ifndef FUSEE_PANIC_COLOR_H +#define FUSEE_PANIC_COLOR_H #define COLOR_0 0x00F00003 #define COLOR_1 0x0F000003 diff --git a/fusee/fusee-primary/src/pinmux.h b/fusee/fusee-primary/src/pinmux.h index d583eaf9a..d7131d1d5 100644 --- a/fusee/fusee-primary/src/pinmux.h +++ b/fusee/fusee-primary/src/pinmux.h @@ -1,14 +1,22 @@ -#ifndef __FUSEE_PINMUX_H__ -#define __FUSEE_PINMUX_H__ +#ifndef FUSEE_PINMUX_H +#define FUSEE_PINMUX_H -#include -#include -#include "utils.h" +#define PINMUX_TRISTATE (1 << 4) +#define PINMUX_PARKED (1 << 5) +#define PINMUX_INPUT (1 << 6) +#define PINMUX_PULL_NONE (0 << 2) +#define PINMUX_PULL_DOWN (1 << 2) +#define PINMUX_PULL_UP (2 << 2) +#define PINMUX_SELECT_FUNCTION0 0 +#define PINMUX_SELECT_FUNCTION1 1 +#define PINMUX_SELECT_FUNCTION2 2 +#define PINMUX_SELECT_FUNCTION3 3 +#define PINMUX_DRIVE_1X (0 << 13) +#define PINMUX_DRIVE_2X (1 << 13) +#define PINMUX_DRIVE_3X (2 << 13) +#define PINMUX_DRIVE_4X (3 << 13) -/** - * Pinmux structures. - */ -struct tegra_pinmux { +typedef struct { uint32_t sdmmc1_clk; uint32_t sdmmc1_cmd; uint32_t sdmmc1_dat3; @@ -174,48 +182,11 @@ struct tegra_pinmux { uint32_t pz3; uint32_t pz4; uint32_t pz5; -}; +} tegra_pinmux_t; -/** - * Constants for use of the Tegra Pinmux. - */ -enum tegra_pinmux_constants { - - /* Tristate (output buffer) control */ - PINMUX_TRISTATE = (1 << 4), - - /* Park control */ - PINMUX_PARKED = (1 << 5), - - /* Input control */ - PINMUX_INPUT = (1 << 6), - - /* Pull resistors */ - PINMUX_PULL_NONE = (0 << 2), - PINMUX_PULL_DOWN = (1 << 2), - PINMUX_PULL_UP = (2 << 2), - - /* Function select */ - PINMUX_SELECT_FUNCTION0 = 0, - PINMUX_SELECT_FUNCTION1 = 1, - PINMUX_SELECT_FUNCTION2 = 2, - PINMUX_SELECT_FUNCTION3 = 3, - - /* Drive */ - PINMUX_DRIVE_1X = (0x0 << 13), - PINMUX_DRIVE_2X = (0x1 << 13), - PINMUX_DRIVE_3X = (0x2 << 13), - PINMUX_DRIVE_4X = (0x3 << 13), -}; - - -/** - * Utility function that grabs the Tegra pinmux registers. - */ -static inline struct tegra_pinmux *pinmux_get_regs(void) +static inline volatile tegra_pinmux_t *pinmux_get_regs(void) { - return (struct tegra_pinmux *)0x70003000; + return (volatile tegra_pinmux_t *)0x70003000; } - #endif diff --git a/fusee/fusee-primary/src/pmc.h b/fusee/fusee-primary/src/pmc.h index 1a1a8b30a..4c10d1a0e 100644 --- a/fusee/fusee-primary/src/pmc.h +++ b/fusee/fusee-primary/src/pmc.h @@ -1,37 +1,27 @@ -#ifndef __FUSEE_PMC_H__ -#define __FUSEE_PMC_H__ - -#include "utils.h" - -#define PMC_BASE 0x7000E400 - +#ifndef FUSEE_PMC_H +#define FUSEE_PMC_H /* TODO: get rid of these defines; use the struct instead */ +#define PMC_BASE 0x7000E400 + +#define PMC_CONTROL_SDMMC1 (1 << 12) +#define PMC_CONTROL_SDMMC3 (1 << 13) +#define PMC_CONTROL_SDMMC4 (1 << 14) + #define APBDEV_PMC_CONTROL MAKE_REG32(PMC_BASE + 0x00) - #define APBDEV_PMC_DPD_ENABLE_0 MAKE_REG32(PMC_BASE + 0x24) - #define APBDEV_PMC_PWRGATE_TOGGLE_0 MAKE_REG32(PMC_BASE + 0x30) #define APBDEV_PMC_PWRGATE_STATUS_0 MAKE_REG32(PMC_BASE + 0x38) - #define APBDEV_PMC_SCRATCH0_0 MAKE_REG32(PMC_BASE + 0x50) - #define APBDEV_PMC_CRYPTO_OP_0 MAKE_REG32(PMC_BASE + 0xF4) - #define APBDEV_PM_0 MAKE_REG32(PMC_BASE + 0x14) #define APBDEV_PMC_WAKE2_STATUS_0 MAKE_REG32(PMC_BASE + 0x168) #define APBDEV_PMC_RST_STATUS_0 MAKE_REG32(PMC_BASE + 0x1B4) #define APBDEV_PMC_CNTRL2_0 MAKE_REG32(PMC_BASE + 0x440) - #define APBDEV_PMC_SCRATCH43_0 MAKE_REG32(PMC_BASE + 0x22C) - #define APBDEV_PMC_SCRATCH200_0 MAKE_REG32(PMC_BASE + 0x840) -/** - * Definitions of the Tegra PMC. - * NOTE: Incomplete, do not use - */ -struct tegra_pmc { +typedef struct { uint32_t cntrl; uint32_t sec_disable; uint32_t pmc_swrst; @@ -297,23 +287,11 @@ struct tegra_pmc { uint32_t scratch118; uint32_t scratch119; uint32_t scratch1_eco; -}; +} tegra_pmc_t; -enum tegra_pmc_masks { - /* NO_IOPOWER, power detect, ect. */ - PMC_CONTROL_SDMMC1 = (1 << 12), - PMC_CONTROL_SDMMC3 = (1 << 13), - PMC_CONTROL_SDMMC4 = (1 << 14), -}; - - -/** - * Utility function that grabs the Tegra PMC registers. - */ -static inline volatile struct tegra_pmc *pmc_get_regs(void) +static inline volatile tegra_pmc_t *pmc_get_regs(void) { - return (volatile struct tegra_pmc *)0x7000E400; + return (volatile tegra_pmc_t *)0x7000E400; } - #endif diff --git a/fusee/fusee-primary/src/sdmmc.c b/fusee/fusee-primary/src/sdmmc.c deleted file mode 100644 index cc57b0513..000000000 --- a/fusee/fusee-primary/src/sdmmc.c +++ /dev/null @@ -1,3748 +0,0 @@ -/** - * Fusée SD/MMC driver for the Switch - * ~ktemkin - */ - -#include -#include -#include -#include - -#include "lib/driver_utils.h" -#include "sdmmc.h" -#include "car.h" -#include "pinmux.h" -#include "timers.h" -#include "apb_misc.h" -#include "gpio.h" -#include "supplies.h" -#include "pmc.h" -#include "pad_control.h" -#include "apb_misc.h" - -#define TEGRA_SDMMC_BASE (0x700B0000) -#define TEGRA_SDMMC_SIZE (0x200) - - -/** - * Map of tegra SDMMC registers - */ -struct 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; - uint32_t response[0x4]; - 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 _0x56[0x2]; - 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[0x4]; - 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[0x20]; - 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, - -}; - -/** - * Lengths of SD command responses - */ -enum sdmmc_constants { - /* Bytes in a LEN136 response */ - MMC_RESPONSE_SIZE_LEN136 = 15, -}; - -/** - * SDMMC clock divider constants - */ -enum sdmmc_clock_dividers { - - /* Clock dividers: SD */ - MMC_CLOCK_DIVIDER_SDR12 = 31, // 16.5, from the TRM table - MMC_CLOCK_DIVIDER_SDR25 = 15, // 8.5, from the table - MMC_CLOCK_DIVIDER_SDR50 = 7, // 4.5, from the table - MMC_CLOCK_DIVIDER_SDR104 = 2, // 2, from the table - - /* Clock dividers: MMC */ - MMC_CLOCK_DIVIDER_HS26 = 30, // 16, from the TRM table - MMC_CLOCK_DIVIDER_HS52 = 14, // 8, from the table - -#if 0 - // TODO: Figure out why PLLC4_OUT2_LJ doesn't work, most likely need to be enabled in hwinit - MMC_CLOCK_DIVIDER_HS200 = 0, // 1 -- NOTE THIS IS WITH RESPECT TO PLLC4_OUT2_LJ - MMC_CLOCK_DIVIDER_HS400 = 0, // 1 -- NOTE THIS IS WITH RESPECT TO PLLC4_OUT2_LJ -#else - MMC_CLOCK_DIVIDER_HS200 = 3, - MMC_CLOCK_DIVIDER_HS400 = 3, -#endif - - /* Clock dividers: Legacy 12 MHz timer */ - MMC_CLOCK_DIVIDER_LEGACY = 66, // 34 - to get 12 MHz out of 408 MHz -}; - -/** - * SDMMC clock divider constants - */ -enum sdmmc_clock_sources { - - /* Clock sources: SD */ - MMC_CLOCK_SOURCE_SDR12 = CLK_SOURCE_SDMMC1_PLLP_OUT0, // PLLP - MMC_CLOCK_SOURCE_SDR25 = CLK_SOURCE_SDMMC1_PLLP_OUT0, - MMC_CLOCK_SOURCE_SDR50 = CLK_SOURCE_SDMMC1_PLLP_OUT0, - MMC_CLOCK_SOURCE_SDR104 = CLK_SOURCE_SDMMC1_PLLP_OUT0, - - /* Clock sources: MMC */ - MMC_CLOCK_SOURCE_HS26 = CLK_SOURCE_SDMMC4_PLLP_OUT0, // PLLP - MMC_CLOCK_SOURCE_HS52 = CLK_SOURCE_SDMMC4_PLLP_OUT0, - -#if 0 - // TODO: Figure out why PLLC4_OUT2_LJ doesn't work, most likely need to be enabled in hwinit - MMC_CLOCK_SOURCE_HS200 = CLK_SOURCE_SDMMC4_PLLC4_OUT2_LJ, // PLLC4_OUT2_LJ - MMC_CLOCK_SOURCE_HS400 = CLK_SOURCE_SDMMC4_PLLC4_OUT2_LJ, -#else - // For the time being, use PLLP_OUT0 - MMC_CLOCK_SOURCE_HS200 = CLK_SOURCE_SDMMC4_PLLP_OUT0, - MMC_CLOCK_SOURCE_HS400 = CLK_SOURCE_SDMMC4_PLLP_OUT0, -#endif - - /* Clock sources: Legacy 12 MHz timer */ - MMC_CLOCK_SOURCE_LEGACY = CLK_SOURCE_SDMMC_LEGACY_PLLP_OUT0, -}; - -/** - * SDMMC response sanity checks - * see the standard for when these should be used - */ -enum sdmmc_response_checks { - MMC_CHECKS_NONE = 0, - MMC_CHECKS_CRC = (1 << 3), - MMC_CHECKS_INDEX = (1 << 4), - MMC_CHECKS_ALL = (1 << 4) | (1 << 3), -}; - -/** - * General masks for SDMMC registers. - */ -enum sdmmc_register_bits { - - /* Present state register */ - MMC_COMMAND_INHIBIT = (1 << 0), - MMC_DATA_INHIBIT = (1 << 1), - MMC_BUFFER_WRITE_ENABLE = (1 << 10), - MMC_BUFFER_READ_ENABLE = (1 << 11), - MMC_DAT0_LINE_STATE = (1 << 20), - MMC_READ_ACTIVE = (1 << 9), - MMC_WRITE_ACTIVE = (1 << 8), - - /* Block size register */ - MMC_DMA_BOUNDARY_MAXIMUM = (0x7 << 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_8K = (0x1 << 12), - MMC_DMA_BOUNDARY_4K = (0x0 << 12), - MMC_TRANSFER_BLOCK_512B = (0x200 << 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_AUTO_CMD_MASK = (0x3 << 2), - MMC_TRANSFER_AUTO_CMD12 = (0x1 << 2), - MMC_TRANSFER_AUTO_CMD23 = (0x2 << 2), - MMC_TRANSFER_AUTO_CMD = (0x3 << 2), - MMC_TRANSFER_CARD_TO_HOST = (1 << 4), - - /* Interrupt status */ - MMC_STATUS_COMMAND_COMPLETE = (1 << 0), - MMC_STATUS_TRANSFER_COMPLETE = (1 << 1), - MMC_STATUS_DMA_INTERRUPT = (1 << 3), - MMC_STATUS_BUFFER_READ_READY = (1 << 5), - MMC_STATUS_COMMAND_TIMEOUT = (1 << 16), - MMC_STATUS_COMMAND_CRC_ERROR = (1 << 17), - MMC_STATUS_COMMAND_END_BIT_ERROR = (1 << 18), - MMC_STATUS_COMMAND_INDEX_ERROR = (1 << 19), - - MMC_STATUS_ERROR_MASK = (0xF << 16), - - /* Clock control */ - MMC_CLOCK_CONTROL_CARD_CLOCK_ENABLE = (1 << 2), - MMC_CLOCK_CONTROL_FREQUENCY_MASK = (0x3FF << 6), - MMC_CLOCK_CONTROL_FREQUENCY_SHIFT = 8, - MMC_CLOCK_CONTROL_FREQUENCY_INIT = 0x1F, // generates 400kHz from the TRM dividers - MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH = 0x00, // passes through the CAR clock unmodified - - /* Host control */ - MMC_DMA_SELECT_MASK = (0x3 << 3), - MMC_DMA_SELECT_SDMA = (0x0 << 3), - MMC_HOST_BUS_WIDTH_MASK = (1 << 1) | (1 << 5), - MMC_HOST_BUS_WIDTH_4BIT = (1 << 1), - MMC_HOST_BUS_WIDTH_8BIT = (1 << 5), - MMC_HOST_ENABLE_HIGH_SPEED = (1 << 2), - - /* Host control 2 */ - MMC_HOST2_DRIVE_STRENGTH_MASK = (0x3 << 4), - MMC_HOST2_DRIVE_STRENGTH_B = (0x0 << 4), - MMC_HOST2_DRIVE_STRENGTH_A = (0x1 << 4), - MMC_HOST2_DRIVE_STRENGTH_C = (0x2 << 4), - MMC_HOST2_DRIVE_STRENGTH_D = (0x3 << 4), - MMC_HOST2_USE_1V8_SIGNALING = (1 << 3), - MMC_HOST2_EXECUTE_TUNING = (1 << 6), - MMC_HOST2_SAMPLING_CLOCK_ENABLED = (1 << 7), - MMC_HOST2_UHS_MODE_MASK = (0x7 << 3), - - /* Software reset */ - MMC_SOFT_RESET_FULL = (1 << 0), - MMC_SOFT_RESET_CMD = (1 << 1), - MMC_SOFT_RESET_DAT = (1 << 2), - - /* Vendor clock control */ - MMC_CLOCK_TAP_MASK = (0xFF << 16), - MMC_CLOCK_TAP_SDMMC1 = (0x04 << 16), - MMC_CLOCK_TAP_SDMMC4 = (0x00 << 16), - - MMC_CLOCK_TRIM_MASK = (0xFF << 24), - MMC_CLOCK_TRIM_SDMMC1 = (0x02 << 24), - MMC_CLOCK_TRIM_SDMMC4 = (0x08 << 24), - - MMC_CLOCK_PADPIPE_CLKEN_OVERRIDE = (1 << 3), - - /* Autocal configuration */ - MMC_AUTOCAL_PDPU_CONFIG_MASK = 0x7f7f, - MMC_AUTOCAL_PDPU_SDMMC1_1V8 = 0x7b7b, - MMC_AUTOCAL_PDPU_SDMMC1_3V3 = 0x7d00, - MMC_AUTOCAL_PDPU_SDMMC4_1V8 = 0x0505, - MMC_AUTOCAL_START = (1 << 31), - MMC_AUTOCAL_ENABLE = (1 << 29), - - /* Autocal status */ - MMC_AUTOCAL_ACTIVE = (1 << 31), - - /* Power control */ - MMC_POWER_CONTROL_VOLTAGE_MASK = (0x3 << 1), - MMC_POWER_CONTROL_VOLTAGE_SHIFT = 1, - MMC_POWER_CONTROL_POWER_ENABLE = (1 << 0), - - /* Capabilities register high */ - MMC_SDR50_REQUIRES_TUNING = (1 << 13), - - /* Vendor tuning control 0*/ - MMC_VENDOR_TUNING_TRIES_MASK = (0x7 << 13), - MMC_VENDOR_TUNING_TRIES_SHIFT = 13, - - MMC_VENDOR_TUNING_MULTIPLIER_MASK = (0x7F << 6), - MMC_VENDOR_TUNING_MULTIPLIER_UNITY = (1 << 6), - - MMC_VENDOR_TUNING_DIVIDER_MASK = (0x7 << 3), - - MMC_VENDOR_TUNING_SET_BY_HW = (1 << 17), - - /* Vendor tuning control 1*/ - MMC_VENDOR_TUNING_STEP_SIZE_SDR50_DEFAULT = (0 << 0), - MMC_VENDOR_TUNING_STEP_SIZE_SDR104_DEFAULT = (0 << 4), - - /* Vendor capability overrides */ - MMC_VENDOR_CAPABILITY_DQS_TRIM_MASK = (0x3f << 8), - MMC_VENDOR_CAPABILITY_DQS_TRIM_HS400 = (0x11 << 8), -}; - - -/** - * Represents the possible tuning modes for the X1 SDMMC controller. - */ -enum sdmmc_tuning_attempts { - MMC_VENDOR_TUNING_TRIES_40 = 0, - MMC_VENDOR_TUNING_TRIES_64 = 1, - MMC_VENDOR_TUNING_TRIES_128 = 2, - MMC_VENDOR_TUNING_TRIES_192 = 3, - MMC_VENDOR_TUNING_TRIES_256 = 4, - - /* Helpful aliases; values are from the TRM */ - MMC_VENDOR_TUNING_TRIES_SDR50 = 4, - MMC_VENDOR_TUNING_TRIES_SDR104 = 2, - MMC_VENDOR_TUNING_TRIES_HS200 = 2, - MMC_VENDOR_TUNING_TRIES_HS400 = 2, -}; - - -/* Constant map that converts from a MMC_VENDOR_TUNING_TRIES_* value to the number of tries. */ -static const int sdmmc_tuning_iterations[] = {40, 64, 128, 192, 256}; - -/** - * 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_GET_RELATIVE_ADDR = 3, - CMD_SET_DSR = 4, - CMD_TOGGLE_SLEEP_AWAKE = 5, - CMD_SWITCH_MODE = 6, - CMD_APP_SWITCH_WIDTH = 6, - CMD_TOGGLE_CARD_SELECT = 7, - CMD_SEND_EXT_CSD = 8, - CMD_SEND_IF_COND = 8, - CMD_SEND_CSD = 9, - CMD_SEND_CID = 10, - CMD_SWITCH_TO_LOW_VOLTAGE = 11, - 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, - CMD_SD_SEND_TUNING_BLOCK = 19, - CMD_MMC_SEND_TUNING_BLOCK = 21, - CMD_WRITE_SINGLE_BLOCK = 24, - CMD_WRITE_MULTIPLE_BLOCK = 25, - - CMD_APP_SEND_OP_COND = 41, - CMD_APP_SET_CARD_DETECT = 42, - CMD_APP_SEND_SCR = 51, - CMD_APP_COMMAND = 55, -}; - - -/** - * Fields that can be modified by CMD_SWITCH_MODE. - */ -enum sdmmc_switch_field { - /* Fields */ - MMC_PARTITION_CONFIG = 179, - MMC_BUS_WIDTH = 183, - MMC_HS_TIMING = 185, -}; - - - -/** - * String descriptions of each command. - */ -static const char *sdmmc_command_string[] = { - "CMD_GO_IDLE_OR_INIT", - "CMD_SEND_OPERATING_CONDITIONS", - "CMD_ALL_SEND_CID", - "CMD_SET_RELATIVE_ADDR", - "CMD_SET_DSR", - "CMD_TOGGLE_SLEEP_AWAKE", - "CMD_SWITCH_MODE", - "CMD_TOGGLE_CARD_SELECT", - "CMD_SEND_EXT_CSD/CMD_SEND_IF_COND", - "CMD_SEND_CSD", - "CMD_SEND_CID ", - "CMD_SWITCH_TO_LOW_VOLTAGE", - "CMD_STOP_TRANSMISSION", - "CMD_READ_STATUS", - "CMD_BUS_TEST", - "CMD_GO_INACTIVE", - "CMD_SET_BLKLEN", - "CMD_READ_SINGLE_BLOCK", - "CMD_READ_MULTIPLE_BLOCK", - "CMD_SD_SEND_TUNING_BLOCK", - "", - "CMD_MMC_SEND_TUNING_BLOCK", - "", - "", - "CMD_WRITE_SINGLE_BLOCK", - "CMD_WRITE_WRITE_BLOCK", -}; - - -/** - * SDMMC command argument numbers - */ -enum sdmmc_command_magic { - MMC_ENABLE_BOOT_INIT_MAGIC = 0xf0f0f0f0, - MMC_ENABLE_BOOT_ENABLE_MAGIC = 0xfffffffa, - - MMC_EMMC_OPERATING_COND_CAPACITY_MAGIC = 0x00ff8080, - - MMC_EMMC_OPERATING_COND_CAPACITY_MASK = 0x0fffffff, - MMC_EMMC_OPERATING_COND_BUSY = (0x04 << 28), - MMC_EMMC_OPERATING_COND_READY = (0x0c << 28), - MMC_EMMC_OPERATING_READINESS_MASK = (0x0f << 28), - - MMC_SD_OPERATING_COND_READY = (1 << 31), - MMC_SD_OPERATING_COND_HIGH_CAPACITY = (1 << 30), - MMC_SD_OPERATING_COND_ACCEPTS_1V8 = (1 << 24), - MMC_SD_OPERATING_COND_ACCEPTS_3V3 = (1 << 20), - - /* READ_STATUS responses */ - MMC_STATUS_MASK = (0xf << 9), - MMC_STATUS_PROGRAMMING = (0x7 << 9), - MMC_STATUS_READY_FOR_DATA = (0x1 << 8), - MMC_STATUS_CHECK_ERROR = (~0x0206BF7F), - - /* IF_COND components */ - MMC_IF_VOLTAGE_3V3 = (1 << 8), - MMC_IF_CHECK_PATTERN = 0xAA, - - /* Switch mode constants */ - SDMMC_SWITCH_MODE_MODE_SHIFT = 31, - SDMMC_SWITCH_MODE_ALL_FUNCTIONS_UNUSED = 0xFFFFFF, - SDMMC_SWITCH_MODE_FUNCTION_MASK = 0xF, - SDMMC_SWITCH_MODE_GROUP_SIZE_BITS = 4, - - SDMMC_SWITCH_MODE_MODE_QUERY = 0, - SDMMC_SWITCH_MODE_MODE_APPLY = 1, - SDMMC_SWITCH_MODE_ACCESS_MODE = 0, - SDMMC_SWITCH_MODE_NO_GROUP = -1, - - /* Misc constants */ - MMC_DEFAULT_BLOCK_ORDER = 9, - MMC_VOLTAGE_SWITCH_TIME = 5000, // 5ms - MMC_POST_CLOCK_DELAY = 1000, // 1ms - MMC_SPEED_MMC_OFFSET = 10, - - MMC_AUTOCAL_TIMEOUT = 10 * 1000, // 10ms - MMC_TUNING_TIMEOUT = 150 * 1000, // 150ms - MMC_TUNING_BLOCK_ORDER_4BIT = 6, - MMC_TUNING_BLOCK_ORDER_8BIT = 7, -}; - - -/** - * Version magic numbers for different CSD versions. - */ -enum sdmmc_csd_versions { - MMC_CSD_VERSION1 = 0, - MMC_CSD_VERSION2 = 1, -}; - - -/** - * Positions of different fields in various CSDs. - * May eventually be replaced with a bitfield struct, if we use enough of the CSDs. - */ -enum sdmmc_csd_extents { - - /* csd structure version */ - MMC_CSD_STRUCTURE_START = 126, - MMC_CSD_STRUCTURE_WIDTH = 2, - - /* read block length */ - MMC_CSD_V1_READ_BL_LENGTH_START = 80, - MMC_CSD_V1_READ_BL_LENGTH_WIDTH = 4, - -}; - - -/** - * Positions of the different fields in the Extended CSD. - */ -enum sdmmc_ext_csd_extents { - MMC_EXT_CSD_SIZE = 512, - - /* Hardware partition registers */ - MMC_EXT_CSD_PARTITION_SETTING_COMPLETE = 155, - MMC_EXT_CSD_PARTITION_SETTING_COMPLETED = (1 << 0), - - MMC_EXT_CSD_PARTITION_ATTRIBUTE = 156, - MMC_EXT_CSD_PARTITION_ENHANCED_ATTRIBUTE = 0x1f, - - MMC_EXT_CSD_PARTITION_SUPPORT = 160, - MMC_SUPPORTS_HARDWARE_PARTS = (1 << 0), - - MMC_EXT_CSD_ERASE_GROUP_DEF = 175, - MMC_EXT_CSD_ERASE_GROUP_DEF_BIT = (1 << 0), - - MMC_EXT_CSD_PARTITION_CONFIG = 179, - MMC_EXT_CSD_PARTITION_SELECT_MASK = 0x7, - - MMC_EXT_CSD_PARTITION_SWITCH_TIME = 199, - MMC_EXT_CSD_PARTITION_SWITCH_SCALE_US = 10000, - - /* Card type register; we skip entries for - * non-1V8 modes, as we're fixed to 1V8 */ - MMC_EXT_CSD_CARD_TYPE = 196, - MMC_EXT_CSD_CARD_TYPE_HS26 = (1 << 0), - MMC_EXT_CSD_CARD_TYPE_HS52 = (1 << 1), - MMC_EXT_CSD_CARD_TYPE_HS200_1V8 = (1 << 4), - MMC_EXT_CSD_CARD_TYPE_HS400_1V8 = (1 << 6), - - /* Current HS mode register */ - MMC_EXT_CSD_HS_TIMING = 185, -}; - - -/** - * Bitfield struct representing an SD SCR. - */ -struct PACKED sdmmc_scr { - uint32_t reserved1; - uint16_t reserved0; - uint8_t supports_width_1bit : 1; - uint8_t supports_width_reserved0 : 1; - uint8_t supports_width_4bit : 1; - uint8_t supports_width_reserved1 : 1; - uint8_t security_support : 3; - uint8_t data_after_erase : 1; - uint8_t spec_version : 4; - uint8_t scr_version : 4; -}; - - -/** - * Bitfield struct represneting an SD card's function status. - */ -struct PACKED sdmmc_function_status { - - /* NOTE: all of the below are reversed from CPU endianness! */ - uint16_t current_consumption; - uint16_t group6_support; - uint16_t group5_support; - uint16_t group4_support; - uint16_t group3_support; - uint16_t group2_support; - - /* support for various speed modes */ - uint16_t group1_support_reserved1 : 8; - uint16_t sdr12_support : 1; - uint16_t sdr25_support : 1; - uint16_t sdr50_support : 1; - uint16_t sdr104_support : 1; - uint16_t ddr50_support : 1; - uint16_t group1_support_reserved2 : 3; - - - uint8_t group5_selection : 4; - uint8_t group6_selection : 4; - uint8_t group3_selection : 4; - uint8_t group4_selection : 4; - uint8_t active_access_mode : 4; - uint8_t group2_selection : 4; - - uint8_t structure_version; - uint16_t group6_busy_status; - uint16_t group5_busy_status; - uint16_t group4_busy_status; - uint16_t group3_busy_status; - uint16_t group2_busy_status; - uint16_t group1_busy_status; - uint8_t reserved[34]; -}; - -/* Callback function typedefs */ -typedef int (*fault_handler_t)(struct mmc *mmc); - -/* Forward declarations */ -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); -static int sdmmc_wait_for_event(struct mmc *mmc, - uint32_t target_irq, uint32_t state_conditions, - uint32_t fault_conditions, fault_handler_t fault_handler); -static int sdmmc_send_command(struct mmc *mmc, enum sdmmc_command command, - enum sdmmc_response_type response_type, enum sdmmc_response_checks checks, - uint32_t argument, void *response_buffer, uint16_t blocks_to_transfer, - bool is_write, bool auto_terminate, void *data_buffer); -static int sdmmc_use_block_size(struct mmc *mmc, int block_order); -static uint8_t sdmmc_get_block_order(struct mmc *mmc, bool is_write); -static void sdmmc_prepare_command_data(struct mmc *mmc, uint16_t blocks, - bool is_write, bool auto_terminate, bool use_dma, int argument); -static void sdmmc_prepare_command_registers(struct mmc *mmc, int blocks_to_xfer, - enum sdmmc_command command, enum sdmmc_response_type response_type, - enum sdmmc_response_checks checks); -static int sdmmc_wait_for_command_readiness(struct mmc *mmc); -static int sdmmc_wait_for_data_readiness(struct mmc *mmc); - -/* SDMMC debug enable */ -static int sdmmc_loglevel = 0; - -/** - * Page-aligned bounce buffer to target with SDMMC DMA. - * If the size of this buffer is changed, the block_size - */ -static uint8_t ALIGN(4096) sdmmc_bounce_buffer[1024 * 8]; -static const uint16_t sdmmc_bounce_dma_boundary = MMC_DMA_BOUNDARY_8K; - - -/** - * Sets the current SDMMC debugging loglevel. - * - * @param loglevel Current log level. A higher value prints more logs. - * @return The loglevel prior to when this was applied, for easy restoration. - */ -int sdmmc_set_loglevel(int loglevel) -{ - int original_loglevel = sdmmc_loglevel; - sdmmc_loglevel = loglevel; - - return original_loglevel; -} - - -/** - * Internal utility function for generating debug prints at various log levels. - */ -static void mmc_vprint(struct mmc *mmc, char *fmt, int required_loglevel, va_list list) -{ - // Allow debug prints to be silenced by a negative loglevel. - if (sdmmc_loglevel < required_loglevel) - return; - - printk("%s: ", mmc->name); - vprintk(fmt, list); - printk("\n"); -} - - -/** - * Normal SDMMC print for SDMMC information. - */ -static void mmc_print(struct mmc *mmc, char *fmt, ...) -{ - va_list list; - - va_start(list, fmt); - mmc_vprint(mmc, fmt, 0, list); - va_end(list); -} - - -/** - * Normal SDMMC print for SDMMC information. - */ -static void mmc_debug(struct mmc *mmc, char *fmt, ...) -{ - va_list list; - - va_start(list, fmt); - mmc_vprint(mmc, fmt, 1, list); - va_end(list); -} - - -/** - * @return a statically allocated string that describes the given command - */ -static const char *sdmmc_get_speed_string(enum sdmmc_bus_speed speed) -{ - switch (speed) { - case SDMMC_SPEED_INIT: return "400kHz (init)"; - - // SD card speeds - case SDMMC_SPEED_SDR12: return "12.5MB/s"; - case SDMMC_SPEED_SDR25: return "25MB/s"; - case SDMMC_SPEED_SDR50: return "50MB/s"; - case SDMMC_SPEED_SDR104: return "104MB/s"; - case SDMMC_SPEED_DDR50: return "104MB/s (DDR)"; - case SDMMC_SPEED_OTHER: return ""; - - // eMMC card speeds - case SDMMC_SPEED_HS26: return "26 MHz"; - case SDMMC_SPEED_HS52: return "52 MHz"; - case SDMMC_SPEED_HS200: return "200MHz"; - case SDMMC_SPEED_HS400: return "200MHz (DDR)"; - } - - return ""; -} - - -/** - * @return a statically allocated string that describes the given command - */ -static const char *sdmmc_get_command_string(enum sdmmc_command command) -{ - switch (command) { - - // Commands that aren't in the lower block. - case CMD_APP_COMMAND: - return "CMD_APP_COMMAND"; - case CMD_APP_SEND_OP_COND: - return "CMD_APP_SEND_OP_COND"; - case CMD_APP_SET_CARD_DETECT: - return "CMD_APP_SET_CARD_DETECT"; - case CMD_APP_SEND_SCR: - return "CMD_APP_SEND_SCR"; - - // For commands with low numbers, read them string from the relevant array. - default: - return sdmmc_command_string[command]; - } -} - - -/** - * Debug: print out any errors that occurred during a command timeout - */ -void mmc_print_command_errors(struct mmc *mmc, int command_errno) -{ - if (command_errno & MMC_STATUS_COMMAND_TIMEOUT) - mmc_print(mmc, "ERROR: command timed out!"); - - if (command_errno & MMC_STATUS_COMMAND_CRC_ERROR) - mmc_print(mmc, "ERROR: command response had invalid CRC"); - - if (command_errno & MMC_STATUS_COMMAND_END_BIT_ERROR) - mmc_print(mmc, "ERROR: command response had invalid end bit"); - - if (command_errno & MMC_STATUS_COMMAND_INDEX_ERROR) - mmc_print(mmc, "ERROR: response appears not to be for the last issued command"); - -} - - -/** - * Retreives the SDMMC register range for the given controller. - */ -static struct tegra_sdmmc *sdmmc_get_regs(enum sdmmc_controller controller) -{ - // 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; -} - -/** - * Performs a soft-reset of the SDMMC controller. - * - * @param mmc The MMC controller to be reset. - * @return 0 if the device successfully came out of reset; or an error code otherwise - */ -static int sdmmc_hardware_reset(struct mmc *mmc, uint32_t reset_flags) -{ - uint32_t timebase = get_time(); - - // Reset the MMC controller... - mmc->regs->software_reset |= reset_flags; - - // Wait for the SDMMC controller to come back up... - while (mmc->regs->software_reset & reset_flags) { - if (get_time_since(timebase) > mmc->timeout) { - mmc_print(mmc, "failed to bring up SDMMC controller"); - return ETIMEDOUT; - } - } - - return 0; -} - -/** - * Delays for a given amount of host clock cycles. - * - * @param mmc The MMC controller whose clock cycles should be waited upon. - * @param clocks The number of clock cycles to wait. - */ -static void sdmmc_host_clock_delay(struct mmc *mmc, unsigned int clocks) -{ - // For the time being simply wait for clocks * 50 us - // This covers clocks as slow as 20 kHz and hence should always be safe - // TODO: determine the actual wait time based on clock source and divider - udelay(50 * clocks); -} - -/** - * Performs low-level initialization for SDMMC4, used for the eMMC. - */ -static int sdmmc4_set_up_clock_and_io(struct mmc *mmc) -{ - volatile struct tegra_car *car = car_get_regs(); - volatile struct tegra_padctl *padctl = padctl_get_regs(); - - // Put SDMMC4 in reset - car->rst_dev_l_set |= 0x8000; - - // Configure the clock to place the device into the initial mode. - car->clk_src[CLK_SOURCE_SDMMC4] = MMC_CLOCK_SOURCE_SDR12 | MMC_CLOCK_DIVIDER_SDR12; - - // Set the legacy divier used for detecting timeouts. - car->clk_src_y[CLK_SOURCE_SDMMC_LEGACY] = MMC_CLOCK_SOURCE_LEGACY | MMC_CLOCK_DIVIDER_LEGACY; - - // Set SDMMC4 clock enable - car->clk_enb_l_set |= 0x8000; - car->clk_enb_y_set |= CAR_CONTROL_SDMMC_LEGACY; - - // Delay 100 host clock cycles - sdmmc_host_clock_delay(mmc, 100); - - // Take SDMMC4 out of reset - car->rst_dev_l_clr |= 0x8000; - - // Enable input paths for all pins. - padctl->sdmmc4_control |= - PADCTL_SDMMC4_ENABLE_DATA_IN | PADCTL_SDMMC4_ENABLE_CLK_IN | PADCTL_SDMMC4_DEEP_LOOPBACK; - - return 0; -} - -/** - * Sets the voltage that the given SDMMC is currently working with. - * - * @param mmc The controller to affect. - * @param voltage The voltage to apply. - */ -static void sdmmc_set_working_voltage(struct mmc *mmc, enum sdmmc_bus_voltage voltage) -{ - // Apply the voltage... - mmc->operating_voltage = voltage; - - // Set up the SD card's voltage. - mmc->regs->power_control &= ~MMC_POWER_CONTROL_VOLTAGE_MASK; - mmc->regs->power_control |= voltage << MMC_POWER_CONTROL_VOLTAGE_SHIFT; - - // Switch to 1V8 signaling. - mmc->regs->host_control2 |= MMC_HOST2_USE_1V8_SIGNALING; - - // Mark the power as on. - mmc->regs->power_control |= MMC_POWER_CONTROL_POWER_ENABLE; -} - - -/** - * Enables power supplies for SDMMC4, used for eMMC. - */ -static int sdmmc4_enable_supplies(struct mmc *mmc) -{ - // As a booot device, SDMMC4's power supply is always on. - // Modify the controller to know the voltage being applied to it, - // and return success. - sdmmc_set_working_voltage(mmc, MMC_VOLTAGE_1V8); - return 0; -} - - -/** - * Enables power supplies for SDMMC1, used for the SD card slot. - */ -static int sdmmc1_enable_supplies(struct mmc *mmc) -{ - volatile struct tegra_pmc *pmc = pmc_get_regs(); - volatile struct tegra_pinmux *pinmux = pinmux_get_regs(); - - // Set PAD_E_INPUT_OR_E_PWRD (relevant for eMMC only) - mmc->regs->sdmemcomppadctrl |= 0x80000000; - - // Ensure the PMC is prepared for the SDMMC card to recieve power. - pmc->no_iopower &= ~PMC_CONTROL_SDMMC1; - pmc->pwr_det_val |= PMC_CONTROL_SDMMC1; - - // Set up SD card voltages. - udelay(1000); - supply_enable(SUPPLY_MICROSD, false); - udelay(1000); - - // Modify the controller to know the voltage being applied to it. - sdmmc_set_working_voltage(mmc, MMC_VOLTAGE_3V3); - - // Configure the enable line for the SD card power. - pinmux->dmic3_clk = PINMUX_SELECT_FUNCTION0; - gpio_configure_mode(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_MODE_GPIO); - gpio_configure_direction(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_DIRECTION_OUTPUT); - - // Bring up the SD card fixed regulator. - gpio_write(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_LEVEL_HIGH); - return 0; -} - - -/** - * Configures clocking parameters for a given controller. - * - * @param mmc The MMC controller to set up. - * @param operating_voltage The operating voltage for the bus, currently. - */ -static int sdmmc_set_up_clocking_parameters(struct mmc *mmc, enum sdmmc_bus_voltage operating_voltage) -{ - // Clear the I/O conditioning constants. - mmc->regs->vendor_clock_cntrl &= ~(MMC_CLOCK_TRIM_MASK | MMC_CLOCK_TAP_MASK); - - // Per the TRM, set the PADPIPE clock enable. - mmc->regs->vendor_clock_cntrl |= MMC_CLOCK_PADPIPE_CLKEN_OVERRIDE; - - // Set up the I/O conditioning constants used to ensure we have a reliable clock. - // Constants above and procedure below from the TRM. - switch (mmc->controller) { - case SWITCH_EMMC: - if (operating_voltage != MMC_VOLTAGE_1V8) { - mmc_print(mmc, "ERROR: eMMC can only run at 1V8, but mmc struct claims voltage %d", operating_voltage); - return EINVAL; - } - - mmc->regs->auto_cal_config &= ~MMC_AUTOCAL_PDPU_CONFIG_MASK; - mmc->regs->auto_cal_config |= MMC_AUTOCAL_PDPU_SDMMC4_1V8; - mmc->regs->vendor_clock_cntrl |= (MMC_CLOCK_TRIM_SDMMC4 | MMC_CLOCK_TAP_SDMMC4); - break; - - case SWITCH_MICROSD: - switch (operating_voltage) { - case MMC_VOLTAGE_1V8: - mmc->regs->auto_cal_config &= ~MMC_AUTOCAL_PDPU_CONFIG_MASK; - mmc->regs->auto_cal_config |= MMC_AUTOCAL_PDPU_SDMMC1_1V8; - break; - case MMC_VOLTAGE_3V3: - mmc->regs->auto_cal_config &= ~MMC_AUTOCAL_PDPU_CONFIG_MASK; - mmc->regs->auto_cal_config |= MMC_AUTOCAL_PDPU_SDMMC1_3V3; - break; - default: - mmc_print(mmc, "ERROR: microsd does not support voltage %d", operating_voltage); - return EINVAL; - } - mmc->regs->vendor_clock_cntrl |= (MMC_CLOCK_TRIM_SDMMC1 | MMC_CLOCK_TAP_SDMMC1); - break; - - default: - printk("ERROR: initialization not yet writen for SDMMC%d", mmc->controller); - return ENODEV; - } - - return 0; -} - - -/** - * Enables or disables delivering a clock to the downstream SD/MMC card. - * - * @param mmc The controller to be affected. - * @param enabled True if the clock should be enabled; false to disable. - */ -void sdmmc_clock_enable(struct mmc *mmc, bool enabled) -{ - // Set or clear the card clock enable bit according to the - // controller paramter. - if (enabled) - mmc->regs->clock_control |= MMC_CLOCK_CONTROL_CARD_CLOCK_ENABLE; - else - mmc->regs->clock_control &= ~MMC_CLOCK_CONTROL_CARD_CLOCK_ENABLE; -} - - -/** - * Runs SDMMC automatic calibration-- this tunes the parameters used for SDMMC - * signal intergrity. - * - * @param mmc The controller whose card is to be tuned. - * @param restart_sd_clock True iff the SD card should be started after calibration. - * - * @return 0 on success, or an error code on failure - */ -static int sdmmc_run_autocal(struct mmc *mmc, bool restart_sd_clock) -{ - uint32_t timebase; - int ret = 0; - - // Stop the SD card's clock, so our autocal sequence doesn't - // confuse the target card. - sdmmc_clock_enable(mmc, false); - - // Start automatic calibration... - mmc->regs->auto_cal_config |= (MMC_AUTOCAL_START | MMC_AUTOCAL_ENABLE); - udelay(1); - - // ... and wait until the autocal is complete - timebase = get_time(); - while ((mmc->regs->auto_cal_status & MMC_AUTOCAL_ACTIVE)) { - - // Ensure we haven't timed out... - if (get_time_since(timebase) > MMC_AUTOCAL_TIMEOUT) { - mmc_print(mmc, "ERROR: autocal timed out!"); - if (mmc->controller == SWITCH_MICROSD) { - // Fallback driver strengths from Tegra X1 TRM - uint32_t drvup = (mmc->operating_voltage == MMC_VOLTAGE_3V3) ? 0x12 : 0x11; - uint32_t drvdn = (mmc->operating_voltage == MMC_VOLTAGE_3V3) ? 0x12 : 0x15; - uint32_t value = APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL_0; - value &= ~(SDMMC1_PAD_CAL_DRVUP_MASK | SDMMC1_PAD_CAL_DRVDN_MASK); - value |= (drvup << SDMMC1_PAD_CAL_DRVUP_SHIFT); - value |= (drvdn << SDMMC1_PAD_CAL_DRVDN_SHIFT); - APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL_0 = value; - } else if (mmc->controller == SWITCH_EMMC) { - uint32_t value = APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0; - value &= ~(CFG2TMC_EMMC4_PAD_DRVUP_COMP_MASK | CFG2TMC_EMMC4_PAD_DRVDN_COMP_MASK); - value |= (0x10 << CFG2TMC_EMMC4_PAD_DRVUP_COMP_SHIFT); - value |= (0x10 << CFG2TMC_EMMC4_PAD_DRVDN_COMP_SHIFT); - APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 = value; - } - mmc->regs->auto_cal_config &= ~MMC_AUTOCAL_ENABLE; - ret = ETIMEDOUT; - break; - } - } - - // If requested, enable the SD clock. - if (restart_sd_clock) - sdmmc_clock_enable(mmc, true); - - return ret; -} - - -/** - * Switches the Switch's microSD card into low-voltage mode. - * - * @param mmc The MMC controller via which to communicate. - * @return 0 on success, or an error code on failure. - */ -static int sdmmc1_switch_to_low_voltage(struct mmc *mmc) -{ - volatile struct tegra_pmc *pmc = pmc_get_regs(); - int rc; - - // Let the SD card know we're about to switch into low-voltage mode. - // Set up the card's relative address. - rc = sdmmc_send_simple_command(mmc, CMD_SWITCH_TO_LOW_VOLTAGE, MMC_RESPONSE_LEN48, 0, NULL); - if (rc) { - mmc_print(mmc, "card was not willling to switch to low voltage! (%d)", rc); - return rc; - } - - // Switch the MicroSD card supply into its low-voltage mode. - supply_enable(SUPPLY_MICROSD, true); - pmc->pwr_det_val &= ~PMC_CONTROL_SDMMC1; - - // Apply our clocking parameters for low-voltage mode. - rc = sdmmc_set_up_clocking_parameters(mmc, MMC_VOLTAGE_1V8); - if (rc) { - mmc_print(mmc, "WARNING: could not optimize card clocking parameters. (%d)", rc); - } - - // Rerun the main clock calibration... - rc = sdmmc_run_autocal(mmc, false); - if (rc) - mmc_print(mmc, "WARNING: failed to re-calibrate after voltage switch!"); - - // ... and ensure the host is set up to apply the relevant change. - sdmmc_set_working_voltage(mmc, MMC_VOLTAGE_1V8); - udelay(MMC_VOLTAGE_SWITCH_TIME); - - // Enable the SD clock. - sdmmc_clock_enable(mmc, true); - udelay(MMC_POST_CLOCK_DELAY); - - mmc_debug(mmc, "now running from 1V8"); - return 0; -} - - -/** - * Low-voltage switching method for controllers that don't - * support a low-voltage switch. Always fails. - * - * @param mmc The MMC controller via which to communicate. - * @return ENOSYS, indicating failure, always - */ -static int sdmmc_always_fail(struct mmc *mmc) -{ - // This card is always in low voltage and should never have to switch. - return ENOSYS; -} - - -/** - * Sets up the clock for the given SDMMC controller. - * Assumes the controller's clock has been stopped with sdmmc_clock_enable before use. - * - * @param mmc The controller to work with. - * @param source The clock source, as defined by the CAR. - * @param car_divisor. The divisor for that source in the CAR. Usually one of the MMC_CLOCK_DIVIDER macros; - * a divider of N results in a clock that's (N/2) + 1 slower. - * @param sdmmc_divisor An additional divisor applied in the SDMMC controller. - */ -static void sdmmc4_configure_clock(struct mmc *mmc, uint32_t source, int car_divisor, int sdmmc_divisor) -{ - volatile struct tegra_car *car = car_get_regs(); - - // Set up the CAR aspect of the clock, and wait 2us per change per the TRM. - car->clk_enb_l_clr = CAR_CONTROL_SDMMC4; - car->clk_src[CLK_SOURCE_SDMMC4] = source | car_divisor; - udelay(2); - car->clk_enb_l_set = CAR_CONTROL_SDMMC4; - - // ... and the SDMMC section of it. - mmc->regs->clock_control &= ~MMC_CLOCK_CONTROL_FREQUENCY_MASK; - mmc->regs->clock_control |= sdmmc_divisor << MMC_CLOCK_CONTROL_FREQUENCY_SHIFT; -} - - -/** - * Sets up the clock for the given SDMMC controller. - * Assumes the controller's clock has been stopped with sdmmc_clock_enable before use. - * - * @param mmc The controller to work with. - * @param source The clock source, as defined by the CAR. - * @param car_divisor. The divisor for that source in the CAR. Usually one of the MMC_CLOCK_DIVIDER macros; - * a divider of N results in a clock that's (N/2) + 1 slower. - * @param sdmmc_divisor An additional divisor applied in the SDMMC controller. - */ -static void sdmmc1_configure_clock(struct mmc *mmc, uint32_t source, int car_divisor, int sdmmc_divisor) -{ - volatile struct tegra_car *car = car_get_regs(); - - // Set up the CAR aspect of the clock, and wait 2us per change per the TRM. - car->clk_enb_l_clr = CAR_CONTROL_SDMMC1; - car->clk_src[CLK_SOURCE_SDMMC1] = source | car_divisor; - udelay(2); - car->clk_enb_l_set = CAR_CONTROL_SDMMC1; - - // ... and the SDMMC section of it. - mmc->regs->clock_control &= ~MMC_CLOCK_CONTROL_FREQUENCY_MASK; - mmc->regs->clock_control |= sdmmc_divisor << MMC_CLOCK_CONTROL_FREQUENCY_SHIFT; -} - - -/** - * Runs a single iteration of an active SDMMC clock tune. - * You probably want sdmmc_tune_clock instead. - */ -static int sdmmc_run_tuning_iteration(struct mmc *mmc, enum sdmmc_command tuning_command) -{ - int rc; - uint32_t saved_int_enable = mmc->regs->int_enable; - - // Enable the BUFFER_READ_READY interrupt for this run, and make sure it's not set. - mmc->regs->int_enable |= MMC_STATUS_BUFFER_READ_READY; - mmc->regs->int_status = mmc->regs->int_status; - - // Wait until we can issue commands to the device. - 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; - } - - rc = sdmmc_wait_for_data_readiness(mmc); - if (rc) { - mmc_print(mmc, "card not willing to accept data-commands (%d / %08x)", rc, mmc->regs->present_state); - return EBUSY; - } - - // Disable the SD card clock. [TRM 32.7.6.2 Step 3] - // NVIDIA notes that issuing the re-tune command triggers a re-selection of clock tap, but also - // due to a hardware issue, causes a one-microsecond window in which a clock glitch can occur. - // We'll disable the SD card clock temporarily so the card itself isn't affected by the glitch. - sdmmc_clock_enable(mmc, false); - - // Issue our tuning command. [TRM 32.7.6.2 Step 4] - sdmmc_prepare_command_data(mmc, 1, false, false, false, 0); - sdmmc_prepare_command_registers(mmc, 1, tuning_command, MMC_RESPONSE_LEN48, MMC_CHECKS_ALL); - - // Wait for 1us [TRM 32.7.6.2 Step 5] - // As part of the workaround above, we'll wait one microsecond for the glitch window to pass. - udelay(1); - - // Issue a software reset for the data and command lines. [TRM 32.7.6.2 Step 6/7] - // This completes the workaround by ensuring the glitch didn't leave the sampling hardware - // for these lines in an uncertain state. This function blocks until the glitch window has - // complete, so it handles both TRM steps 7 and 8. - sdmmc_hardware_reset(mmc, MMC_SOFT_RESET_CMD | MMC_SOFT_RESET_DAT); - - // Restore the SDMMC clock, now that the workaround is complete. [TRM 32.7.6.2 Step 8] - // This enables the actual command to be issued. - sdmmc_clock_enable(mmc, true); - - // Wait for the command to be completed. [TRM 32.7.6.2 Step 9] - rc = sdmmc_wait_for_event(mmc, MMC_STATUS_BUFFER_READ_READY, 0, 0, NULL); - - // Always restore the prior interrupt settings. - mmc->regs->int_enable = saved_int_enable; - - // If we had an error waiting for the interrupt, something went wrong. - // TODO: decide if this should be a retry condition? - if (rc) { - mmc_print(mmc, "buffer ready ready didn't go high in time?"); - mmc_print(mmc, "error message: %s", strerror(rc)); - mmc_print(mmc, "interrupt reg: %08x", mmc->regs->int_status); - mmc_print(mmc, "interrupt en: %08x", mmc->regs->int_enable); - return rc; - } - - // Check the status of the "execute tuning", which indicates the success of - // this tuning step. [TRM 32.7.6.2 Step 10] - if (mmc->regs->host_control2 & MMC_HOST2_EXECUTE_TUNING) - return EAGAIN; - - return 0; -} - - -/** - * Performs an SDMMC clock tuning -- should be issued when switching up to a UHS-I - * mode, and periodically thereafter per the spec. - * - * @param mmc The controller to tune. - * @param iterations The total number of iterations to perform. - * @param tuning_command The command to be used for tuning; usually CMD19/21. - */ -static int sdmmc_tune_clock(struct mmc *mmc, enum sdmmc_tuning_attempts iterations, - enum sdmmc_command tuning_command) -{ - int rc; - - // We follow the NVIDIA-suggested tuning procedure (TRM section 32.7.6), - // including sections where it deviates from the SDMMC specifications. - // - // This seems to produce the most reliable results, and includes workarounds - // for bugs in the X1 hardware. - - // Read the current block order, so we can restore it. - int original_block_order = sdmmc_get_block_order(mmc, false); - - // Stores the current timeout; we'll restore it in a bit. - int original_timeout = mmc->timeout; - - // The SDMMC host spec suggests tuning should occur over 40 iterations, so we'll stick to that. - // Vendors seem to deviate from this, so this is a possible area to look into if something doesn't - // wind up working correctly. - int attempts_remaining = sdmmc_tuning_iterations[iterations]; - mmc_debug(mmc, "executing tuning (%d iterations)", attempts_remaining); - - // Allow the tuning hardware to control e.g. our clock taps. - mmc->regs->vendor_tuning_cntrl0 |= MMC_VENDOR_TUNING_SET_BY_HW; - - // Apply our number of tries. - mmc->regs->vendor_tuning_cntrl0 &= ~MMC_VENDOR_TUNING_TRIES_MASK; - mmc->regs->vendor_tuning_cntrl0 |= (iterations << MMC_VENDOR_TUNING_TRIES_SHIFT); - - // Don't use a multiplier or a divider. - mmc->regs->vendor_tuning_cntrl0 &= ~(MMC_VENDOR_TUNING_MULTIPLIER_MASK | MMC_VENDOR_TUNING_DIVIDER_MASK); - mmc->regs->vendor_tuning_cntrl0 |= MMC_VENDOR_TUNING_MULTIPLIER_UNITY; - - // Use zero step sizes; per TRM 32.7.6.1. - mmc->regs->vendor_tuning_cntrl1 = MMC_VENDOR_TUNING_STEP_SIZE_SDR50_DEFAULT | MMC_VENDOR_TUNING_STEP_SIZE_SDR104_DEFAULT; - - // Start the tuning process. [TRM 32.7.6.2 Step 2] - mmc->regs->host_control2 |= MMC_HOST2_EXECUTE_TUNING; - - // Momentarily step down to a smaller block size, so we don't - // have to allocate a huge buffer for this command. - mmc->read_block_order = mmc->tuning_block_order; - - // Step down to the timeout recommended in the specification. - mmc->timeout = MMC_TUNING_TIMEOUT; - - // Iterate an iteration of the tuning process. - while (attempts_remaining--) { - - // Run an iteration of our tuning process. - rc = sdmmc_run_tuning_iteration(mmc, tuning_command); - - // If we have an error other than "retry, break. - if (rc != EAGAIN) - break; - } - - // Restore the original parameters. - mmc->read_block_order = original_block_order; - mmc->timeout = original_timeout; - - // If we exceeded our attempts, set this as a timeout. - if (rc == EAGAIN) { - mmc_print(mmc, "tuning attempts exceeded!"); - rc = ETIMEDOUT; - } - - // If the tuning failed, for any reason, print and return the error. - if (rc) { - mmc_print(mmc, "ERROR: failed to tune the SDMMC clock! (%d)", rc); - mmc_print(mmc, "error message %s", strerror(rc)); - return rc; - } - - // If we've made it here, this iteration completed tuning. - // Check for a tuning failure (SAMPLE CLOCK = 0). [TRM 32.7.6.2 Step 11] - if (!(mmc->regs->host_control2 & MMC_HOST2_SAMPLING_CLOCK_ENABLED)) { - mmc_print(mmc, "ERROR: tuning failed after complete iteration!"); - mmc_print(mmc, "host_control2: %08x", mmc->regs->host_control2); - return EIO; - } - - return 0; -} - - -/** - * Configures the host controller to work at a given UHS-I mode. - * - * @param mmc The controller to work with - * @param speed The UHS or pre-UHS speed to work at. - */ -static void sdmmc_set_uhs_mode(struct mmc *mmc, enum sdmmc_bus_speed speed) -{ - // Set the UHS mode register. - mmc->regs->host_control2 &= MMC_HOST2_UHS_MODE_MASK; - mmc->regs->host_control2 |= speed; -} - - -/** - * Applies the appropriate clock dividers to the CAR and SD controller to enable use of the - * provided speed. Does not handle any requisite communications with the card. - * - * @param mmc The controller to affect. - * @param speed The speed to apply. - * @param enable_after If set, the SDMMC clock will be enabled after the change. If not, it will be left disabled. - */ -static int sdmmc_apply_clock_speed(struct mmc *mmc, enum sdmmc_bus_speed speed, bool enable_after) -{ - int rc; - - // By default, don't execute tuning after applying the clock. - bool execute_tuning = false; - enum sdmmc_tuning_attempts tuning_attempts = MMC_VENDOR_TUNING_TRIES_40; - enum sdmmc_command tuning_command = CMD_SD_SEND_TUNING_BLOCK; - - // Ensure the clocks are not currently running to avoid glitches. - sdmmc_clock_enable(mmc, false); - - // Clear the registers of any existing values, so we can apply new ones. - mmc->regs->host_control &= ~MMC_HOST_ENABLE_HIGH_SPEED; - - // Apply the dividers according to the speed provided. - switch (speed) { - - // 400kHz initialization mode. - case SDMMC_SPEED_INIT: - mmc->configure_clock(mmc, MMC_CLOCK_SOURCE_SDR12, MMC_CLOCK_DIVIDER_SDR12, MMC_CLOCK_CONTROL_FREQUENCY_INIT); - sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_SDR12); - break; - - // 25MHz default speed (SD) - case SDMMC_SPEED_SDR12: - mmc->configure_clock(mmc, MMC_CLOCK_SOURCE_SDR12, MMC_CLOCK_DIVIDER_SDR12, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH); - sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_SDR12); - break; - - // 26MHz default speed (MMC) - case SDMMC_SPEED_HS26: - mmc->configure_clock(mmc, MMC_CLOCK_SOURCE_HS26, MMC_CLOCK_DIVIDER_HS26, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH); - break; - - // 50MHz high speed (SD) - case SDMMC_SPEED_SDR25: - // Configure the host to use high-speed timing. - mmc->regs->host_control |= MMC_HOST_ENABLE_HIGH_SPEED; - mmc->configure_clock(mmc, MMC_CLOCK_SOURCE_SDR25, MMC_CLOCK_DIVIDER_SDR25, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH); - sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_SDR25); - break; - - // 52MHz high speed (MMC) - case SDMMC_SPEED_HS52: - mmc->configure_clock(mmc, MMC_CLOCK_SOURCE_HS52, MMC_CLOCK_DIVIDER_HS52, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH); - break; - - // 100MHz UHS-I (SD) - case SDMMC_SPEED_SDR50: - mmc->regs->host_control |= MMC_HOST_ENABLE_HIGH_SPEED; - mmc->configure_clock(mmc, MMC_CLOCK_SOURCE_SDR50, MMC_CLOCK_DIVIDER_SDR50, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH); - // Tegra X1 Series Processors Silicon Errata MMC-2 mentions setting SDR104 mode as workaround. - sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_SDR104); - - execute_tuning = true; - tuning_attempts = MMC_VENDOR_TUNING_TRIES_SDR50; - break; - - // 200MHz UHS-I (SD) - case SDMMC_SPEED_SDR104: - mmc->regs->host_control |= MMC_HOST_ENABLE_HIGH_SPEED; - mmc->configure_clock(mmc, MMC_CLOCK_SOURCE_SDR104, MMC_CLOCK_DIVIDER_SDR104, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH); - sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_SDR104); - - execute_tuning = true; - tuning_attempts = MMC_VENDOR_TUNING_TRIES_SDR104; - break; - - // 200MHz single-data rate (MMC) - case SDMMC_SPEED_HS200: - case SDMMC_SPEED_HS400: - mmc->regs->host_control |= MMC_HOST_ENABLE_HIGH_SPEED; - mmc->configure_clock(mmc, MMC_CLOCK_SOURCE_HS200, MMC_CLOCK_DIVIDER_HS200, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH); - sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_SDR104); // Per datasheet; we set the controller in SDR104 mode. - - // Execute tuning. - execute_tuning = true; - tuning_attempts = MMC_VENDOR_TUNING_TRIES_HS200; - tuning_command = CMD_MMC_SEND_TUNING_BLOCK; - break; - - default: - mmc_print(mmc, "ERROR: switching to unsupported speed!\n"); - return ENOSYS; - } - - // If we need to execute tuning for this clock mode, do so. - if (execute_tuning) { - rc = sdmmc_tune_clock(mmc, tuning_attempts, tuning_command); - if (rc) { - mmc_print(mmc, "WARNING: clock tuning failed! speed mode can't be used. (%d)", rc); - sdmmc_clock_enable(mmc, true); - return rc; - } - } - - // Special case: HS400 mode should be applied _after_ HS200 is applied, so we apply that - // first above, and then switch up and re-tune. - if (speed == SDMMC_SPEED_HS400) { - sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_OTHER); // Special value, per datasheet - - // Per the TRM, we should also use this opportunity to set up the DQS path trimmer. - // TODO: should this be used only in HS400? - mmc->regs->vendor_cap_overrides &= ~MMC_VENDOR_CAPABILITY_DQS_TRIM_MASK; - mmc->regs->vendor_cap_overrides |= MMC_VENDOR_CAPABILITY_DQS_TRIM_HS400; - - // TODO: run the DLLCAL here! - - mmc_print(mmc, "TODO: double the data rate here!"); - } - - - // Re-enable the clock, if necessary. - if (enable_after) { - sdmmc_clock_enable(mmc, true); - udelay(MMC_POST_CLOCK_DELAY); - } - - // Finally store the operating speed. - mmc->operating_speed = speed; - return 0; -} - - -/** - * Performs low-level initialization for SDMMC1, used for the SD card slot. - */ -static int sdmmc1_set_up_clock_and_io(struct mmc *mmc) -{ - volatile struct tegra_car *car = car_get_regs(); - volatile struct tegra_pinmux *pinmux = pinmux_get_regs(); - volatile struct tegra_padctl *padctl = padctl_get_regs(); - - // Set up each of the relevant pins to be connected to output drivers, - // and selected for SDMMC use. - pinmux->sdmmc1_clk = PINMUX_DRIVE_2X | PINMUX_PARKED | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT; - pinmux->sdmmc1_cmd = PINMUX_DRIVE_2X | PINMUX_PARKED | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT | PINMUX_PULL_UP; - pinmux->sdmmc1_dat3 = PINMUX_DRIVE_2X | PINMUX_PARKED | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT | PINMUX_PULL_UP; - pinmux->sdmmc1_dat2 = PINMUX_DRIVE_2X | PINMUX_PARKED | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT | PINMUX_PULL_UP; - pinmux->sdmmc1_dat1 = PINMUX_DRIVE_2X | PINMUX_PARKED | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT | PINMUX_PULL_UP; - pinmux->sdmmc1_dat0 = PINMUX_DRIVE_2X | PINMUX_PARKED | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT | PINMUX_PULL_UP; - - // Set up the card detect pin as a GPIO input. - pinmux->pz1 = PINMUX_TRISTATE | PINMUX_SELECT_FUNCTION1 | PINMUX_PULL_UP | PINMUX_INPUT; - gpio_configure_mode(GPIO_MICROSD_CARD_DETECT, GPIO_MODE_GPIO); - gpio_configure_direction(GPIO_MICROSD_CARD_DETECT, GPIO_DIRECTION_INPUT); - udelay(100); - - // Ensure we're using GPIO and not GPIO for the SD card's card detect. - padctl->vgpio_gpio_mux_sel &= ~PADCTL_SDMMC1_CD_SOURCE; - - // Put SDMMC1 in reset - car->rst_dev_l_set = CAR_CONTROL_SDMMC1; - - // Configure the clock to place the device into the initial mode. - car->clk_src[CLK_SOURCE_SDMMC1] = MMC_CLOCK_SOURCE_SDR12 | MMC_CLOCK_DIVIDER_SDR12; - - // Set the legacy divier used for detecting timeouts. - car->clk_src_y[CLK_SOURCE_SDMMC_LEGACY] = MMC_CLOCK_SOURCE_LEGACY | MMC_CLOCK_DIVIDER_LEGACY; - - // Set SDMMC1 clock enable - car->clk_enb_l_set = CAR_CONTROL_SDMMC1; - car->clk_enb_y_set = CAR_CONTROL_SDMMC_LEGACY; - - // Delay 100 host clock cycles - sdmmc_host_clock_delay(mmc, 100); - - // Take SDMMC1 out of reset - car->rst_dev_l_clr |= CAR_CONTROL_SDMMC1; - - // Enable clock loopback. - padctl->sdmmc1_control |= PADCTL_SDMMC1_DEEP_LOOPBACK; - - return 0; -} - - -/** - * Initialize the low-level SDMMC hardware. - * Thanks to hexkyz for this init code. - * - * FIXME: clean up the magic numbers, split into sections. - */ -static int sdmmc_hardware_init(struct mmc *mmc) -{ - volatile struct tegra_sdmmc *regs = mmc->regs; - - uint32_t timebase; - bool is_timeout; - - int rc; - - // Initialize the Tegra resources necessary to use the given piece of hardware. - rc = mmc->set_up_clock_and_io(mmc); - if (rc) { - mmc_print(mmc, "ERROR: could not set up controller for use!"); - return rc; - } - - // Software reset the SDMMC device. - rc = sdmmc_hardware_reset(mmc, MMC_SOFT_RESET_FULL); - if (rc) { - mmc_print(mmc, "failed to reset!"); - return rc; - } - - // Turn on the card's power supplies... - rc = mmc->enable_supplies(mmc); - if (rc) { - mmc_print(mmc, "ERROR: could power on the card!"); - return rc; - } - - - // Set IO_SPARE[19] (one cycle delay) - regs->io_spare |= 0x80000; - - // Clear SEL_VREG - regs->vendor_io_trim_cntrl &= ~(0x04); - - // Set SDMMC2TMC_CFG_SDMEMCOMP_VREF_SEL to 0x07 - regs->sdmemcomppadctrl &= ~(0x0F); - regs->sdmemcomppadctrl |= 0x07; - - // Set ourselves up to have a stable. - rc = sdmmc_set_up_clocking_parameters(mmc, mmc->operating_voltage); - if (rc) { - mmc_print(mmc, "WARNING: could not optimize card clocking parameters. (%d)", rc); - } - - // Set PAD_E_INPUT_OR_E_PWRD - regs->sdmemcomppadctrl |= 0x80000000; - - // Wait one milisecond - udelay(1000); - - // Run automatic calibration. - rc = sdmmc_run_autocal(mmc, false); - if (rc) { - mmc_print(mmc, "autocal failed! (%d)", rc); - return rc; - } - - // 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 - 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; - } - - // FIXME: replace this to support better clocks - regs->host_control2 = 0; - - // Clear SDHCI_PROG_CLOCK_MODE - regs->clock_control &= ~(0x20); - - // Ensure we're using Single-operation DMA (SDMA) mode for DMA. - regs->host_control &= ~MMC_DMA_SELECT_MASK; - - // Set the timeout to be the maximum value - regs->timeout_control &= ~(0x0F); - regs->timeout_control |= 0x0E; - - // Ensure we start off using a single-bit bus. - mmc->regs->host_control &= ~MMC_HOST_BUS_WIDTH_MASK; - - // TODO: move me into enable voltages, if applicable? - - // Clear TAP_VAL_UPDATED_BY_HW - regs->vendor_tuning_cntrl0 &= ~(0x20000); - - // Start off in non-high-speed mode. - regs->host_control &= ~MMC_HOST_ENABLE_HIGH_SPEED; - - // Clear SDHCI_CTRL_VDD_180 - regs->host_control2 &= ~(0x08); - - // Set up the card's initialization. - rc = sdmmc_apply_clock_speed(mmc, SDMMC_SPEED_INIT, true); - if (rc) { - mmc_print(mmc, "ERROR: could not set SDMMC card speed!"); - return rc; - } - - return 0; -} - - -/* - * Blocks until the card has reached a given physical state, - * as indicated by the present state register. - * - * @param mmc The MMC controller whose state we should wait on - * @param present_state_mask A mask that indicates when we should return. - * Returns when the mask bits are no longer set in present_state if invert is true, - * or true when the mask bits are _set_ in the present state if invert is false. - * - * @return 0 on success, or an error on failure - */ -static int sdmmc_wait_for_physical_state(struct mmc *mmc, uint32_t present_state_mask, bool invert) -{ - uint32_t timebase = get_time(); - uint32_t condition; - - // Retry until the event or an error happens - while (true) { - - // Handle timeout. - if (get_time_since(timebase) > mmc->timeout) { - mmc_print(mmc, "timed out waiting for command readiness!"); - return ETIMEDOUT; - } - - // Read the status, and invert the condition, if necessary. - condition = mmc->regs->present_state & present_state_mask; - if (invert) { - condition = !condition; - } - - // Return once our condition is met. - if (condition) - return 0; - } -} - - -/** - * 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) -{ - return sdmmc_wait_for_physical_state(mmc, MMC_COMMAND_INHIBIT, true); -} - - -/** - * Blocks until the SD driver is ready to transmit data, - * or the MMC controller's timeout interval is met. - * - * @param mmc The MMC controller whose data line we should wait for. - */ -static int sdmmc_wait_for_data_readiness(struct mmc *mmc) -{ - return sdmmc_wait_for_physical_state(mmc, MMC_DATA_INHIBIT, true); -} - - -/** - * Blocks until the SD driver's data lines are clear, - * indicating the card is no longer busy. - * - * @param mmc The MMC controller whose data line we should wait for. - */ -static int sdmmc_wait_until_no_longer_busy(struct mmc *mmc) -{ - 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; -} - -/** - * Generic SDMMC waiting function. - * - * @param mmc The MMC controller on which to wait. - * @param target_irq A bitmask that specifies the interrupt bits that - * will make this function return success. - * @param state_condition A bitmask that specifies a collection of bits - * that indicate business in present_state. If zero, all of the relevant - * conditions becoming false will cause a sucessful return. - * @param fault_conditions A bitmask that specifies the bits that - * 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, - * or an error code if a transfer failure occurs - */ -static int sdmmc_wait_for_event(struct mmc *mmc, - uint32_t target_irq, uint32_t state_conditions, - uint32_t fault_conditions, fault_handler_t fault_handler) -{ - uint32_t timebase = get_time(); - uint32_t intstatus; - int rc; - - // Wait until we either wind up ready, or until we've timed out. - while (true) { - if (get_time_since(timebase) > mmc->timeout) - return ETIMEDOUT; - - // Read intstatus into temporary variable to make sure that the - // priorities are: fault conditions, target irq, errors - // This makes sure that if fault conditions and target irq - // comes nearly at the same time that the fault handler will - // always be called - intstatus = mmc->regs->int_status; - if (intstatus & fault_conditions) { - - // If we don't have a handler, fault. - if (!fault_handler) { - mmc_print(mmc, "ERROR: unhandled DMA fault!"); - return EFAULT; - } - - // Call the DMA fault handler. - rc = fault_handler(mmc); - if (rc) { - mmc_print(mmc, "ERROR: unhandled DMA fault! (%d)", rc); - return rc; - } - - // Finally, EOI the relevant interrupt. - mmc->regs->int_status = fault_conditions; - intstatus &= ~(fault_conditions); - - // Reset the timebase, so it applies to the next - // DMA interval. - timebase = get_time(); - } - - if (intstatus & target_irq) - return 0; - - if (state_conditions && !(mmc->regs->present_state & state_conditions)) - return 0; - - // If an error occurs, return it. - if (intstatus & MMC_STATUS_ERROR_MASK) - return (intstatus & MMC_STATUS_ERROR_MASK); - } -} - -/** - * 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) -{ - return sdmmc_wait_for_event(mmc, MMC_STATUS_COMMAND_COMPLETE, 0, 0, NULL); -} - - -/** - * 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) -{ - int rc = sdmmc_wait_for_event(mmc, MMC_STATUS_TRANSFER_COMPLETE, - MMC_WRITE_ACTIVE | MMC_READ_ACTIVE, MMC_STATUS_DMA_INTERRUPT, sdmmc_flush_bounce_buffer); - - - return rc; -} - - -/** - * Returns the block order for a given operation on the MMC controller. - * - * @param mmc The MMC controller for which we're quierying block size. - * @param is_write True iff the given operation is a write. - */ -static uint8_t sdmmc_get_block_order(struct mmc *mmc, bool is_write) -{ - if (is_write) - return mmc->write_block_order; - else - return mmc->read_block_order; -} - - -/** - * Returns the block size for a given operation on the MMC controller. - * - * @param mmc The MMC controller for which we're quierying block size. - * @param is_write True iff the given operation is a write. - */ -static uint32_t sdmmc_get_block_size(struct mmc *mmc, bool is_write) -{ - return (1 << sdmmc_get_block_order(mmc, is_write)); -} - - -/** - * Handles execution of a DATA stage using the CPU, rather than by using DMA. - * - * @param mmc The MMc controller to work with. - * @param blocks The number of blocks to work with. - * @param is_write True iff the data is being set _to_ the CARD. - * @param data_buffer The data buffer to be transmitted or populated. - * - * @return 0 on success, or an error code on failure. - */ -static int sdmmc_handle_cpu_transfer(struct mmc *mmc, uint16_t blocks, bool is_write, void *data_buffer) -{ - uint16_t blocks_remaining = blocks; - uint16_t bytes_remaining_in_block; - - uint32_t timebase = get_time(); - - // Get a window that lets us work with the data buffer in 32-bit chunks. - uint32_t *buffer = data_buffer; - - // Figure out the mask to check based on whether this is a read or a write. - uint32_t mask = is_write ? MMC_BUFFER_WRITE_ENABLE : MMC_BUFFER_READ_ENABLE; - - // While we have blocks left to read... - while (blocks_remaining) { - - // Get the number of bytes per block read. - bytes_remaining_in_block = sdmmc_get_block_size(mmc, false); - - // Wait for a block read to complete. - while (!(mmc->regs->present_state & mask)) { - - // If an error occurs, return it. - if (mmc->regs->int_status & MMC_STATUS_ERROR_MASK) { - return (mmc->regs->int_status & MMC_STATUS_ERROR_MASK); - } - - // Check for timeout. - if (get_time_since(timebase) > mmc->timeout) - return ETIMEDOUT; - } - - // While we've still bytes left to read. - while (bytes_remaining_in_block) { - - // Check for timeout. - if (get_time_since(timebase) > mmc->timeout) - return ETIMEDOUT; - - // Transfer the data to the relevant - if (is_write) { - if ((uintptr_t)buffer & 3) { - // Handle unaligned buffers - uint32_t w; - uint8_t *data = (uint8_t *)buffer; - w = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); - mmc->regs->buffer = w; - } else { - mmc->regs->buffer = *buffer; - } - } else { - if ((uintptr_t)buffer & 3) { - // Handle unaligned buffers - uint32_t w = mmc->regs->buffer; - uint8_t *data = (uint8_t *)buffer; - data[0] = w & 0xFF; - data[1] = (w >> 8) & 0xFF; - data[2] = (w >> 16) & 0xFF; - data[3] = (w >> 24) & 0xFF; - } else { - *buffer = mmc->regs->buffer; - } - } - - // Advance by a register size... - bytes_remaining_in_block -= sizeof(mmc->regs->buffer); - ++buffer; - } - - // Advice by a block... - --blocks_remaining; - } - - return 0; -} - -/** - * 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. - * @param auto_termiante True iff we should instruct the system - * to reclaim the data lines after a transaction. - */ -static void sdmmc_prepare_command_data(struct mmc *mmc, uint16_t blocks, - bool is_write, bool auto_terminate, bool use_dma, int argument) -{ - if (blocks) { - uint16_t block_size = sdmmc_get_block_size(mmc, is_write); - - // If we're using DMA, target our bounce buffer. - if (use_dma) - mmc->regs->dma_address = (uint32_t)sdmmc_bounce_buffer; - - // Set up the DMA block size and count. - // This is synchronized with the size of our bounce buffer. - mmc->regs->block_size = sdmmc_bounce_dma_boundary | block_size; - mmc->regs->block_count = blocks; - } - - // Populate the command argument. - mmc->regs->argument = argument; - - if (blocks) { - uint32_t to_write = MMC_TRANSFER_LIMIT_BLOCK_COUNT; - - // If this controller should use DMA, set that up. - if (use_dma) - to_write |= MMC_TRANSFER_DMA_ENABLE; - - // If this is a multi-block datagram, indicate so. - if (blocks > 1) - to_write |= MMC_TRANSFER_MULTIPLE_BLOCKS; - - // If this command should automatically terminate, set the host to - // terminate it after the block span is complete. - if (auto_terminate) { - // If we're in SDR104, use AUTO_CMD23 intead of AUTO_CMD12, - // per the host controller specification. - if (mmc->operating_speed == SDMMC_SPEED_SDR104) - to_write |= MMC_TRANSFER_AUTO_CMD23; - else - to_write |= MMC_TRANSFER_AUTO_CMD12; - } - - // If this is a read, set the READ mode. - if (!is_write) - to_write |= MMC_TRANSFER_CARD_TO_HOST; - - mmc->regs->transfer_mode = to_write; - } - -} - - -/** - * 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, enum sdmmc_response_checks checks) -{ - // Populate the command number - uint16_t to_write = (command << MMC_COMMAND_NUMBER_SHIFT) | (response_type << MMC_COMMAND_RESPONSE_TYPE_SHIFT) | checks; - - // If this is a "stop transmitting" command, set the abort flag. - if (command == CMD_STOP_TRANSMISSION) - to_write |= MMC_COMMAND_TYPE_ABORT; - - // If this command has a data stage, include it. - // Note that tuning commands are atypical, but are considered to have a data stage - // consiting of the tuning pattern. - 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; -} - - -/** - * Enables or disables the SDMMC interrupts. - * We leave these masked, but checkt their status in their status register. - * - * @param mmc The eMMC device to work with. - * @param enabled True if interrupts should enabled, or false to disable them. - */ -static void sdmmc_enable_interrupts(struct mmc *mmc, bool enabled) -{ - // Get an mask that represents all interrupts. - uint32_t all_interrupts = - MMC_STATUS_COMMAND_COMPLETE | MMC_STATUS_TRANSFER_COMPLETE | - MMC_STATUS_DMA_INTERRUPT | MMC_STATUS_ERROR_MASK; - - // Clear any pending interrupts. - mmc->regs->int_status = all_interrupts; - - // And enable or disable the pseudo-interrupts. - if (enabled) { - mmc->regs->int_enable |= all_interrupts; - } else { - mmc->regs->int_enable &= ~all_interrupts; - } -} - -/** - * Handle the response to an SDMMC command, copying the data - * from the SDMMC response holding area to the user-provided response buffer. - */ -static int sdmmc_handle_command_response(struct mmc *mmc, - enum sdmmc_response_type type, void *response_buffer) -{ - uint32_t *buffer = (uint32_t *)response_buffer; - int rc; - - // If we don't have a place to put the response, - // skip copying it out. - if (!response_buffer) - return 0; - - - switch (type) { - - // Easy case: we don't have a response. We don't need to do anything. - case MMC_RESPONSE_NONE: - break; - - // If we have a response we have to wait on busy-completion for, - // wait for the DAT0 line to clear. - case MMC_RESPONSE_LEN48_CHK_BUSY: - mmc_print(mmc, "waiting for card to stop being busy..."); - rc = sdmmc_wait_until_no_longer_busy(mmc); - if (rc) { - mmc_print(mmc, "failure waiting for card to finish being busy (%d)", rc); - return rc; - } - // (fall-through) - - // If we have a 48-bit response, then we have 32 bits of response and 16 bits of CRC/command. - // The naming is a little odd, but that's thanks to the SDMMC standard. - case MMC_RESPONSE_LEN48: - *buffer = mmc->regs->response[0]; - break; - - // If we have a 136-bit response, we have 128 of response and 8 bits of CRC. - // TODO: validate that this is the right format/endianness/everything - case MMC_RESPONSE_LEN136: - - // Copy the response to the buffer manually. - // We avoid memcpy here, because this is volatile. - for(int i = 0; i < 4; ++i) - buffer[i] = mmc->regs->response[i]; - break; - - default: - mmc_print(mmc, "invalid response type; not handling response"); - } - - return 0; -} - - -/** - * Sends a command to the SD card, and awaits a response. - * - * @param mmc The SDMMC device to be used to transmit the command. - * @param response_type The type of response to expect-- mostly specifies the length. - * @param checks Determines which sanity checks the host controller should run. - * @param argument The argument to the SDMMC command. - * @param response_buffer A buffer to store the response. Should be at uint32_t for a LEN48 command, - * or 16 bytes for a LEN136 command. If this arguemnt is NULL, no response will be returned. - * @param blocks_to_transfer The number of SDMMC blocks to be transferred with the given command, - * or 0 to indicate that this command should not expect response data. - * @param is_write True iff the given command issues data _to_ the card, instead of vice versa. - * @param auto_terminate True iff the gven command needs to be terminated with e.g. CMD12 - * @param data_buffer A byte buffer that either contains the data to be sent, or which should - * receive data, depending on the is_write argument. - * - * @returns 0 on success, an error number on failure - */ -static int sdmmc_send_command(struct mmc *mmc, enum sdmmc_command command, - enum sdmmc_response_type response_type, enum sdmmc_response_checks checks, - uint32_t argument, void *response_buffer, uint16_t blocks_to_transfer, - bool is_write, bool auto_terminate, void *data_buffer) -{ - uint32_t total_data_to_xfer = sdmmc_get_block_size(mmc, is_write) * blocks_to_transfer; - int rc; - - // Store user data buffer for use by future DMA operations. - mmc->active_data_buffer = (uint32_t)data_buffer; - - // Sanity check: if this is a data transfer, make sure we have a data buffer... - if (blocks_to_transfer && !data_buffer) { - mmc_print(mmc, "WARNING: no data buffer provided, but this is a data transfer!"); - mmc_print(mmc, "this does nothing; but is supported for debug"); - } - - // Wait until we can issue commands to the device. - 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; - } - - // If this is a data command, or a command that uses the data lines for busy-detection. - if (blocks_to_transfer || (response_type == MMC_RESPONSE_LEN48_CHK_BUSY)) { - rc = sdmmc_wait_for_data_readiness(mmc); - if (rc) { - mmc_print(mmc, "card not willing to accept data-commands (%d / %08x)", rc, mmc->regs->present_state); - return -EBUSY; - } - } - - // Periodically recalibrate the SD controller - if (mmc->controller == SWITCH_MICROSD) { - sdmmc_run_autocal(mmc, true); - } - - // If we have data to send, prepare it. - sdmmc_prepare_command_data(mmc, blocks_to_transfer, is_write, auto_terminate, mmc->use_dma, argument); - - // If this is a write and we have data, we'll need to populate the bounce buffer before - // issuing the command. - if (blocks_to_transfer && is_write && mmc->use_dma && data_buffer) - memcpy(sdmmc_bounce_buffer, (void *)mmc->active_data_buffer, total_data_to_xfer); - - // Ensure we get the status response we want. - sdmmc_enable_interrupts(mmc, true); - - // Configure the controller to send the command. - sdmmc_prepare_command_registers(mmc, blocks_to_transfer, command, response_type, checks); - - // Wait for the command to be completed. - rc = sdmmc_wait_for_command_completion(mmc); - if (rc) { - mmc_print(mmc, "failed to issue %s (arg=%x, rc=%d)", sdmmc_get_command_string(command), argument, rc); - mmc_print_command_errors(mmc, rc); - - sdmmc_enable_interrupts(mmc, false); - return rc; - } - - // Copy the response received to the output buffer, if applicable. - rc = sdmmc_handle_command_response(mmc, response_type, response_buffer); - if (rc) { - mmc_print(mmc, "failed to handle %s response! (%d)", sdmmc_get_command_string(command), rc); - return rc; - } - - // If we had a data stage, handle it. - if (blocks_to_transfer) { - - // If this is a DMA transfer, wait for its completion. - if (mmc->use_dma) { - - // Wait for the transfer to be complete... - rc = sdmmc_wait_for_transfer_completion(mmc); - if (rc) { - mmc_print(mmc, "failed to complete %s data stage via DMA (%d)", sdmmc_get_command_string(command), command, rc); - sdmmc_enable_interrupts(mmc, false); - return rc; - } - - // If this is a read, and we've just finished a transfer, copy the data from - // our bounce buffer to the target data buffer. - if (!is_write && data_buffer) - sdmmc_flush_bounce_buffer(mmc); - } - // Otherwise, perform the transfer using the CPU. - else { - rc = sdmmc_handle_cpu_transfer(mmc, blocks_to_transfer, is_write, data_buffer); - if (rc) { - mmc_print(mmc, "failed to complete CMD%d data stage via CPU (%d)", command, rc); - sdmmc_enable_interrupts(mmc, false); - return rc; - } - } - } - - // Disable resporting psuedo-interrupts. - // (This is mostly for when the GIC is brought up) - sdmmc_enable_interrupts(mmc, false); - - mmc_debug(mmc, "completed %s.", sdmmc_get_command_string(command)); - return 0; -} - - -/** - * Convenience function that sends a simple SDMMC command - * and awaits response. Wrapper around sdmmc_send_command. - * - * @param mmc The SDMMC device to be used to transmit the command. - * @param response_type The type of response to expect-- mostly specifies the length. - * @param argument The argument to the SDMMC command. - * @param response_buffer A buffer to store the response. Should be at uint32_t for a LEN48 command, - * or 16 bytes for a LEN136 command. - * - * @returns 0 on success, an error number on failure - */ -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) -{ - // If we don't expect a response, don't check; otherwise check everything. - enum sdmmc_response_checks checks = (response_type == MMC_RESPONSE_NONE) ? MMC_CHECKS_NONE : MMC_CHECKS_ALL; - - // Deletegate the full checks function. - return sdmmc_send_command(mmc, command, response_type, checks, argument, response_buffer, 0, false, false, NULL); -} - - -/** - * Sends an SDMMC application command. - * - * @param mmc The SDMMC device to be used to transmit the command. - * @param response_type The type of response to expect-- mostly specifies the length. - * @param checks Determines which sanity checks the host controller should run. - * @param argument The argument to the SDMMC command. - * @param response_buffer A buffer to store the response. Should be at uint32_t for a LEN48 command, - * or 16 bytes for a LEN136 command. - * - * @returns 0 on success, an error number on failure - */ -static int sdmmc_send_app_command(struct mmc *mmc, enum sdmmc_command command, - enum sdmmc_response_type response_type, enum sdmmc_response_checks checks, - uint32_t argument, void *response_buffer, uint16_t blocks_to_transfer, - bool auto_terminate, void *data_buffer) -{ - int rc; - - // First, send the application command. - rc = sdmmc_send_simple_command(mmc, CMD_APP_COMMAND, MMC_RESPONSE_LEN48, mmc->relative_address << 16, NULL); - if (rc) { - mmc_print(mmc, "failed to prepare application command %s! (%d)", sdmmc_get_command_string(command), rc); - return rc; - } - - // And issue the body of the command. - return sdmmc_send_command(mmc, command, response_type, checks, argument, response_buffer, - blocks_to_transfer, false, auto_terminate, data_buffer); -} - - -/** - * Sends an SDMMC application command. - * - * @param mmc The SDMMC device to be used to transmit the command. - * @param response_type The type of response to expect-- mostly specifies the length. - * @param checks Determines which sanity checks the host controller should run. - * @param argument The argument to the SDMMC command. - * @param response_buffer A buffer to store the response. Should be at uint32_t for a LEN48 command, - * or 16 bytes for a LEN136 command. - * - * @returns 0 on success, an error number on failure - */ -static int sdmmc_send_simple_app_command(struct mmc *mmc, enum sdmmc_command command, - enum sdmmc_response_type response_type, enum sdmmc_response_checks checks, - uint32_t argument, void *response_buffer) -{ - // Deletegate to the full app command function. - return sdmmc_send_app_command(mmc, command, response_type, checks, argument, response_buffer, 0, false, NULL); -} - - -/** - * Reads a collection of bits from the CSD register. - * - * @param csd An array of four uint32_ts containing the CSD. - * @param start The bit number to start at. - * @param width. The width of the relveant read. - * - * @returns the extracted bits - */ -static uint32_t sdmmc_extract_csd_bits(uint32_t *csd, int start, int width) -{ - uint32_t relevant_dword, result; - int offset_into_dword, bits_into_next_dword; - - // Sanity check our span. - if ((start + width) > 128) { - printk("MMC ERROR: invalid CSD slice!\n"); - return 0xFFFFFFFF; - } - - // Figure out where the relevant range is in our CSD. - relevant_dword = csd[start / 32]; - offset_into_dword = start % 32; - - // Grab all the bits we can from the relevant DWORD. - result = relevant_dword >> offset_into_dword; - - // Special case: if we spanned a word boundary, we'll - // need to read one word. - // - // FIXME: I'm writing this at 5AM, and this requires basic arithemtic, - // my greatest weakness. This is going to be stupid wrong. - if (offset_into_dword + width > 32) { - bits_into_next_dword = (offset_into_dword + width) - 32; - - // Grab the next dword in the CSD... - relevant_dword = csd[(start / 32) + 1]; - - // ... mask away the bits higher than the bits we want... - relevant_dword &= (1 << (bits_into_next_dword)) - 1; - - // .. and shift the relevant bits up to their position. - relevant_dword <<= (width - bits_into_next_dword); - - // Finally, combine in the new word. - result |= relevant_dword; - } - - return result; -} - - -/** - * Parses a fetched CSD per the Version 1 standard. - * - * @param mmc The MMC structure to be populated. - * @param csd A four-dword array containing the read CSD. - * - * @returns int 0 on success, or an error code if the CSD appears invalid - */ -static int sdmmc_parse_csd_version1(struct mmc *mmc, uint32_t *csd) -{ - // Get the maximum allowed read-block size. - mmc->read_block_order = sdmmc_extract_csd_bits(csd, MMC_CSD_V1_READ_BL_LENGTH_START, MMC_CSD_V1_READ_BL_LENGTH_WIDTH); - - // TODO: handle other attributes - return 0; -} - - -/** - * Decides on a block transfer sized based on the information observed, - * and applies it to the card. - * - * @param mmc The controller to use to set the order - * @param block_order The order (log-base-2) of the block size to be used. - */ -static int sdmmc_use_block_size(struct mmc *mmc, int block_order) -{ - int rc; - - // Inform the card of the block size we'll want to use. - rc = sdmmc_send_simple_command(mmc, CMD_SET_BLKLEN, MMC_RESPONSE_LEN48, 1 << block_order, NULL); - if (rc) { - mmc_print(mmc, "could not fetch the CID"); - return ENODEV; - } - - // On success, store the relevant block size. - mmc->read_block_order = block_order; - mmc->write_block_order = block_order; - return 0; -} - - -/** - * Reads the active SD card's SD Configuration Register, and updates the object's properties. - * - * @param mmc The controller with which to query and to update. - * @returns 0 on success, or an errno on failure - */ -static int sdmmc_read_and_parse_scr(struct mmc *mmc) -{ - int rc; - struct sdmmc_scr scr; - - // Read the current block order, so we can restore it. - int original_block_order = sdmmc_get_block_order(mmc, false); - - // Always request a single 8-byte block. - const int block_order = 3; - const int num_blocks = 1; - - // Momentarily step down to a smaller block size, so we don't - // have to allocate a huge buffer for this command. - mmc->read_block_order = block_order; - - // Request the CSD from the device. - rc = sdmmc_send_app_command(mmc, CMD_APP_SEND_SCR, MMC_RESPONSE_LEN48, MMC_CHECKS_ALL, 0, NULL, num_blocks, false, &scr); - if (rc) { - mmc_print(mmc, "could not get the card's SCR!"); - mmc->read_block_order = original_block_order; - return rc; - } - - // Store the SCR data. - mmc->spec_version = scr.spec_version; - - // Restore the original block order. - mmc->read_block_order = original_block_order; - - return 0; -} - - -/** - * Reads the active MMC card's Card Specific Data, and updates the MMC object's properties. - * - * @param mmc The MMC to be queired and updated. - * @returns 0 on success, or an errno on failure - */ -static int sdmmc_read_and_parse_csd(struct mmc *mmc) -{ - int rc; - uint32_t csd[4]; - uint16_t csd_version; - - // Request the CSD from the device. - rc = sdmmc_send_simple_command(mmc, CMD_SEND_CSD, MMC_RESPONSE_LEN136, mmc->relative_address << 16, csd); - if (rc) { - mmc_print(mmc, "could not get the card's CSD!"); - return ENODEV; - } - - // Figure out the CSD version. - csd_version = sdmmc_extract_csd_bits(csd, MMC_CSD_STRUCTURE_START, MMC_CSD_STRUCTURE_WIDTH); - - // Handle each CSD version. - switch (csd_version) { - - // Handle version 1 CSDs. - // (The Switch eMMC appears to always use ver1 CSDs.) - case MMC_CSD_VERSION1: - return sdmmc_parse_csd_version1(mmc, csd); - - // For now, don't support any others. - default: - mmc_print(mmc, "ERROR: we don't currently support cards with v%d CSDs!", csd_version); - return ENOTTY; - } -} - - -/** - * Reads the active MMC card's Card Specific Data, and updates the MMC object's properties. - * - * @param mmc The MMC to be queired and updated. - * @returns 0 on success, or an errno on failure - */ -static int sdmmc_read_and_parse_ext_csd(struct mmc *mmc) -{ - int rc; - uint8_t ext_csd[MMC_EXT_CSD_SIZE]; - - // Read the single EXT CSD block. - rc = sdmmc_send_command(mmc, CMD_SEND_EXT_CSD, MMC_RESPONSE_LEN48, - MMC_CHECKS_ALL, 0, NULL, 1, false, false, ext_csd); - if (rc) { - mmc_print(mmc, "ERROR: failed to read the extended CSD!"); - return rc; - } - - /** - * Parse the extended CSD: - */ - - // Hardware partition support. - mmc->partition_support = ext_csd[MMC_EXT_CSD_PARTITION_SUPPORT]; - mmc->partition_config = ext_csd[MMC_EXT_CSD_PARTITION_CONFIG] & ~MMC_EXT_CSD_PARTITION_SELECT_MASK; - mmc->partition_switch_time = ext_csd[MMC_EXT_CSD_PARTITION_SWITCH_TIME] * MMC_EXT_CSD_PARTITION_SWITCH_SCALE_US; - mmc->partitioned = ext_csd[MMC_EXT_CSD_PARTITION_SETTING_COMPLETE] & MMC_EXT_CSD_PARTITION_SETTING_COMPLETED; - mmc->partition_attribute = ext_csd[MMC_EXT_CSD_PARTITION_ATTRIBUTE]; - - // Speed support. - mmc->mmc_card_type = ext_csd[MMC_EXT_CSD_CARD_TYPE]; - - return 0; -} - - -/** - * Switches the SDMMC card and controller to the fullest bus width possible. - * - * @param mmc The MMC controller to switch up to a full bus width. - */ -static int sdmmc_mmc_switch_bus_width(struct mmc *mmc, enum sdmmc_bus_width width) -{ - // Ask the card to adjust to the wider bus width. - int rc = mmc->switch_mode(mmc, MMC_SWITCH_EXTCSD_NORMAL, - MMC_BUS_WIDTH, width, mmc->timeout, NULL); - if (rc) { - mmc_print(mmc, "could not switch mode on the card side!"); - return rc; - } - - // Apply the same changes on the host side. - mmc->regs->host_control &= ~MMC_HOST_BUS_WIDTH_MASK; - switch (width) { - case MMC_BUS_WIDTH_4BIT: - mmc->regs->host_control |= MMC_HOST_BUS_WIDTH_4BIT; - break; - case MMC_BUS_WIDTH_8BIT: - mmc->regs->host_control |= MMC_HOST_BUS_WIDTH_8BIT; - break; - default: - break; - } - return 0; -} - - -/** - * Switches the SDMMC card and controller to the fullest bus width possible. - * - * @param mmc The MMC controller to switch up to a full bus width. - */ -static int sdmmc_sd_switch_bus_width(struct mmc *mmc, enum sdmmc_bus_width width) -{ - // By default, SD DAT3 is used for card detect. We'll need to - // release it from this function by dropping its pull-up resistor - // before we can use the line for data. Do so. - int rc = sdmmc_send_simple_app_command(mmc, CMD_APP_SET_CARD_DETECT, - MMC_RESPONSE_LEN48, MMC_CHECKS_ALL, 0, NULL); - if (rc) { - mmc_print(mmc, "could not stop using DAT3 as a card detect!"); - return rc; - } - - // Ask the card to adjust to the wider bus width. - rc = sdmmc_send_simple_app_command(mmc, CMD_APP_SWITCH_WIDTH, - MMC_RESPONSE_LEN48, MMC_CHECKS_ALL, width, NULL); - if (rc) { - mmc_print(mmc, "could not switch mode on the card side!"); - return rc; - } - - // Apply the same changes on the host side. - mmc->regs->host_control &= ~MMC_HOST_BUS_WIDTH_MASK; - if (mmc->max_bus_width == SD_BUS_WIDTH_4BIT) { - mmc->regs->host_control |= MMC_HOST_BUS_WIDTH_4BIT; - } - - return 0; -} - - -/** - * Optimize our SDMMC transfer mode to fully utilize the bus. - */ -static int sdmmc_optimize_transfer_mode(struct mmc *mmc) -{ - int rc; - - // Switch the device to its maximum bus width. - rc = mmc->switch_bus_width(mmc, mmc->max_bus_width); - if (rc) { - mmc_print(mmc, "could not switch the controller's bus width!"); - return rc; - } - - // Automatically optimize speed as much as is possible. How this works depends on - // the type of card. - rc = mmc->optimize_speed(mmc); - if (rc) { - mmc_print(mmc, "could not optimize the controller's speed!"); - return rc; - } - - return 0; -} - - -/** - * Switches the active card and host controller to the given speed mode. - * - * @param mmc The MMC controller to affect. - * @param speed The speed to switch to. - * @param status_out The status result of the relevant switch operation. - */ -static int sdmmc_switch_bus_speed(struct mmc *mmc, enum sdmmc_bus_speed speed) -{ - int rc; - - // Ask the card to switch to the new speed. - rc = mmc->card_switch_bus_speed(mmc, speed); - if (rc) { - mmc_print(mmc, "WARNING: could not switch card to %s", sdmmc_get_speed_string(speed)); - return rc; - } - - // Switch the host controller so it talks the relevant speed. - rc = sdmmc_apply_clock_speed(mmc, speed, true); - if (rc) { - mmc_print(mmc, "WARNING: could not switch the host to %s", sdmmc_get_speed_string(speed)); - - // Fall back to the original speed before returning.. - sdmmc_apply_clock_speed(mmc, mmc->operating_speed, true); - return rc; - } - - mmc_debug(mmc, "now operating at %s!", sdmmc_get_speed_string(speed)); - return 0; -} - - -/** - * Switches the active SD card and host controller to the given speed mode. - * - * @param mmc The MMC controller to affect. - * @param speed The speed to switch to. - * @param status_out The status result of the relevant switch operation. - */ -static int sdmmc_sd_switch_bus_speed(struct mmc *mmc, enum sdmmc_bus_speed speed) -{ - struct sdmmc_function_status status_out; - int rc; - - // Read the supported functions from the card. - rc = mmc->switch_mode(mmc, SDMMC_SWITCH_MODE_MODE_APPLY, SDMMC_SWITCH_MODE_ACCESS_MODE, speed, 0, &status_out); - if (rc) { - mmc_print(mmc, "WARNING: could not issue the bus speed switch"); - return rc; - } - - // Check that the active operating mode has switched to the new mode. - if (status_out.active_access_mode != speed) { - mmc_print(mmc, "WARNING: device did not accept mode %s!", sdmmc_get_speed_string(speed)); - mmc_print(mmc, " reported mode is %s / %d", sdmmc_get_speed_string(status_out.active_access_mode), status_out.active_access_mode); - return EINVAL; - } - - return 0; -} - - -/** - * Switches the active MMC card and host controller to the given speed mode. - * - * @param mmc The MMC controller to affect. - * @param speed The speed to switch to. - * @param status_out The status result of the relevant switch operation. - */ -static int sdmmc_mmc_switch_bus_speed(struct mmc *mmc, enum sdmmc_bus_speed speed) -{ - int rc; - uint8_t ext_csd[MMC_EXT_CSD_SIZE]; - - // To disambiguate constants, we add ten to every MMC speed constant. - // we have to undo this before sending the constants out to the card. - uint32_t argument = speed - MMC_SPEED_MMC_OFFSET; - - // Read the supported functions from the card. - rc = mmc->switch_mode(mmc, MMC_SWITCH_MODE_WRITE_BYTE, MMC_HS_TIMING, argument, 0, NULL); - if (rc) { - mmc_print(mmc, "WARNING: could not issue the mode switch"); - return rc; - } - - // Read the single EXT-CSD block, which contains the current speed. - rc = sdmmc_send_command(mmc, CMD_SEND_EXT_CSD, MMC_RESPONSE_LEN48, - MMC_CHECKS_ALL, 0, NULL, 1, false, false, ext_csd); - if (rc) { - mmc_print(mmc, "ERROR: failed to read the extended CSD after mode-switch!"); - return rc; - } - - // Check the ext-csd to make sure the change took. - if (ext_csd[MMC_EXT_CSD_HS_TIMING] != argument) { - mmc_print(mmc, "WARNING: device did not accept mode %s!", sdmmc_get_speed_string(speed)); - mmc_print(mmc, " reported mode is %s / %d", sdmmc_get_speed_string(ext_csd[MMC_EXT_CSD_HS_TIMING]), ext_csd[MMC_EXT_CSD_HS_TIMING]); - return EINVAL; - } - - return 0; -} - - -/** - * Optimize our SDMMC transfer speed by maxing out the bus as much as is possible. - */ -static int sdmmc_sd_optimize_speed(struct mmc *mmc) -{ - int rc; - struct sdmmc_function_status function_status; - - // We started off with the controller opearting with a divided clock, - // which is meant to keep us within the pre-init operating frequency of 400kHz. - // We're now set up, so we can drop the divider. From this point forward, the - // clock is now driven by the CAR frequency. - rc = sdmmc_apply_clock_speed(mmc, SDMMC_SPEED_SDR12, true); - if (rc) { - mmc_print(mmc, "WARNING: could not step up to normal operating speeds!"); - mmc_print(mmc, " ... expect delays."); - return rc; - } - mmc_debug(mmc, "now operating at 12.5MB/s!"); - - // If we're not at a full bus width, we can't switch to a higher speed. - // We're already optimal, vacuously succeed. - if (mmc->max_bus_width != SD_BUS_WIDTH_4BIT) - return 0; - - // Read the supported functions from the card. - rc = mmc->switch_mode(mmc, SDMMC_SWITCH_MODE_MODE_QUERY, SDMMC_SWITCH_MODE_NO_GROUP, 0, 0, &function_status); - if (rc) { - mmc_print(mmc, "WARNING: could not query card mode status; speed will be limited"); - return rc; - } - - // Debug information for very vebose modes. - mmc_debug(mmc, "this card supports the following speed modes:"); - mmc_debug(mmc, "SDR12: %d SDR25: %d SDR50: %d SDR104: %d DDR50: %d\n", - function_status.sdr12_support, function_status.sdr25_support, - function_status.sdr50_support, function_status.sdr104_support, - function_status.ddr50_support); - - // If we're operating in a UHS-compatbile low-voltage mode, check for UHS-modes. - if (mmc->operating_voltage == MMC_VOLTAGE_1V8) { - - // Try each of the UHS-I modes, we support. - if (function_status.sdr104_support && !sdmmc_switch_bus_speed(mmc, SDMMC_SPEED_SDR104)) - return 0; - if (function_status.sdr50_support && !sdmmc_switch_bus_speed(mmc, SDMMC_SPEED_SDR50)) - return 0; - } - - // If we support High Speed but not a UHS-I mode, use it. - if (function_status.sdr25_support) - return sdmmc_switch_bus_speed(mmc, SDMMC_SPEED_SDR25); - - mmc_debug(mmc, "couldn't improve speed above the speed already set."); - return 0; -} - - -/** - * Optimize our MMC transfer speed by maxing out the bus as much as is possible. - */ -static int sdmmc_mmc_optimize_speed(struct mmc *mmc) -{ - int rc; - bool hs52_support, hs200_support, hs400_support; - - // XXX - sdmmc_set_loglevel(3); - - // We started off with the controller opearting with a divided clock, - // which is meant to keep us within the pre-init operating frequency of 400kHz. - // We're now set up, so we can drop the divider. From this point forward, the - // clock is now driven by the CAR frequency. - rc = sdmmc_apply_clock_speed(mmc, SDMMC_SPEED_SDR12, true); - if (rc) { - mmc_print(mmc, "WARNING: could not step up to normal operating speeds!"); - mmc_print(mmc, " ... expect delays."); - return rc; - } - mmc_debug(mmc, "now operating at 25MB/s!"); - - // Determine which high-speed modes are supported, for easy reference below. - hs200_support = mmc->mmc_card_type & MMC_EXT_CSD_CARD_TYPE_HS400_1V8; - hs400_support = mmc->mmc_card_type & MMC_EXT_CSD_CARD_TYPE_HS400_1V8; - hs52_support = mmc->mmc_card_type & MMC_EXT_CSD_CARD_TYPE_HS52; - - // First, try modes that are only supported at 1V8 and below, - // if we can. - if (mmc->operating_voltage == MMC_VOLTAGE_1V8) { - //if (hs400_support && !sdmmc_switch_bus_speed(mmc, SDMMC_SPEED_HS400)) - // return 0; - if (hs200_support && !sdmmc_switch_bus_speed(mmc, SDMMC_SPEED_HS200)) - return 0; - - (void)hs400_support; - } - - // Next, try the legacy "high speed" mode. - if (hs52_support) - return sdmmc_switch_bus_speed(mmc, SDMMC_SPEED_HS52); - - mmc_debug(mmc, "couldn't improve speed above the default"); - return 0; -} - - -/** - * Requests that an MMC target use the card's current relative address. - * - * @param mmc The SDMMC controller to work with. - * @return 0 on success, or an error code on failure. - */ -static int sdmmc_set_relative_address(struct mmc *mmc) -{ - int rc; - - // Set up the card's relative address. - rc = sdmmc_send_simple_command(mmc, CMD_SET_RELATIVE_ADDR, MMC_RESPONSE_LEN48, mmc->relative_address << 16, NULL); - if (rc) { - mmc_print(mmc, "could not set the card's relative address! (%d)", rc); - return rc; - } - - return 0; -} - - -/** - * Requests that an SD target report a relative address for us to use - * to communicate with it. - * - * @param mmc The SDMMC controller to work with. - * @return 0 on success, or an error code on failure. - */ -static int sdmmc_get_relative_address(struct mmc *mmc) -{ - int rc; - uint32_t response; - - // Set up the card's relative address. - rc = sdmmc_send_simple_command(mmc, CMD_GET_RELATIVE_ADDR, MMC_RESPONSE_LEN48, 0, &response); - if (rc) { - mmc_print(mmc, "could not get the card's relative address! (%d)", rc); - return rc; - } - - // Apply the fetched relative address. - mmc->relative_address = response >> 16; - return 0; -} - - -/** - * Shared card initialization for SD and MMC cards. - * Used to bring the card fully online and gather information about the card. - * - * @param mmc The MMC controller that will perform the initilaization. - */ -static int sdmmc_card_init(struct mmc *mmc) -{ - int rc; - uint32_t response[4]; - - // Retreive the card ID. - rc = sdmmc_send_simple_command(mmc, CMD_ALL_SEND_CID, MMC_RESPONSE_LEN136, 0, response); - if (rc) { - mmc_print(mmc, "could not fetch the CID"); - return rc; - } - - // Store the card ID for later. - memcpy(mmc->cid, response, sizeof(mmc->cid)); - - // Establish a relative address to communicate with - rc = mmc->establish_relative_address(mmc); - if (rc) { - mmc_print(mmc, "could not establish a relative address! (%d)", rc); - return rc; - } - - // Read and handle card's Card Specific Data (CSD). - rc = sdmmc_read_and_parse_csd(mmc); - if (rc) { - mmc_print(mmc, "could not populate CSD attributes! (%d)", rc); - return rc; - } - - // Select our eMMC card, so it knows we're talking to it. - rc = sdmmc_send_simple_command(mmc, CMD_TOGGLE_CARD_SELECT, MMC_RESPONSE_LEN48, mmc->relative_address << 16, response); - if (rc) { - mmc_print(mmc, "could not select the active card for use! (%d)", rc); - return rc; - } - - // Set up a block transfer size of 512B blocks. - // 1) every card supports this, and 2) we use SDMA, which only supports up to 512B - rc = sdmmc_use_block_size(mmc, MMC_DEFAULT_BLOCK_ORDER); - if (rc) { - mmc_print(mmc, "could not set up block transfer sizes! (%d)", rc); - return rc; - } - - return 0; -} - - -/** - * Blocks until the eMMC card is fully initialized. - * - * @param mmc The MMC device that should do the waiting. - */ -static int sdmmc_mmc_wait_for_card_readiness(struct mmc *mmc) -{ - int rc; - uint32_t response[4]; - - - while (true) { - - uint32_t response_masked; - - // Ask the SD card to identify its state. It will respond with readiness and a capacity magic. - int original_loglevel = sdmmc_set_loglevel(0); - rc = sdmmc_send_command(mmc, CMD_SEND_OPERATING_CONDITIONS, MMC_RESPONSE_LEN48, - MMC_CHECKS_NONE, 0x40000080, response, 0, false, false, NULL); - sdmmc_set_loglevel(original_loglevel); - - if (rc) { - mmc_print(mmc, "ERROR: could not read the card's operating conditions!"); - return rc; - } - - // Validate that this is a valid Switch eMMC. - // Per the spec, any card greater than 2GiB should respond with this magic number. - response_masked = response[0] & MMC_EMMC_OPERATING_COND_CAPACITY_MASK; - if (response_masked != MMC_EMMC_OPERATING_COND_CAPACITY_MAGIC) { - mmc_print(mmc, "ERROR: this doesn't appear to be a valid Switch eMMC!"); - return ENOTTY; - } - - // If the device has just become ready, we're done! - response_masked = response[0] & MMC_EMMC_OPERATING_READINESS_MASK; - if (response_masked == MMC_EMMC_OPERATING_COND_READY) { - return 0; - } - } -} - - -/** - * Blocks until the SD card is fully initialized. - * - * @param mmc The MMC device that should do the waiting. - * @aparam response Out argument that recieves the final, ready command response. - * Should have roon for uint32_t. - */ -static int sdmmc_sd_wait_for_card_readiness(struct mmc *mmc, uint32_t *response) -{ - int rc; - uint32_t argument = MMC_SD_OPERATING_COND_ACCEPTS_3V3; - - // If this is a SDv2 or higher card, check for an SDHC card, - // and for low-voltage support. - if (mmc->spec_version >= SD_VERSION_2_0) { - argument |= MMC_SD_OPERATING_COND_HIGH_CAPACITY; - argument |= MMC_SD_OPERATING_COND_ACCEPTS_1V8; - } - - while (true) { - - // Ask the SD card to identify its state. - int original_loglevel = sdmmc_set_loglevel(0); - rc = sdmmc_send_simple_app_command(mmc, CMD_APP_SEND_OP_COND, - MMC_RESPONSE_LEN48, MMC_CHECKS_NONE, argument, response); - sdmmc_set_loglevel(original_loglevel); - if (rc) { - mmc_print(mmc, "ERROR: could not read the card's operating conditions!"); - return rc; - } - - // If the device has just become ready, we're done! - if (response[0] & MMC_SD_OPERATING_COND_READY) - return 0; - - // Wait a delay so we're not spamming the card incessantly. - udelay(1000); - } -} - - -/** - * Handles MMC-specific card initialization. - */ -static int sdmmc_mmc_card_init(struct mmc *mmc) -{ - int rc; - - mmc_debug(mmc, "setting up card as MMC"); - - // Bring the bus out of its idle state. - rc = sdmmc_send_simple_command(mmc, CMD_GO_IDLE_OR_INIT, MMC_RESPONSE_NONE, 0, NULL); - if (rc) { - mmc_print(mmc, "could not bring bus to idle!"); - return rc; - } - - // Wait for the card to finish being busy. - rc = sdmmc_mmc_wait_for_card_readiness(mmc); - if (rc) { - mmc_print(mmc, "card failed to come up! (%d)", rc); - return rc; - } - - // Run the common core card initialization. - rc = sdmmc_card_init(mmc); - if (rc) { - mmc_print(mmc, "failed to set up card (%d)!", rc); - return rc; - } - - // Read and handle card's Extended Card Specific Data (ext-CSD). - rc = sdmmc_read_and_parse_ext_csd(mmc); - if (rc) { - mmc_print(mmc, "could not populate extended-CSD attributes! (%d)", rc); - return EPIPE; - } - - return 0; -} - - -/** - * Evalutes a check pattern response (used with interface commands) - * and validates that it contains our common check pattern. - * - * @param response The response recieved after a given command. - * @return True iff the given response has a valid check pattern. - */ -static bool sdmmc_check_pattern_present(uint32_t response) -{ - uint32_t pattern_byte = response & 0xFF; - return pattern_byte == MMC_IF_CHECK_PATTERN; -} - - -/** - * Handles SD-specific card initialization. - */ -static int sdmmc_sd_card_init(struct mmc *mmc) -{ - int rc; - uint32_t ocr, response; - - mmc_debug(mmc, "setting up card as SD"); - - // Bring the bus out of its idle state. - rc = sdmmc_send_simple_command(mmc, CMD_GO_IDLE_OR_INIT, MMC_RESPONSE_NONE, 0, NULL); - if (rc) { - mmc_print(mmc, "could not bring bus to idle!"); - return rc; - } - - // Validate that the card can handle working with the voltages we can provide. - rc = sdmmc_send_simple_command(mmc, CMD_SEND_IF_COND, MMC_RESPONSE_LEN48, MMC_IF_VOLTAGE_3V3 | MMC_IF_CHECK_PATTERN, &response); - if (rc || !sdmmc_check_pattern_present(response)) { - - // TODO: This is either a broken, SDv1 or MMC card. - // Handle the latter two cases as best we can. - - mmc_print(mmc, "ERROR: this card isn't an SDHC card!"); - mmc_print(mmc, " we don't yet support low-capacity cards. :("); - return rc; - } - // If this responded, indicate that this is a v2 card. - else { - // store that this is a v2 card - mmc->spec_version = SD_VERSION_2_0; - } - - // Wait for the card to finish being busy. - rc = sdmmc_sd_wait_for_card_readiness(mmc, &ocr); - if (rc) { - mmc_print(mmc, "card failed to come up! (%d)", rc); - return rc; - } - - // If the response indicated this was a high capacity card, - // always use block addressing. - mmc->uses_block_addressing = !!(ocr & MMC_SD_OPERATING_COND_HIGH_CAPACITY); - - // If the card supports using 1V8, drop down using lower voltages. - if (mmc->allow_voltage_switching && ocr & MMC_SD_OPERATING_COND_ACCEPTS_1V8) { - if (mmc->operating_voltage != MMC_VOLTAGE_1V8) { - rc = mmc->switch_to_low_voltage(mmc); - if (rc) - mmc_print(mmc, "WARNING: could not switch to low-voltage mode! (%d)", rc); - } - } - - // Run the common core card initialization. - rc = sdmmc_card_init(mmc); - if (rc) { - mmc_print(mmc, "failed to set up card (%d)!", rc); - return rc; - } - - // Read the card's SCR. - rc = sdmmc_read_and_parse_scr(mmc); - if (rc) { - mmc_print(mmc, "failed to read SCR! (%d)!", rc); - return rc; - } - - return 0; -} - - -/** - * @returns true iff the given READ_STATUS response indicates readiness - */ -static bool sdmmc_status_indicates_readiness(uint32_t status) -{ - // If the card is currently programming, it's not ready. - if ((status & MMC_STATUS_MASK) == MMC_STATUS_PROGRAMMING) - return false; - - // Return true iff the card is ready for data. - return status & MMC_STATUS_READY_FOR_DATA; -} - - -/** - * Waits for card readiness; should be issued after e.g. enabling partitioning. - * - * @param mmc The MMC to wait on. - * @param 0 if the wait completed with the card being ready; or an error code otherwise - */ -static int sdmmc_wait_for_card_ready(struct mmc *mmc, uint32_t timeout) -{ - int rc; - uint32_t status; - - uint32_t timebase = get_time(); - - while (true) { - // Read the card's status. - rc = sdmmc_send_simple_command(mmc, CMD_READ_STATUS, MMC_RESPONSE_LEN48, mmc->relative_address << 16, &status); - - // Ensure we haven't timed out. - if (get_time_since(timebase) > timeout) - return ETIMEDOUT; - - // If we couldn't read, try again. - if (rc) - continue; - - // Check to see if we hit a fatal error. - if (status & MMC_STATUS_CHECK_ERROR) - return EPIPE; - - // Check for ready status. - if (sdmmc_status_indicates_readiness(status)) - return 0; - } -} - - -/** - * Issues a SWITCH_MODE command, which can be used to write registers on the MMC card's controller, - * and thus to e.g. switch partitions. - * - * @param mmc The MMC device to use for comms. - * @param mode The access mode with which to access the controller. - * @param field The field to access. - * @param value The argument to the access mode. - * @param timeout The timeout, which is often longer than the normal MMC timeout. - * - * @return 0 on success, or an error code on failure - */ -static int sdmmc_mmc_switch_mode(struct mmc *mmc, int mode, int field, int value, uint32_t timeout, void *unused) -{ - // Collapse our various parameters into a single argument. - uint32_t argument = - (mode << MMC_SWITCH_ACCESS_MODE_SHIFT) | - (field << MMC_SWITCH_FIELD_SHIFT) | - (value << MMC_SWITCH_VALUE_SHIFT); - - // Issue the switch mode command. - int rc = sdmmc_send_simple_command(mmc, CMD_SWITCH_MODE, MMC_RESPONSE_LEN48_CHK_BUSY, argument, NULL); - if (rc){ - mmc_print(mmc, "failed to issue SWITCH_MODE command! (%d / %d / %d; rc=%d)", mode, field, value, rc); - return rc; - } - - // Wait until we have a sense of the card status to return. - if (timeout != 0) { - rc = sdmmc_wait_for_card_ready(mmc, timeout); - if (rc){ - mmc_print(mmc, "failed to talk to the card after SWITCH_MODE (%d)", rc); - return rc; - } - } - - return 0; -} - - -/** - * @return True iff the given MMC card supports hardare partitions. - */ -static bool sdmmc_supports_hardware_partitions(struct mmc *mmc) -{ - return mmc->partition_support & MMC_SUPPORTS_HARDWARE_PARTS; -} - - -/** - * card detect method for built-in cards. - */ -bool sdmmc_builtin_card_present(struct mmc *mmc) -{ - return true; -} - -/** - * card detect method for GPIO-based card detects - */ -bool sdmmc_external_card_present(struct mmc *mmc) -{ - return !gpio_read(mmc->card_detect_gpio); -} - -/** - * Issues an SD card mode-switch command. - * - * @param mmc The controller to use. - * @param mode The mode flag -- one to set function data, zero to query. - * @param group The SD card function group-- see the SD card Physical Layer spec. Set this negative to not apply arguments. - * @param response 64-byte buffer to store the response. - */ -static int sdmmc_sd_switch_mode(struct mmc *mmc, int mode, int group, int value, uint32_t timeout, void *response) -{ - int rc; - - // Read the current block order, so we can restore it. - int original_block_order = sdmmc_get_block_order(mmc, false); - - // Always request a single 64-byte block. - const int block_order = 6; - const int num_blocks = 1; - - // Build the argument we're going to issue. - uint32_t argument = (mode << SDMMC_SWITCH_MODE_MODE_SHIFT) | SDMMC_SWITCH_MODE_ALL_FUNCTIONS_UNUSED; - - // If we have a group argument, apply the group and value. - if(group >= 0) { - const int group_shift = group * SDMMC_SWITCH_MODE_GROUP_SIZE_BITS; - argument &= ~(SDMMC_SWITCH_MODE_FUNCTION_MASK << group_shift); - argument |= value << group_shift; - } - - // Momentarily step down to a smaller block size, so we don't - // have to allocate a huge buffer for this command. - mmc->read_block_order = block_order; - - // Issue the command itself. - rc = sdmmc_send_command(mmc, CMD_SWITCH_MODE, MMC_RESPONSE_LEN48, MMC_CHECKS_ALL, argument, NULL, num_blocks, false, false, response); - if (rc) { - mmc_print(mmc, "could not issue switch command!"); - mmc->read_block_order = original_block_order; - return rc; - } - - // Restore the original block order. - mmc->read_block_order = original_block_order; - - return 0; -} - - -/** - * Switches a given SDMMC Controller where - */ -static void sdmmc_apply_card_type(struct mmc *mmc, enum sdmmc_card_type type) -{ - // Store the card type for our own reference... - mmc->card_type = type; - - // Set up our per-protocol function pointers. - switch (type) { - - // MMC-protoco cards - case MMC_CARD_EMMC: - case MMC_CARD_MMC: - mmc->card_init = sdmmc_mmc_card_init; - mmc->establish_relative_address = sdmmc_set_relative_address; - mmc->switch_mode = sdmmc_mmc_switch_mode; - mmc->switch_bus_width = sdmmc_mmc_switch_bus_width; - mmc->card_switch_bus_speed = sdmmc_mmc_switch_bus_speed; - mmc->optimize_speed = sdmmc_mmc_optimize_speed; - - break; - - // SD-protocol cards - case MMC_CARD_SD: - mmc->card_init = sdmmc_sd_card_init; - mmc->establish_relative_address = sdmmc_get_relative_address; - mmc->switch_mode = sdmmc_sd_switch_mode; - mmc->switch_bus_width = sdmmc_sd_switch_bus_width; - mmc->optimize_speed = sdmmc_sd_optimize_speed; - mmc->card_switch_bus_speed = sdmmc_sd_switch_bus_speed; - break; - - // Switch-cart protocol cards - case MMC_CARD_CART: - printk("BUG: trying to use an impossible code path, hanging!\n"); - while(true); - } -} - - -/** - * Populates the given MMC object with defaults for its controller. - * - * @param mmc The mmc object to populate. - */ -static int sdmmc_initialize_defaults(struct mmc *mmc) -{ - // Set up based on the controller - switch (mmc->controller) { - case SWITCH_EMMC: - mmc->name = "eMMC"; - mmc->max_bus_width = MMC_BUS_WIDTH_8BIT; - mmc->tuning_block_order = MMC_TUNING_BLOCK_ORDER_8BIT; - mmc->operating_voltage = MMC_VOLTAGE_1V8; - - // Set up function pointers for each of our per-instance functions. - mmc->set_up_clock_and_io = sdmmc4_set_up_clock_and_io; - mmc->enable_supplies = sdmmc4_enable_supplies; - mmc->switch_to_low_voltage = sdmmc_always_fail; - mmc->card_present = sdmmc_builtin_card_present; - mmc->configure_clock = sdmmc4_configure_clock; - - // The EMMC controller always uses an EMMC card. - sdmmc_apply_card_type(mmc, MMC_CARD_EMMC); - - // The Switch's eMMC always uses block addressing. - mmc->uses_block_addressing = true; - break; - - case SWITCH_MICROSD: - mmc->name = "uSD"; - mmc->card_type = MMC_CARD_SD; - mmc->max_bus_width = SD_BUS_WIDTH_4BIT; - mmc->tuning_block_order = MMC_TUNING_BLOCK_ORDER_4BIT; - mmc->operating_voltage = MMC_VOLTAGE_3V3; - mmc->card_detect_gpio = GPIO_MICROSD_CARD_DETECT; - - // Per-instance functions. - mmc->set_up_clock_and_io = sdmmc1_set_up_clock_and_io; - mmc->enable_supplies = sdmmc1_enable_supplies; - mmc->switch_to_low_voltage = sdmmc1_switch_to_low_voltage; - mmc->card_present = sdmmc_external_card_present; - mmc->configure_clock = sdmmc1_configure_clock; - - // For the microSD card slot, assume we have an SD-type card. - // Negotiation has a chance to change this, later. - sdmmc_apply_card_type(mmc, MMC_CARD_SD); - - // Start off assuming byte addressing; we'll detect and correct this - // later, if necessary. - mmc->uses_block_addressing = false; - break; - - default: - printk("ERROR: initialization not yet writen for SDMMC%d", mmc->controller + 1); - return ENOSYS; - } - - return 0; -} - - -/** - * Set up a new SDMMC driver. - * - * @param mmc The SDMMC structure to be initiailized with the device state. - * @param controler The controller description to be used; usually SWITCH_EMMC - * or SWITCH_MICROSD. - * @param allow_voltage_switching True if we should allow voltage switching, - * which may not make sense if we're about to chainload to another component without - * preseving the overall structure. - */ -int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller, bool allow_voltage_switching) -{ - int rc; - - // Get a reference to the registers for the relevant SDMMC controller. - mmc->controller = controller; - mmc->regs = sdmmc_get_regs(controller); - mmc->allow_voltage_switching = allow_voltage_switching; - - // Set the defaults for the card, including the default function pointers - // for the assumed card type, and the per-controller options. - rc = sdmmc_initialize_defaults(mmc); - if (rc) { - printk("ERROR: controller SDMMC%d not currently supported!\n", controller + 1); - return rc; - } - - // Default to a timeout of 1s. - mmc->timeout = 1000000; - mmc->partition_switch_time = 1000; - - // Use DMA, by default. - mmc->use_dma = true; - - // Don't allow writing unless the caller explicitly enables it. - mmc->write_enable = SDMMC_WRITE_DISABLED; - - // Default to relative address of zero. - mmc->relative_address = 0; - - // Initialize the raw SDMMC controller. - rc = sdmmc_hardware_init(mmc); - if (rc) { - mmc_print(mmc, "failed to set up controller! (%d)", rc); - return rc; - } - - // ... and verify that the card is there. - if (!mmc->card_present(mmc)) { - mmc_print(mmc, "ERROR: no card detected!"); - return ENODEV; - } - - // Handle the initialization that's specific to the card type. - rc = mmc->card_init(mmc); - if (rc) { - mmc_print(mmc, "failed to set run card-specific initialization (%d)!", rc); - return rc; - } - - // Switch to a transfer mode that can more efficiently utilize the bus. - rc = sdmmc_optimize_transfer_mode(mmc); - if (rc) { - mmc_print(mmc, "WARNING: could not optimize bus utlization! (%d)", rc); - } - - return 0; -} - - -/** - * Imports a SDMMC driver struct from another program. This mainly intended for stage2, - * so that it can reuse stage1's SDMMC struct instance(s). - * - * @param mmc The SDMMC structure to be imported. - */ -int sdmmc_import_struct(struct mmc *mmc) -{ - int rc; - bool uses_block_addressing = mmc->uses_block_addressing; - mmc->regs = sdmmc_get_regs(mmc->controller); - - rc = sdmmc_initialize_defaults(mmc); - if (rc) { - printk("ERROR: controller SDMMC%d not currently supported!\n", mmc->controller + 1); - return rc; - } - - mmc->uses_block_addressing = uses_block_addressing; - return 0; -} - - -/** - * Selects the active MMC partition. Can be used to select - * boot partitions for access. Affects all operations going forward. - * - * @param mmc The MMC controller whose card is to be used. - * @param partition The partition number to be selected. - * - * @return 0 on success, or an error code on failure. - */ -int sdmmc_select_partition(struct mmc *mmc, enum sdmmc_partition partition) -{ - uint16_t argument = partition; - int rc; - - // If we're trying to access hardware partitions on a device that doesn't support them, - // bail out. - if (!sdmmc_supports_hardware_partitions(mmc)) - return ENOTTY; - - // Set the PARTITION_CONFIG register to select the active partition. - mmc_print(mmc, "switching to partition %d", partition); - rc = mmc->switch_mode(mmc, MMC_SWITCH_MODE_WRITE_BYTE, MMC_PARTITION_CONFIG, argument, 0, NULL); - if (rc) { - mmc_print(mmc, "failed to select partition %d (%02x, rc=%d)", partition, argument, rc); - } - - mmc_print(mmc, "waiting for %d us", mmc->partition_switch_time); - udelay(mmc->partition_switch_time); - - return rc; -} - - -/** - * Reads a sector or sectors from a given SD/MMC card. - * - * @param mmc The MMC device to work with. - * @param buffer The output buffer to target. - * @param block The sector number to read. - * @param count The number of sectors to read. - * - * @return 0 on success, or an error code on failure. - */ -int sdmmc_read(struct mmc *mmc, void *buffer, uint32_t block, unsigned int count) -{ - uint32_t command = (count == 1) ? CMD_READ_SINGLE_BLOCK : CMD_READ_MULTIPLE_BLOCK; - - // Determine the argument, which indicates which address we're reading/writing. - uint32_t extent = block; - - // If this card uses byte addressing rather than sector addressing, - // multiply by the block size. - if (!mmc->uses_block_addressing) { - extent *= sdmmc_get_block_size(mmc, false); - } - - // Execute the relevant read. - return sdmmc_send_command(mmc, command, MMC_RESPONSE_LEN48, MMC_CHECKS_ALL, extent, NULL, count, false, count > 1, buffer); -} - -/** - * Releases the SDMMC write lockout, enabling access to the card. - * Note that by default, setting this to WRITE_ENABLED will not allow access to eMMC. - * Check the source for a third constant that can be used to enable eMMC writes. - * - * @param perms The permissions to apply-- typically WRITE_DISABLED or WRITE_ENABLED. - */ -void sdmmc_set_write_enable(struct mmc *mmc, enum sdmmc_write_permission perms) -{ - mmc->write_enable = perms; -} - - -/** - * Writes a sector or sectors to a given SD/MMC card. - * - * @param mmc The MMC device to work with. - * @param buffer The input buffer to write. - * @param block The sector number to write from. - * @param count The number of sectors to write. - * - * @return 0 on success, or an error code on failure. - */ -int sdmmc_write(struct mmc *mmc, const void *buffer, uint32_t block, unsigned int count) -{ - // Sanity check variables: we're especially careful about allowing writes to the switch eMMC. - bool is_emmc = (mmc->controller == SWITCH_EMMC); - bool allow_mmc_write = (mmc->write_enable == SDMMC_WRITE_ENABLED_INCLUDING_EMMC); - - uint32_t command = (count == 1) ? CMD_WRITE_SINGLE_BLOCK : CMD_WRITE_MULTIPLE_BLOCK; - - // Determine the argument, which indicates which address we're reading/writing. - uint32_t extent = block; - - // If we don't have an explict write enable, don't allow writes. - if (mmc->write_enable == SDMMC_WRITE_DISABLED) { - mmc_print(mmc, "tried to write to an external card, but write was not enabled!"); - return EACCES; - } - - // Explicitly protect the switch's eMMC to prevent bricks. - if (is_emmc && !allow_mmc_write) { - mmc_print(mmc, "cowardly refusing to write to the switch's eMMMC"); - return EACCES; - } - - // If this card uses byte addressing rather than sector addressing, - // multiply by the block size. - if (!mmc->uses_block_addressing) { - extent *= sdmmc_get_block_size(mmc, true); - } - - // Execute the relevant read. - return sdmmc_send_command(mmc, command, MMC_RESPONSE_LEN48, MMC_CHECKS_ALL, extent, NULL, count, true, count > 1, (void *)buffer); -} - -/** - * Checks to see whether an SD card is present. - * - * @mmc mmc The controller with which to check for card presence. - * @return true iff a card is present - */ -bool sdmmc_card_present(struct mmc *mmc) -{ - return mmc->card_present(mmc); -} - -/** - * Prints out all of the tegra_mmc struct fields - * - * @mmc mmc The controller with which to dump registers. - */ -void sdmmc_dump_regs(struct mmc *mmc) { - mmc_debug(mmc, "dma_address: 0x%08" PRIX32 "\n",mmc->regs->dma_address); - mmc_debug(mmc, "block_size: 0x%04" PRIX16 "\n",mmc->regs->block_size); - mmc_debug(mmc, "block_count: 0x%04" PRIX16 "\n",mmc->regs->block_count); - mmc_debug(mmc, "argument: 0x%08" PRIX32 "\n",mmc->regs->argument); - mmc_debug(mmc, "transfer_mode: 0x%04" PRIX16 "\n",mmc->regs->transfer_mode); - mmc_debug(mmc, "command: 0x%04" PRIX16 "\n",mmc->regs->command); - mmc_debug(mmc, "response[0]: 0x%08" PRIX32 "\n",mmc->regs->response[0]); - mmc_debug(mmc, "response[1]: 0x%08" PRIX32 "\n",mmc->regs->response[1]); - mmc_debug(mmc, "response[2]: 0x%08" PRIX32 "\n",mmc->regs->response[2]); - mmc_debug(mmc, "response[3]: 0x%08" PRIX32 "\n",mmc->regs->response[3]); - mmc_debug(mmc, "buffer: 0x%08" PRIX32 "\n",mmc->regs->buffer); - mmc_debug(mmc, "present_state: 0x%08" PRIX32 "\n",mmc->regs->present_state); - mmc_debug(mmc, "host_control: 0x%02" PRIX8 "\n",mmc->regs->host_control); - mmc_debug(mmc, "power_control: 0x%02" PRIX8 "\n",mmc->regs->power_control); - mmc_debug(mmc, "block_gap_control: 0x%02" PRIX8 "\n",mmc->regs->block_gap_control); - mmc_debug(mmc, "wake_up_control: 0x%02" PRIX8 "\n",mmc->regs->wake_up_control); - mmc_debug(mmc, "clock_control: 0x%04" PRIX16 "\n",mmc->regs->clock_control); - mmc_debug(mmc, "timeout_control: 0x%02" PRIX8 "\n",mmc->regs->timeout_control); - mmc_debug(mmc, "software_reset: 0x%02" PRIX8 "\n",mmc->regs->software_reset); - mmc_debug(mmc, "int_status: 0x%08" PRIX32 "\n",mmc->regs->int_status); - mmc_debug(mmc, "int_enable: 0x%08" PRIX32 "\n",mmc->regs->int_enable); - mmc_debug(mmc, "signal_enable: 0x%08" PRIX32 "\n",mmc->regs->signal_enable); - mmc_debug(mmc, "acmd12_err: 0x%04" PRIX16 "\n",mmc->regs->acmd12_err); - mmc_debug(mmc, "host_control2: 0x%04" PRIX16 "\n",mmc->regs->host_control2); - mmc_debug(mmc, "capabilities: 0x%08" PRIX32 "\n",mmc->regs->capabilities); - mmc_debug(mmc, "capabilities_1: 0x%08" PRIX32 "\n",mmc->regs->capabilities_1); - mmc_debug(mmc, "max_current: 0x%08" PRIX32 "\n",mmc->regs->max_current); - mmc_debug(mmc, "set_acmd12_error: 0x%04" PRIX16 "\n",mmc->regs->set_acmd12_error); - mmc_debug(mmc, "set_int_error: 0x%04" PRIX16 "\n",mmc->regs->set_int_error); - mmc_debug(mmc, "adma_error: 0x%04" PRIX16 "\n",mmc->regs->adma_error); - mmc_debug(mmc, "adma_address: 0x%08" PRIX32 "\n",mmc->regs->adma_address); - mmc_debug(mmc, "upper_adma_address: 0x%08" PRIX32 "\n",mmc->regs->upper_adma_address); - mmc_debug(mmc, "preset_for_init: 0x%04" PRIX16 "\n",mmc->regs->preset_for_init); - mmc_debug(mmc, "preset_for_default: 0x%04" PRIX16 "\n",mmc->regs->preset_for_default); - mmc_debug(mmc, "preset_for_high: 0x%04" PRIX16 "\n",mmc->regs->preset_for_high); - mmc_debug(mmc, "preset_for_sdr12: 0x%04" PRIX16 "\n",mmc->regs->preset_for_sdr12); - mmc_debug(mmc, "preset_for_sdr25: 0x%04" PRIX16 "\n",mmc->regs->preset_for_sdr25); - mmc_debug(mmc, "preset_for_sdr50: 0x%04" PRIX16 "\n",mmc->regs->preset_for_sdr50); - mmc_debug(mmc, "preset_for_sdr104: 0x%04" PRIX16 "\n",mmc->regs->preset_for_sdr104); - mmc_debug(mmc, "preset_for_ddr50: 0x%04" PRIX16 "\n",mmc->regs->preset_for_ddr50); - mmc_debug(mmc, "slot_int_status: 0x%04" PRIX16 "\n",mmc->regs->slot_int_status); - mmc_debug(mmc, "host_version: 0x%04" PRIX16 "\n",mmc->regs->host_version); - mmc_debug(mmc, "vendor_clock_cntrl: 0x%08" PRIX32 "\n",mmc->regs->vendor_clock_cntrl); - mmc_debug(mmc, "vendor_sys_sw_cntrl: 0x%08" PRIX32 "\n",mmc->regs->vendor_sys_sw_cntrl); - mmc_debug(mmc, "vendor_err_intr_status: 0x%08" PRIX32 "\n",mmc->regs->vendor_err_intr_status); - mmc_debug(mmc, "vendor_cap_overrides: 0x%08" PRIX32 "\n",mmc->regs->vendor_cap_overrides); - mmc_debug(mmc, "vendor_boot_cntrl: 0x%08" PRIX32 "\n",mmc->regs->vendor_boot_cntrl); - mmc_debug(mmc, "vendor_boot_ack_timeout: 0x%08" PRIX32 "\n",mmc->regs->vendor_boot_ack_timeout); - mmc_debug(mmc, "vendor_boot_dat_timeout: 0x%08" PRIX32 "\n",mmc->regs->vendor_boot_dat_timeout); - mmc_debug(mmc, "vendor_debounce_count: 0x%08" PRIX32 "\n",mmc->regs->vendor_debounce_count); - mmc_debug(mmc, "vendor_misc_cntrl: 0x%08" PRIX32 "\n",mmc->regs->vendor_misc_cntrl); - mmc_debug(mmc, "max_current_override: 0x%08" PRIX32 "\n",mmc->regs->max_current_override); - mmc_debug(mmc, "max_current_override_hi: 0x%08" PRIX32 "\n",mmc->regs->max_current_override_hi); - mmc_debug(mmc, "vendor_io_trim_cntrl: 0x%08" PRIX32 "\n",mmc->regs->vendor_io_trim_cntrl); - mmc_debug(mmc, "vendor_dllcal_cfg: 0x%08" PRIX32 "\n",mmc->regs->vendor_dllcal_cfg); - mmc_debug(mmc, "vendor_dll_ctrl0: 0x%08" PRIX32 "\n",mmc->regs->vendor_dll_ctrl0); - mmc_debug(mmc, "vendor_dll_ctrl1: 0x%08" PRIX32 "\n",mmc->regs->vendor_dll_ctrl1); - mmc_debug(mmc, "vendor_dllcal_cfg_sta: 0x%08" PRIX32 "\n",mmc->regs->vendor_dllcal_cfg_sta); - mmc_debug(mmc, "vendor_tuning_cntrl0: 0x%08" PRIX32 "\n",mmc->regs->vendor_tuning_cntrl0); - mmc_debug(mmc, "vendor_tuning_cntrl1: 0x%08" PRIX32 "\n",mmc->regs->vendor_tuning_cntrl1); - mmc_debug(mmc, "vendor_tuning_status0: 0x%08" PRIX32 "\n",mmc->regs->vendor_tuning_status0); - mmc_debug(mmc, "vendor_tuning_status1: 0x%08" PRIX32 "\n",mmc->regs->vendor_tuning_status1); - mmc_debug(mmc, "vendor_clk_gate_hysteresis_count: 0x%08" PRIX32 "\n",mmc->regs->vendor_clk_gate_hysteresis_count); - mmc_debug(mmc, "vendor_preset_val0: 0x%08" PRIX32 "\n",mmc->regs->vendor_preset_val0); - mmc_debug(mmc, "vendor_preset_val1: 0x%08" PRIX32 "\n",mmc->regs->vendor_preset_val1); - mmc_debug(mmc, "vendor_preset_val2: 0x%08" PRIX32 "\n",mmc->regs->vendor_preset_val2); - mmc_debug(mmc, "sdmemcomppadctrl: 0x%08" PRIX32 "\n",mmc->regs->sdmemcomppadctrl); - mmc_debug(mmc, "auto_cal_config: 0x%08" PRIX32 "\n",mmc->regs->auto_cal_config); - mmc_debug(mmc, "auto_cal_interval: 0x%08" PRIX32 "\n",mmc->regs->auto_cal_interval); - mmc_debug(mmc, "auto_cal_status: 0x%08" PRIX32 "\n",mmc->regs->auto_cal_status); - mmc_debug(mmc, "io_spare: 0x%08" PRIX32 "\n",mmc->regs->io_spare); - mmc_debug(mmc, "sdmmca_mccif_fifoctrl: 0x%08" PRIX32 "\n",mmc->regs->sdmmca_mccif_fifoctrl); - mmc_debug(mmc, "timeout_wcoal_sdmmca: 0x%08" PRIX32 "\n",mmc->regs->timeout_wcoal_sdmmca); -} \ No newline at end of file diff --git a/fusee/fusee-primary/src/sdmmc.h b/fusee/fusee-primary/src/sdmmc.h deleted file mode 100644 index 63538a405..000000000 --- a/fusee/fusee-primary/src/sdmmc.h +++ /dev/null @@ -1,313 +0,0 @@ -/** - * Fusée SD/MMC driver for the Switch - * ~ktemkin - */ - -#ifndef __FUSEE_SDMMC_H__ -#define __FUSEE_SDMMC_H__ - -#include -#include -#include "utils.h" -#include "gpio.h" - -/* Opaque pointer to the Tegra SDMMC registers */ -struct tegra_sdmmc; - - -/** - * Bus widths supported by the SD/MMC cards. - */ -enum sdmmc_bus_width { - MMC_BUS_WIDTH_1BIT = 0, - MMC_BUS_WIDTH_4BIT = 1, - MMC_BUS_WIDTH_8BIT = 2, - - SD_BUS_WIDTH_1BIT = 0, - SD_BUS_WIDTH_4BIT = 2 -}; - - -/** - * Describes the voltages that the host will use to drive the SD card. - * CAUTION: getting these wrong can damage (especially embedded) cards! - */ -enum sdmmc_bus_voltage { - MMC_VOLTAGE_3V3 = 0b111, - MMC_VOLTAGE_1V8 = 0b101, -}; - - -/** - * Represents the different types of devices an MMC object - * can represent. - */ -enum sdmmc_card_type { - MMC_CARD_EMMC, - MMC_CARD_MMC, - MMC_CARD_SD, - MMC_CARD_CART, -}; - - - -/** - * Specification versions for SD/MMC cards. - */ -enum sdmmc_spec_version { - - /* MMC card versions */ - MMC_VERSION_4 = 0, - - /* SD card versions */ - SD_VERSION_1_0 = 0, - SD_VERSION_1_1 = 1, - SD_VERSION_2_0 = 2, - -}; - - -/** - * SDMMC controllers - */ -enum sdmmc_controller { - SWITCH_MICROSD = 0, - SWITCH_EMMC = 3 -}; - -/** - * Write permission modes for SD cards. - */ -enum sdmmc_write_permission { - SDMMC_WRITE_DISABLED, - SDMMC_WRITE_ENABLED, - - /* use this with the utmost caution so you don't wind up with a brick */ - SDMMC_WRITE_ENABLED_INCLUDING_EMMC, -}; - - -/** - * Methods by which we can touch registers accessed via. - * CMD_SWITCH_MODE. - */ -enum sdmmc_switch_access_mode { - - /* Normal commands */ - MMC_SWITCH_MODE_CMD_SET = 0, - MMC_SWITCH_MODE_SET_BITS = 1, - MMC_SWITCH_MODE_CLEAR_BITS = 2, - MMC_SWITCH_MODE_WRITE_BYTE = 3, - - /* EXTCSD access */ - MMC_SWITCH_EXTCSD_NORMAL = 1, -}; - - -/** - * Offsets into the SWITCH_MODE argument. - */ -enum sdmmc_switch_argument_offsets { - MMC_SWITCH_VALUE_SHIFT = 8, - MMC_SWITCH_FIELD_SHIFT = 16, - MMC_SWITCH_ACCESS_MODE_SHIFT = 24, -}; - - -/** - * Bus speeds possible for an SDMMC controller. - */ -enum sdmmc_bus_speed { - /* SD card speeds */ - SDMMC_SPEED_SDR12 = 0, - SDMMC_SPEED_SDR25 = 1, - SDMMC_SPEED_SDR50 = 2, - SDMMC_SPEED_SDR104 = 3, - SDMMC_SPEED_DDR50 = 4, - - /* Other speed: non-spec-compliant value used for e.g. HS400 */ - SDMMC_SPEED_OTHER = 5, - - /* eMMC card speeds */ - /* note: to avoid an enum clash, we add ten to these */ - SDMMC_SPEED_HS26 = 10 , - SDMMC_SPEED_HS52 = 11 , - SDMMC_SPEED_HS200 = 12 , - SDMMC_SPEED_HS400 = 13 , - - /* special speeds */ - SDMMC_SPEED_INIT = -1, -}; - - - -/** - * Primary data structure describing a Fusée MMC driver. - */ -struct mmc { - enum sdmmc_controller controller; - - /* Controller properties */ - const char *name; - bool use_dma; - bool allow_voltage_switching; - unsigned int timeout; - enum tegra_named_gpio card_detect_gpio; - enum sdmmc_write_permission write_enable; - - /* Per-controller operations. */ - int (*set_up_clock_and_io)(struct mmc *mmc); - void (*configure_clock)(struct mmc *mmc, uint32_t source, int car_divisor, int sdmmc_divisor); - int (*enable_supplies)(struct mmc *mmc); - int (*switch_to_low_voltage)(struct mmc *mmc); - bool (*card_present)(struct mmc *mmc); - - /* Per-card-type operations */ - int (*card_init)(struct mmc *mmc); - int (*establish_relative_address)(struct mmc *mmc); - int (*switch_mode)(struct mmc *mmc, int a, int b, int c, uint32_t timeout, void *response); - int (*switch_bus_width)(struct mmc *mmc, enum sdmmc_bus_width width); - int (*optimize_speed)(struct mmc *mmc); - int (*card_switch_bus_speed)(struct mmc *mmc, enum sdmmc_bus_speed speed); - - /* Card properties */ - enum sdmmc_card_type card_type; - uint32_t mmc_card_type; - - uint8_t cid[15]; - uint32_t relative_address; - uint8_t partitioned; - enum sdmmc_spec_version spec_version; - enum sdmmc_bus_width max_bus_width; - enum sdmmc_bus_voltage operating_voltage; - enum sdmmc_bus_speed operating_speed; - - uint8_t partition_support; - uint8_t partition_config; - uint8_t partition_attribute; - uint32_t partition_switch_time; - - uint8_t read_block_order; - uint8_t write_block_order; - uint8_t tuning_block_order; - bool uses_block_addressing; - - /* Current operation status flags */ - uint32_t active_data_buffer; - - /* Pointers to hardware structures */ - volatile struct tegra_sdmmc *regs; -}; - - - - -/** - * Primary data structure describing a Fusée MMC driver. - */ -struct mmc; - - -/** - * Parititions supported by the Switch eMMC. - */ -enum sdmmc_partition { - SDMMC_PARTITION_USER = 0, - SDMMC_PARTITION_BOOT0 = 1, - SDMMC_PARTITION_BOOT1 = 2, -}; - - -/** - * Sets the current SDMMC debugging loglevel. - * - * @param loglevel Current log level. A higher value prints more logs. - * @return The loglevel prior to when this was applied, for easy restoration. - */ -int sdmmc_set_loglevel(int loglevel); - - -/** - * Set up a new SDMMC driver. - * - * @param mmc The SDMMC structure to be initiailized with the device state. - * @param controler The controller description to be used; usually SWITCH_EMMC - * or SWITCH_MICROSD. - * @param allow_voltage_switching True if we should allow voltage switching, - * which may not make sense if we're about to chainload to another component without - * preseving the overall structure. - */ -int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller, bool allow_voltage_switching); - - -/** - * Imports a SDMMC driver struct from another program. This mainly intended for stage2, - * so that it can reuse stage1's SDMMC struct instance(s). - * - * @param mmc The SDMMC structure to be imported. - */ -int sdmmc_import_struct(struct mmc *mmc); - - -/** - * Selects the active MMC partition. Can be used to select - * boot partitions for access. Affects all operations going forward. - * - * @param mmc The MMC controller whose card is to be used. - * @param partition The partition number to be selected. - * - * @return 0 on success, or an error code on failure. - */ -int sdmmc_select_partition(struct mmc *mmc, enum sdmmc_partition partition); - - -/** - * 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); - - -/** - * Releases the SDMMC write lockout, enabling access to the card. - * Note that by default, setting this to WRITE_ENABLED will not allow access to eMMC. - * Check the source for a third constant that can be used to enable eMMC writes. - * - * @param perms The permissions to apply-- typically WRITE_DISABLED or WRITE_ENABLED. - */ -void sdmmc_set_write_enable(struct mmc *mmc, enum sdmmc_write_permission perms); - - -/** - * Writes a sector or sectors to a given SD/MMC card. - * - * @param mmc The MMC device to work with. - * @param buffer The input buffer to write. - * @param block The sector number to write from. - * @param count The number of sectors to write. - * - * @return 0 on success, or an error code on failure. - */ -int sdmmc_write(struct mmc *mmc, const void *buffer, uint32_t block, unsigned int count); - - -/** - * Checks to see whether an SD card is present. - * - * @mmc mmc The controller with which to check for card presence. - * @return true iff a card is present - */ -bool sdmmc_card_present(struct mmc *mmc); - -/** - * Prints out all of the tegra_mmc struct fields - * - * @mmc mmc The controller with which to dump registers. - */ -void sdmmc_dump_regs(struct mmc *mmc); - -#endif diff --git a/fusee/fusee-primary/src/sdmmc/mmc.h b/fusee/fusee-primary/src/sdmmc/mmc.h new file mode 100644 index 000000000..e357d00a4 --- /dev/null +++ b/fusee/fusee-primary/src/sdmmc/mmc.h @@ -0,0 +1,448 @@ +/* + * Header for MultiMediaCard (MMC) + * + * Copyright 2002 Hewlett-Packard Company + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, + * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS + * FITNESS FOR ANY PARTICULAR PURPOSE. + * + * Many thanks to Alessandro Rubini and Jonathan Corbet! + * + * Based strongly on code by: + * + * Author: Yong-iL Joh + * + * Author: Andrew Christian + * 15 May 2002 + */ + +#ifndef LINUX_MMC_MMC_H +#define LINUX_MMC_MMC_H + +/* Standard MMC commands (4.1) type argument response */ + /* class 1 */ +#define MMC_GO_IDLE_STATE 0 /* bc */ +#define MMC_SEND_OP_COND 1 /* bcr [31:0] OCR R3 */ +#define MMC_ALL_SEND_CID 2 /* bcr R2 */ +#define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */ +#define MMC_SET_DSR 4 /* bc [31:16] RCA */ +#define MMC_SLEEP_AWAKE 5 /* ac [31:16] RCA 15:flg R1b */ +#define MMC_SWITCH 6 /* ac [31:0] See below R1b */ +#define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */ +#define MMC_SEND_EXT_CSD 8 /* adtc R1 */ +#define MMC_SEND_CSD 9 /* ac [31:16] RCA R2 */ +#define MMC_SEND_CID 10 /* ac [31:16] RCA R2 */ +#define MMC_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */ +#define MMC_STOP_TRANSMISSION 12 /* ac R1b */ +#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */ +#define MMC_BUS_TEST_R 14 /* adtc R1 */ +#define MMC_GO_INACTIVE_STATE 15 /* ac [31:16] RCA */ +#define MMC_BUS_TEST_W 19 /* adtc R1 */ +#define MMC_SPI_READ_OCR 58 /* spi spi_R3 */ +#define MMC_SPI_CRC_ON_OFF 59 /* spi [0:0] flag spi_R1 */ + + /* class 2 */ +#define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */ +#define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */ +#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */ +#define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */ +#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */ + + /* class 3 */ +#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */ + + /* class 4 */ +#define MMC_SET_BLOCK_COUNT 23 /* adtc [31:0] data addr R1 */ +#define MMC_WRITE_BLOCK 24 /* adtc [31:0] data addr R1 */ +#define MMC_WRITE_MULTIPLE_BLOCK 25 /* adtc R1 */ +#define MMC_PROGRAM_CID 26 /* adtc R1 */ +#define MMC_PROGRAM_CSD 27 /* adtc R1 */ + + /* class 6 */ +#define MMC_SET_WRITE_PROT 28 /* ac [31:0] data addr R1b */ +#define MMC_CLR_WRITE_PROT 29 /* ac [31:0] data addr R1b */ +#define MMC_SEND_WRITE_PROT 30 /* adtc [31:0] wpdata addr R1 */ + + /* class 5 */ +#define MMC_ERASE_GROUP_START 35 /* ac [31:0] data addr R1 */ +#define MMC_ERASE_GROUP_END 36 /* ac [31:0] data addr R1 */ +#define MMC_ERASE 38 /* ac R1b */ + + /* class 9 */ +#define MMC_FAST_IO 39 /* ac R4 */ +#define MMC_GO_IRQ_STATE 40 /* bcr R5 */ + + /* class 7 */ +#define MMC_LOCK_UNLOCK 42 /* adtc R1b */ + + /* class 8 */ +#define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */ +#define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */ + + /* class 11 */ +#define MMC_QUE_TASK_PARAMS 44 /* ac [20:16] task id R1 */ +#define MMC_QUE_TASK_ADDR 45 /* ac [31:0] data addr R1 */ +#define MMC_EXECUTE_READ_TASK 46 /* adtc [20:16] task id R1 */ +#define MMC_EXECUTE_WRITE_TASK 47 /* adtc [20:16] task id R1 */ +#define MMC_CMDQ_TASK_MGMT 48 /* ac [20:16] task id R1b */ + +/* + * MMC_SWITCH argument format: + * + * [31:26] Always 0 + * [25:24] Access Mode + * [23:16] Location of target Byte in EXT_CSD + * [15:08] Value Byte + * [07:03] Always 0 + * [02:00] Command Set + */ + +/* + MMC status in R1, for native mode (SPI bits are different) + Type + e : error bit + s : status bit + r : detected and set for the actual command response + x : detected and set during command execution. the host must poll + the card by sending status command in order to read these bits. + Clear condition + a : according to the card state + b : always related to the previous command. Reception of + a valid command will clear it (with a delay of one command) + c : clear by read + */ + +#define R1_OUT_OF_RANGE (1 << 31) /* er, c */ +#define R1_ADDRESS_ERROR (1 << 30) /* erx, c */ +#define R1_BLOCK_LEN_ERROR (1 << 29) /* er, c */ +#define R1_ERASE_SEQ_ERROR (1 << 28) /* er, c */ +#define R1_ERASE_PARAM (1 << 27) /* ex, c */ +#define R1_WP_VIOLATION (1 << 26) /* erx, c */ +#define R1_CARD_IS_LOCKED (1 << 25) /* sx, a */ +#define R1_LOCK_UNLOCK_FAILED (1 << 24) /* erx, c */ +#define R1_COM_CRC_ERROR (1 << 23) /* er, b */ +#define R1_ILLEGAL_COMMAND (1 << 22) /* er, b */ +#define R1_CARD_ECC_FAILED (1 << 21) /* ex, c */ +#define R1_CC_ERROR (1 << 20) /* erx, c */ +#define R1_ERROR (1 << 19) /* erx, c */ +#define R1_UNDERRUN (1 << 18) /* ex, c */ +#define R1_OVERRUN (1 << 17) /* ex, c */ +#define R1_CID_CSD_OVERWRITE (1 << 16) /* erx, c, CID/CSD overwrite */ +#define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */ +#define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */ +#define R1_ERASE_RESET (1 << 13) /* sr, c */ +#define R1_STATUS(x) (x & 0xFFFFE000) +#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ +#define R1_READY_FOR_DATA (1 << 8) /* sx, a */ +#define R1_SWITCH_ERROR (1 << 7) /* sx, c */ +#define R1_EXCEPTION_EVENT (1 << 6) /* sr, a */ +#define R1_APP_CMD (1 << 5) /* sr, c */ + +#define R1_STATE_IDLE 0 +#define R1_STATE_READY 1 +#define R1_STATE_IDENT 2 +#define R1_STATE_STBY 3 +#define R1_STATE_TRAN 4 +#define R1_STATE_DATA 5 +#define R1_STATE_RCV 6 +#define R1_STATE_PRG 7 +#define R1_STATE_DIS 8 + +/* + * MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS + * R1 is the low order byte; R2 is the next highest byte, when present. + */ +#define R1_SPI_IDLE (1 << 0) +#define R1_SPI_ERASE_RESET (1 << 1) +#define R1_SPI_ILLEGAL_COMMAND (1 << 2) +#define R1_SPI_COM_CRC (1 << 3) +#define R1_SPI_ERASE_SEQ (1 << 4) +#define R1_SPI_ADDRESS (1 << 5) +#define R1_SPI_PARAMETER (1 << 6) +/* R1 bit 7 is always zero */ +#define R2_SPI_CARD_LOCKED (1 << 8) +#define R2_SPI_WP_ERASE_SKIP (1 << 9) /* or lock/unlock fail */ +#define R2_SPI_LOCK_UNLOCK_FAIL R2_SPI_WP_ERASE_SKIP +#define R2_SPI_ERROR (1 << 10) +#define R2_SPI_CC_ERROR (1 << 11) +#define R2_SPI_CARD_ECC_ERROR (1 << 12) +#define R2_SPI_WP_VIOLATION (1 << 13) +#define R2_SPI_ERASE_PARAM (1 << 14) +#define R2_SPI_OUT_OF_RANGE (1 << 15) /* or CSD overwrite */ +#define R2_SPI_CSD_OVERWRITE R2_SPI_OUT_OF_RANGE + +/* + * OCR bits are mostly in host.h + */ +#define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */ +#define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */ +#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */ +#define MMC_VDD_21_22 0x00000200 /* VDD voltage 2.1 ~ 2.2 */ +#define MMC_VDD_22_23 0x00000400 /* VDD voltage 2.2 ~ 2.3 */ +#define MMC_VDD_23_24 0x00000800 /* VDD voltage 2.3 ~ 2.4 */ +#define MMC_VDD_24_25 0x00001000 /* VDD voltage 2.4 ~ 2.5 */ +#define MMC_VDD_25_26 0x00002000 /* VDD voltage 2.5 ~ 2.6 */ +#define MMC_VDD_26_27 0x00004000 /* VDD voltage 2.6 ~ 2.7 */ +#define MMC_VDD_27_28 0x00008000 /* VDD voltage 2.7 ~ 2.8 */ +#define MMC_VDD_28_29 0x00010000 /* VDD voltage 2.8 ~ 2.9 */ +#define MMC_VDD_29_30 0x00020000 /* VDD voltage 2.9 ~ 3.0 */ +#define MMC_VDD_30_31 0x00040000 /* VDD voltage 3.0 ~ 3.1 */ +#define MMC_VDD_31_32 0x00080000 /* VDD voltage 3.1 ~ 3.2 */ +#define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */ +#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */ +#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */ +#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */ + +/* + * Card Command Classes (CCC) + */ +#define CCC_BASIC (1<<0) /* (0) Basic protocol functions */ + /* (CMD0,1,2,3,4,7,9,10,12,13,15) */ + /* (and for SPI, CMD58,59) */ +#define CCC_STREAM_READ (1<<1) /* (1) Stream read commands */ + /* (CMD11) */ +#define CCC_BLOCK_READ (1<<2) /* (2) Block read commands */ + /* (CMD16,17,18) */ +#define CCC_STREAM_WRITE (1<<3) /* (3) Stream write commands */ + /* (CMD20) */ +#define CCC_BLOCK_WRITE (1<<4) /* (4) Block write commands */ + /* (CMD16,24,25,26,27) */ +#define CCC_ERASE (1<<5) /* (5) Ability to erase blocks */ + /* (CMD32,33,34,35,36,37,38,39) */ +#define CCC_WRITE_PROT (1<<6) /* (6) Able to write protect blocks */ + /* (CMD28,29,30) */ +#define CCC_LOCK_CARD (1<<7) /* (7) Able to lock down card */ + /* (CMD16,CMD42) */ +#define CCC_APP_SPEC (1<<8) /* (8) Application specific */ + /* (CMD55,56,57,ACMD*) */ +#define CCC_IO_MODE (1<<9) /* (9) I/O mode */ + /* (CMD5,39,40,52,53) */ +#define CCC_SWITCH (1<<10) /* (10) High speed switch */ + /* (CMD6,34,35,36,37,50) */ + /* (11) Reserved */ + /* (CMD?) */ + +/* + * CSD field definitions + */ + +#define CSD_STRUCT_VER_1_0 0 /* Valid for system specification 1.0 - 1.2 */ +#define CSD_STRUCT_VER_1_1 1 /* Valid for system specification 1.4 - 2.2 */ +#define CSD_STRUCT_VER_1_2 2 /* Valid for system specification 3.1 - 3.2 - 3.31 - 4.0 - 4.1 */ +#define CSD_STRUCT_EXT_CSD 3 /* Version is coded in CSD_STRUCTURE in EXT_CSD */ + +#define CSD_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.2 */ +#define CSD_SPEC_VER_1 1 /* Implements system specification 1.4 */ +#define CSD_SPEC_VER_2 2 /* Implements system specification 2.0 - 2.2 */ +#define CSD_SPEC_VER_3 3 /* Implements system specification 3.1 - 3.2 - 3.31 */ +#define CSD_SPEC_VER_4 4 /* Implements system specification 4.0 - 4.1 */ + +/* + * EXT_CSD fields + */ + +#define EXT_CSD_CMDQ_MODE_EN 15 /* R/W */ +#define EXT_CSD_FLUSH_CACHE 32 /* W */ +#define EXT_CSD_CACHE_CTRL 33 /* R/W */ +#define EXT_CSD_POWER_OFF_NOTIFICATION 34 /* R/W */ +#define EXT_CSD_PACKED_FAILURE_INDEX 35 /* RO */ +#define EXT_CSD_PACKED_CMD_STATUS 36 /* RO */ +#define EXT_CSD_EXP_EVENTS_STATUS 54 /* RO, 2 bytes */ +#define EXT_CSD_EXP_EVENTS_CTRL 56 /* R/W, 2 bytes */ +#define EXT_CSD_DATA_SECTOR_SIZE 61 /* R */ +#define EXT_CSD_GP_SIZE_MULT 143 /* R/W */ +#define EXT_CSD_PARTITION_SETTING_COMPLETED 155 /* R/W */ +#define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */ +#define EXT_CSD_PARTITION_SUPPORT 160 /* RO */ +#define EXT_CSD_HPI_MGMT 161 /* R/W */ +#define EXT_CSD_RST_N_FUNCTION 162 /* R/W */ +#define EXT_CSD_BKOPS_EN 163 /* R/W */ +#define EXT_CSD_BKOPS_START 164 /* W */ +#define EXT_CSD_SANITIZE_START 165 /* W */ +#define EXT_CSD_WR_REL_PARAM 166 /* RO */ +#define EXT_CSD_RPMB_MULT 168 /* RO */ +#define EXT_CSD_FW_CONFIG 169 /* R/W */ +#define EXT_CSD_BOOT_WP 173 /* R/W */ +#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */ +#define EXT_CSD_PART_CONFIG 179 /* R/W */ +#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */ +#define EXT_CSD_BUS_WIDTH 183 /* R/W */ +#define EXT_CSD_STROBE_SUPPORT 184 /* RO */ +#define EXT_CSD_HS_TIMING 185 /* R/W */ +#define EXT_CSD_POWER_CLASS 187 /* R/W */ +#define EXT_CSD_REV 192 /* RO */ +#define EXT_CSD_STRUCTURE 194 /* RO */ +#define EXT_CSD_CARD_TYPE 196 /* RO */ +#define EXT_CSD_DRIVER_STRENGTH 197 /* RO */ +#define EXT_CSD_OUT_OF_INTERRUPT_TIME 198 /* RO */ +#define EXT_CSD_PART_SWITCH_TIME 199 /* RO */ +#define EXT_CSD_PWR_CL_52_195 200 /* RO */ +#define EXT_CSD_PWR_CL_26_195 201 /* RO */ +#define EXT_CSD_PWR_CL_52_360 202 /* RO */ +#define EXT_CSD_PWR_CL_26_360 203 /* RO */ +#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ +#define EXT_CSD_S_A_TIMEOUT 217 /* RO */ +#define EXT_CSD_REL_WR_SEC_C 222 /* RO */ +#define EXT_CSD_HC_WP_GRP_SIZE 221 /* RO */ +#define EXT_CSD_ERASE_TIMEOUT_MULT 223 /* RO */ +#define EXT_CSD_HC_ERASE_GRP_SIZE 224 /* RO */ +#define EXT_CSD_BOOT_MULT 226 /* RO */ +#define EXT_CSD_SEC_TRIM_MULT 229 /* RO */ +#define EXT_CSD_SEC_ERASE_MULT 230 /* RO */ +#define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */ +#define EXT_CSD_TRIM_MULT 232 /* RO */ +#define EXT_CSD_PWR_CL_200_195 236 /* RO */ +#define EXT_CSD_PWR_CL_200_360 237 /* RO */ +#define EXT_CSD_PWR_CL_DDR_52_195 238 /* RO */ +#define EXT_CSD_PWR_CL_DDR_52_360 239 /* RO */ +#define EXT_CSD_BKOPS_STATUS 246 /* RO */ +#define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */ +#define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */ +#define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */ +#define EXT_CSD_PWR_CL_DDR_200_360 253 /* RO */ +#define EXT_CSD_FIRMWARE_VERSION 254 /* RO, 8 bytes */ +#define EXT_CSD_PRE_EOL_INFO 267 /* RO */ +#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A 268 /* RO */ +#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B 269 /* RO */ +#define EXT_CSD_CMDQ_DEPTH 307 /* RO */ +#define EXT_CSD_CMDQ_SUPPORT 308 /* RO */ +#define EXT_CSD_SUPPORTED_MODE 493 /* RO */ +#define EXT_CSD_TAG_UNIT_SIZE 498 /* RO */ +#define EXT_CSD_DATA_TAG_SUPPORT 499 /* RO */ +#define EXT_CSD_MAX_PACKED_WRITES 500 /* RO */ +#define EXT_CSD_MAX_PACKED_READS 501 /* RO */ +#define EXT_CSD_BKOPS_SUPPORT 502 /* RO */ +#define EXT_CSD_HPI_FEATURES 503 /* RO */ + +/* + * EXT_CSD field definitions + */ + +#define EXT_CSD_WR_REL_PARAM_EN (1<<2) + +#define EXT_CSD_BOOT_WP_B_PWR_WP_DIS (0x40) +#define EXT_CSD_BOOT_WP_B_PERM_WP_DIS (0x10) +#define EXT_CSD_BOOT_WP_B_PERM_WP_EN (0x04) +#define EXT_CSD_BOOT_WP_B_PWR_WP_EN (0x01) + +#define EXT_CSD_PART_CONFIG_ACC_MASK (0x7) +#define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1) +#define EXT_CSD_PART_CONFIG_ACC_RPMB (0x3) +#define EXT_CSD_PART_CONFIG_ACC_GP0 (0x4) + +#define EXT_CSD_PART_SETTING_COMPLETED (0x1) +#define EXT_CSD_PART_SUPPORT_PART_EN (0x1) + +#define EXT_CSD_CMD_SET_NORMAL (1<<0) +#define EXT_CSD_CMD_SET_SECURE (1<<1) +#define EXT_CSD_CMD_SET_CPSECURE (1<<2) + +#define EXT_CSD_CARD_TYPE_HS_26 (1<<0) /* Card can run at 26MHz */ +#define EXT_CSD_CARD_TYPE_HS_52 (1<<1) /* Card can run at 52MHz */ +#define EXT_CSD_CARD_TYPE_HS (EXT_CSD_CARD_TYPE_HS_26 | \ + EXT_CSD_CARD_TYPE_HS_52) +#define EXT_CSD_CARD_TYPE_DDR_1_8V (1<<2) /* Card can run at 52MHz */ + /* DDR mode @1.8V or 3V I/O */ +#define EXT_CSD_CARD_TYPE_DDR_1_2V (1<<3) /* Card can run at 52MHz */ + /* DDR mode @1.2V I/O */ +#define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V \ + | EXT_CSD_CARD_TYPE_DDR_1_2V) +#define EXT_CSD_CARD_TYPE_HS200_1_8V (1<<4) /* Card can run at 200MHz */ +#define EXT_CSD_CARD_TYPE_HS200_1_2V (1<<5) /* Card can run at 200MHz */ + /* SDR mode @1.2V I/O */ +#define EXT_CSD_CARD_TYPE_HS200 (EXT_CSD_CARD_TYPE_HS200_1_8V | \ + EXT_CSD_CARD_TYPE_HS200_1_2V) +#define EXT_CSD_CARD_TYPE_HS400_1_8V (1<<6) /* Card can run at 200MHz DDR, 1.8V */ +#define EXT_CSD_CARD_TYPE_HS400_1_2V (1<<7) /* Card can run at 200MHz DDR, 1.2V */ +#define EXT_CSD_CARD_TYPE_HS400 (EXT_CSD_CARD_TYPE_HS400_1_8V | \ + EXT_CSD_CARD_TYPE_HS400_1_2V) +#define EXT_CSD_CARD_TYPE_HS400ES (1<<8) /* Card can run at HS400ES */ + +#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */ +#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */ +#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */ +#define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */ +#define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */ +#define EXT_CSD_BUS_WIDTH_STROBE BIT(7) /* Enhanced strobe mode */ + +#define EXT_CSD_TIMING_BC 0 /* Backwards compatility */ +#define EXT_CSD_TIMING_HS 1 /* High speed */ +#define EXT_CSD_TIMING_HS200 2 /* HS200 */ +#define EXT_CSD_TIMING_HS400 3 /* HS400 */ +#define EXT_CSD_DRV_STR_SHIFT 4 /* Driver Strength shift */ + +#define EXT_CSD_SEC_ER_EN BIT(0) +#define EXT_CSD_SEC_BD_BLK_EN BIT(2) +#define EXT_CSD_SEC_GB_CL_EN BIT(4) +#define EXT_CSD_SEC_SANITIZE BIT(6) /* v4.5 only */ + +#define EXT_CSD_RST_N_EN_MASK 0x3 +#define EXT_CSD_RST_N_ENABLED 1 /* RST_n is enabled on card */ + +#define EXT_CSD_NO_POWER_NOTIFICATION 0 +#define EXT_CSD_POWER_ON 1 +#define EXT_CSD_POWER_OFF_SHORT 2 +#define EXT_CSD_POWER_OFF_LONG 3 + +#define EXT_CSD_PWR_CL_8BIT_MASK 0xF0 /* 8 bit PWR CLS */ +#define EXT_CSD_PWR_CL_4BIT_MASK 0x0F /* 8 bit PWR CLS */ +#define EXT_CSD_PWR_CL_8BIT_SHIFT 4 +#define EXT_CSD_PWR_CL_4BIT_SHIFT 0 + +#define EXT_CSD_PACKED_EVENT_EN BIT(3) + +/* + * EXCEPTION_EVENT_STATUS field + */ +#define EXT_CSD_URGENT_BKOPS BIT(0) +#define EXT_CSD_DYNCAP_NEEDED BIT(1) +#define EXT_CSD_SYSPOOL_EXHAUSTED BIT(2) +#define EXT_CSD_PACKED_FAILURE BIT(3) + +#define EXT_CSD_PACKED_GENERIC_ERROR BIT(0) +#define EXT_CSD_PACKED_INDEXED_ERROR BIT(1) + +/* + * BKOPS status level + */ +#define EXT_CSD_BKOPS_LEVEL_2 0x2 + +/* + * BKOPS modes + */ +#define EXT_CSD_MANUAL_BKOPS_MASK 0x01 +#define EXT_CSD_AUTO_BKOPS_MASK 0x02 + +/* + * Command Queue + */ +#define EXT_CSD_CMDQ_MODE_ENABLED BIT(0) +#define EXT_CSD_CMDQ_DEPTH_MASK GENMASK(4, 0) +#define EXT_CSD_CMDQ_SUPPORTED BIT(0) + +/* + * MMC_SWITCH access modes + */ +#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */ +#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits which are 1 in value */ +#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */ +#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ + +/* + * Erase/trim/discard + */ +#define MMC_ERASE_ARG 0x00000000 +#define MMC_SECURE_ERASE_ARG 0x80000000 +#define MMC_TRIM_ARG 0x00000001 +#define MMC_DISCARD_ARG 0x00000003 +#define MMC_SECURE_TRIM1_ARG 0x80000001 +#define MMC_SECURE_TRIM2_ARG 0x80008000 +#define MMC_SECURE_ARGS 0x80000000 +#define MMC_TRIM_ARGS 0x00008001 + +#endif /* LINUX_MMC_MMC_H */ diff --git a/fusee/fusee-primary/src/sdmmc/sd.h b/fusee/fusee-primary/src/sdmmc/sd.h new file mode 100644 index 000000000..e5707f55f --- /dev/null +++ b/fusee/fusee-primary/src/sdmmc/sd.h @@ -0,0 +1,153 @@ +/* + * include/linux/mmc/sd.h + * + * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#ifndef LINUX_MMC_SD_H +#define LINUX_MMC_SD_H + +/* SD commands type argument response */ +/* class 0 */ +/* This is basically the same command as for MMC with some quirks. */ +#define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */ +#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */ +#define SD_SWITCH_VOLTAGE 11 /* ac R1 */ + +/* class 10 */ +#define SD_SWITCH 6 /* adtc [31:0] See below R1 */ + +/* class 5 */ +#define SD_ERASE_WR_BLK_START 32 /* ac [31:0] data addr R1 */ +#define SD_ERASE_WR_BLK_END 33 /* ac [31:0] data addr R1 */ + +/* Application commands */ +#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */ +#define SD_APP_SD_STATUS 13 /* adtc R1 */ +#define SD_APP_SEND_NUM_WR_BLKS 22 /* adtc R1 */ +#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */ +#define SD_APP_SET_CLR_CARD_DETECT 42 /* ac [0] set cd R1 */ +#define SD_APP_SEND_SCR 51 /* adtc R1 */ + +/* OCR bit definitions */ +#define SD_OCR_S18R (1 << 24) /* 1.8V switching request */ +#define SD_ROCR_S18A SD_OCR_S18R /* 1.8V switching accepted by card */ +#define SD_OCR_XPC (1 << 28) /* SDXC power control */ +#define SD_OCR_CCS (1 << 30) /* Card Capacity Status */ +#define SD_OCR_VDD_LOW (1 << 7) /* SD: Reserved for Low Voltage Range */ +#define SD_OCR_VDD_20_21 (1 << 8) +#define SD_OCR_VDD_21_22 (1 << 9) +#define SD_OCR_VDD_22_23 (1 << 10) +#define SD_OCR_VDD_23_24 (1 << 11) +#define SD_OCR_VDD_24_25 (1 << 12) +#define SD_OCR_VDD_25_26 (1 << 13) +#define SD_OCR_VDD_26_27 (1 << 14) +#define SD_OCR_VDD_27_28 (1 << 15) +#define SD_OCR_VDD_28_29 (1 << 16) +#define SD_OCR_VDD_29_30 (1 << 17) +#define SD_OCR_VDD_30_31 (1 << 18) +#define SD_OCR_VDD_31_32 (1 << 19) +#define SD_OCR_VDD_32_33 (1 << 20) +#define SD_OCR_VDD_33_34 (1 << 21) +#define SD_OCR_VDD_34_35 (1 << 22) +#define SD_OCR_VDD_35_36 (1 << 23) + +/* + * SD_SWITCH argument format: + * + * [31] Check (0) or switch (1) + * [30:24] Reserved (0) + * [23:20] Function group 6 + * [19:16] Function group 5 + * [15:12] Function group 4 + * [11:8] Function group 3 + * [7:4] Function group 2 + * [3:0] Function group 1 + */ + +/* + * SD_SEND_IF_COND argument format: + * + * [31:12] Reserved (0) + * [11:8] Host Voltage Supply Flags + * [7:0] Check Pattern (0xAA) + */ + +/* + * SCR field definitions + */ + +#define SD_SCR_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.01 */ +#define SD_SCR_SPEC_VER_1 1 /* Implements system specification 1.10 */ +#define SD_SCR_SPEC_VER_2 2 /* Implements system specification 2.00-3.0X */ +#define SD_SCR_BUS_WIDTH_1 1 +#define SD_SCR_BUS_WIDTH_4 4 +#define SD_SCR_CMD20_SUPPORT 1 +#define SD_SCR_CMD23_SUPPORT 2 + +/* + * SD bus widths + */ +#define SD_BUS_WIDTH_1 0 +#define SD_BUS_WIDTH_4 2 + +/* + * SD bus speed modes + */ +#define UHS_SDR12_BUS_SPEED 0 +#define HIGH_SPEED_BUS_SPEED 1 +#define UHS_SDR25_BUS_SPEED 1 +#define UHS_SDR50_BUS_SPEED 2 +#define UHS_SDR104_BUS_SPEED 3 +#define UHS_DDR50_BUS_SPEED 4 +#define SD_MODE_HIGH_SPEED (1 << HIGH_SPEED_BUS_SPEED) +#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED) +#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED) +#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED) +#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED) +#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED) + +/* + * SD bus driver types + */ +#define SD_DRIVER_TYPE_B 0x01 +#define SD_DRIVER_TYPE_A 0x02 +#define SD_DRIVER_TYPE_C 0x04 +#define SD_DRIVER_TYPE_D 0x08 + +/* + * SD bus current limits + */ +#define SD_SET_CURRENT_LIMIT_200 0 +#define SD_SET_CURRENT_LIMIT_400 1 +#define SD_SET_CURRENT_LIMIT_600 2 +#define SD_SET_CURRENT_LIMIT_800 3 +#define SD_SET_CURRENT_NO_CHANGE (-1) +#define SD_MAX_CURRENT_200 (1 << SD_SET_CURRENT_LIMIT_200) +#define SD_MAX_CURRENT_400 (1 << SD_SET_CURRENT_LIMIT_400) +#define SD_MAX_CURRENT_600 (1 << SD_SET_CURRENT_LIMIT_600) +#define SD_MAX_CURRENT_800 (1 << SD_SET_CURRENT_LIMIT_800) + +/* + * SD_SWITCH mode + */ +#define SD_SWITCH_CHECK 0 +#define SD_SWITCH_SET 1 + +/* + * SD_SWITCH function groups + */ +#define SD_SWITCH_GRP_ACCESS 0 + +/* + * SD_SWITCH access modes + */ +#define SD_SWITCH_ACCESS_DEF 0 +#define SD_SWITCH_ACCESS_HS 1 + +#endif /* LINUX_MMC_SD_H */ diff --git a/fusee/fusee-primary/src/sdmmc/sdmmc.c b/fusee/fusee-primary/src/sdmmc/sdmmc.c new file mode 100644 index 000000000..f4243d9c7 --- /dev/null +++ b/fusee/fusee-primary/src/sdmmc/sdmmc.c @@ -0,0 +1,1510 @@ +#include +#include +#include +#include +#include + +#include "sdmmc.h" +#include "mmc.h" +#include "sd.h" +#include "../timers.h" +#include "../lib/driver_utils.h" + +#define UNSTUFF_BITS(resp,start,size) \ +({ \ + const int __size = size; \ + const uint32_t __mask = (__size < 32 ? 1 << __size : 0) - 1; \ + const int __off = 3 - ((start) / 32); \ + const int __shft = (start) & 31; \ + uint32_t __res; \ + \ + __res = resp[__off] >> __shft; \ + if (__size + __shft > 32) \ + __res |= resp[__off-1] << ((32 - __shft) % 32); \ + __res & __mask; \ +}) + +static const unsigned int tran_exp[] = { + 10000, 100000, 1000000, 10000000, + 0, 0, 0, 0 +}; + +static const unsigned char tran_mant[] = { + 0, 10, 12, 13, 15, 20, 25, 30, + 35, 40, 45, 50, 55, 60, 70, 80, +}; + +static const unsigned int taac_exp[] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, +}; + +static const unsigned int taac_mant[] = { + 0, 10, 12, 13, 15, 20, 25, 30, + 35, 40, 45, 50, 55, 60, 70, 80, +}; + +/* + Common SDMMC device functions. +*/ + +static bool is_sdmmc_device_r1_error(uint32_t status) +{ + return (status & (R1_OUT_OF_RANGE | R1_ADDRESS_ERROR | R1_BLOCK_LEN_ERROR + | R1_ERASE_SEQ_ERROR | R1_ERASE_PARAM | R1_WP_VIOLATION + | R1_LOCK_UNLOCK_FAILED | R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND + | R1_CARD_ECC_FAILED | R1_CC_ERROR | R1_ERROR | R1_CID_CSD_OVERWRITE + | R1_WP_ERASE_SKIP | R1_ERASE_RESET | R1_SWITCH_ERROR)); +} + +static int sdmmc_device_send_r1_cmd(sdmmc_device_t *device, uint32_t opcode, uint32_t arg, bool is_busy, uint32_t resp_mask, uint32_t resp_state) +{ + sdmmc_command_t cmd = {}; + + cmd.opcode = opcode; + cmd.arg = arg; + cmd.flags = (is_busy ? SDMMC_RSP_R1B : SDMMC_RSP_R1); + + /* Try to send the command. */ + if (!sdmmc_send_cmd(device->sdmmc, &cmd, 0, 0)) + return 0; + + uint32_t resp = 0; + + /* Try to load back the response. */ + if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R1, &resp)) + return 0; + + /* Mask the response, if necessary. */ + if (resp_mask) + resp &= ~(resp_mask); + + /* We got an error state. */ + if (is_sdmmc_device_r1_error(resp)) + return 0; + + /* We need to check for the desired state. */ + if (resp_state != 0xFFFFFFFF) + { + /* We didn't get the expected state. */ + if (R1_CURRENT_STATE(resp) != resp_state) + return 0; + } + + return 1; +} + +static int sdmmc_device_go_idle(sdmmc_device_t *device) +{ + sdmmc_command_t cmd = {}; + + cmd.opcode = MMC_GO_IDLE_STATE; + cmd.arg = 0; + cmd.flags = SDMMC_RSP_NONE; + + return sdmmc_send_cmd(device->sdmmc, &cmd, 0, 0); +} + +static int sdmmc_device_send_cid(sdmmc_device_t *device, uint32_t *cid) +{ + sdmmc_command_t cmd = {}; + + cmd.opcode = MMC_ALL_SEND_CID; + cmd.arg = 0; + cmd.flags = SDMMC_RSP_R2; + + /* Try to send the command. */ + if (!sdmmc_send_cmd(device->sdmmc, &cmd, 0, 0)) + return 0; + + /* Try to load back the response. */ + return sdmmc_load_response(device->sdmmc, SDMMC_RSP_R2, cid); +} + +static int sdmmc_device_send_csd(sdmmc_device_t *device, uint32_t *csd) +{ + sdmmc_command_t cmd = {}; + + cmd.opcode = MMC_SEND_CSD; + cmd.arg = (device->rca << 16); + cmd.flags = SDMMC_RSP_R2; + + /* Try to send the command. */ + if (!sdmmc_send_cmd(device->sdmmc, &cmd, 0, 0)) + return 0; + + /* Try to load back the response. */ + return sdmmc_load_response(device->sdmmc, SDMMC_RSP_R2, csd); +} + +static int sdmmc_device_select_card(sdmmc_device_t *device) +{ + /* Try to send the command. */ + return sdmmc_device_send_r1_cmd(device, MMC_SELECT_CARD, (device->rca << 16), true, 0, 0xFFFFFFFF); +} + +static int sdmmc_device_set_blocklen(sdmmc_device_t *device, uint32_t blocklen) +{ + /* Try to send the command. */ + return sdmmc_device_send_r1_cmd(device, MMC_SET_BLOCKLEN, blocklen, false, 0, R1_STATE_TRAN); +} + +static int sdmmc_device_send_status(sdmmc_device_t *device) +{ + /* Try to send the command. */ + return sdmmc_device_send_r1_cmd(device, MMC_SEND_STATUS, (device->rca << 16), false, 0, R1_STATE_TRAN); +} + +static int sdmmc_device_rw(sdmmc_device_t *device, uint32_t sector, uint32_t num_sectors, void *data, bool is_read) +{ + uint8_t *buf = (uint8_t *)data; + + sdmmc_command_t cmd = {}; + sdmmc_request_t req = {}; + + while (num_sectors) + { + uint32_t num_blocks_out = 0; + uint32_t num_retries = 10; + + for (; num_retries > 0; num_retries--) + { + cmd.opcode = is_read ? MMC_READ_MULTIPLE_BLOCK : MMC_WRITE_MULTIPLE_BLOCK; + cmd.arg = sector; + cmd.flags = SDMMC_RSP_R1; + + req.data = buf; + req.blksz = 512; + req.num_blocks = num_sectors; + req.is_read = is_read; + req.is_multi_block = true; + req.is_auto_cmd12 = true; + + /* Try to send the command. */ + if (!sdmmc_send_cmd(device->sdmmc, &cmd, &req, &num_blocks_out)) + { + /* Abort the transmission. */ + sdmmc_abort(device->sdmmc, MMC_STOP_TRANSMISSION); + + /* Peek the SD card's status. */ + sdmmc_device_send_status(device); + + /* Wait for a while. */ + udelay(100000); + } + else + break; + } + + /* Failed to read/write on all attempts. */ + if (!num_retries) + return 0; + + /* Advance to next sector. */ + sector += num_blocks_out; + num_sectors -= num_blocks_out; + buf += (512 * num_blocks_out); + } + + return 1; +} + +int sdmmc_device_read(sdmmc_device_t *device, uint32_t sector, uint32_t num_sectors, void *data) +{ + return sdmmc_device_rw(device, sector, num_sectors, data, true); +} + +int sdmmc_device_write(sdmmc_device_t *device, uint32_t sector, uint32_t num_sectors, void *data) +{ + return sdmmc_device_rw(device, sector, num_sectors, data, false); +} + +int sdmmc_device_finish(sdmmc_device_t *device) +{ + /* Place the device in idle state. */ + if (!sdmmc_device_go_idle(device)) + return 0; + + /* Terminate the device. */ + sdmmc_finish(device->sdmmc); + return 1; +} + +/* + SD device functions. +*/ + +static void sdmmc_sd_decode_cid(sdmmc_device_t *device, uint32_t *cid) +{ + device->cid.manfid = UNSTUFF_BITS(cid, 120, 8); + device->cid.oemid = UNSTUFF_BITS(cid, 104, 16); + device->cid.prod_name[0] = UNSTUFF_BITS(cid, 96, 8); + device->cid.prod_name[1] = UNSTUFF_BITS(cid, 88, 8); + device->cid.prod_name[2] = UNSTUFF_BITS(cid, 80, 8); + device->cid.prod_name[3] = UNSTUFF_BITS(cid, 72, 8); + device->cid.prod_name[4] = UNSTUFF_BITS(cid, 64, 8); + device->cid.hwrev = UNSTUFF_BITS(cid, 60, 4); + device->cid.fwrev = UNSTUFF_BITS(cid, 56, 4); + device->cid.serial = UNSTUFF_BITS(cid, 24, 32); + device->cid.year = UNSTUFF_BITS(cid, 12, 8) + 2000; /* SD cards year offset */ + device->cid.month = UNSTUFF_BITS(cid, 8, 4); +} + +static int sdmmc_sd_decode_csd(sdmmc_device_t *device, uint32_t *csd) +{ + unsigned int e, m; + device->csd.structure = UNSTUFF_BITS(csd, 126, 2); + + switch (device->csd.structure) { + case 0: + m = UNSTUFF_BITS(csd, 115, 4); + e = UNSTUFF_BITS(csd, 112, 3); + device->csd.taac_ns = (taac_exp[e] * taac_mant[m] + 9) / 10; + device->csd.taac_clks = UNSTUFF_BITS(csd, 104, 8) * 100; + + m = UNSTUFF_BITS(csd, 99, 4); + e = UNSTUFF_BITS(csd, 96, 3); + device->csd.max_dtr = tran_exp[e] * tran_mant[m]; + device->csd.cmdclass = UNSTUFF_BITS(csd, 84, 12); + + e = UNSTUFF_BITS(csd, 47, 3); + m = UNSTUFF_BITS(csd, 62, 12); + device->csd.capacity = ((1 + m) << (e + 2)); + + device->csd.read_blkbits = UNSTUFF_BITS(csd, 80, 4); + device->csd.read_partial = UNSTUFF_BITS(csd, 79, 1); + device->csd.write_misalign = UNSTUFF_BITS(csd, 78, 1); + device->csd.read_misalign = UNSTUFF_BITS(csd, 77, 1); + device->csd.dsr_imp = UNSTUFF_BITS(csd, 76, 1); + device->csd.r2w_factor = UNSTUFF_BITS(csd, 26, 3); + device->csd.write_blkbits = UNSTUFF_BITS(csd, 22, 4); + device->csd.write_partial = UNSTUFF_BITS(csd, 21, 1); + + if (UNSTUFF_BITS(csd, 46, 1)) { + device->csd.erase_size = 1; + } else if (device->csd.write_blkbits >= 9) { + device->csd.erase_size = UNSTUFF_BITS(csd, 39, 7) + 1; + device->csd.erase_size <<= (device->csd.write_blkbits - 9); + } + break; + case 1: + device->csd.taac_ns = 0; /* Unused */ + device->csd.taac_clks = 0; /* Unused */ + + m = UNSTUFF_BITS(csd, 99, 4); + e = UNSTUFF_BITS(csd, 96, 3); + device->csd.max_dtr = tran_exp[e] * tran_mant[m]; + device->csd.cmdclass = UNSTUFF_BITS(csd, 84, 12); + device->csd.c_size = UNSTUFF_BITS(csd, 48, 22); + + m = UNSTUFF_BITS(csd, 48, 22); + device->csd.capacity = ((1 + m) << 10); + + device->csd.read_blkbits = 9; + device->csd.read_partial = 0; + device->csd.write_misalign = 0; + device->csd.read_misalign = 0; + device->csd.r2w_factor = 4; /* Unused */ + device->csd.write_blkbits = 9; + device->csd.write_partial = 0; + device->csd.erase_size = 1; + break; + default: + return 0; + } + + return 1; +} + +static int sdmmc_sd_decode_scr(sdmmc_device_t *device, uint8_t *scr) +{ + uint8_t tmp[8]; + uint32_t resp[4]; + + /* This must be swapped. */ + for (int i = 0; i < 8; i += 4) + { + tmp[i + 3] = scr[i]; + tmp[i + 2] = scr[i + 1]; + tmp[i + 1] = scr[i + 2]; + tmp[i] = scr[i + 3]; + } + + resp[3] = *(uint32_t *)&tmp[4]; + resp[2] = *(uint32_t *)&tmp[0]; + + device->scr.sda_vsn = UNSTUFF_BITS(resp, 56, 4); + device->scr.bus_widths = UNSTUFF_BITS(resp, 48, 4); + + /* Check if Physical Layer Spec v3.0 is supported. */ + if (device->scr.sda_vsn == SD_SCR_SPEC_VER_2) + device->scr.sda_spec3 = UNSTUFF_BITS(resp, 47, 1); + + if (device->scr.sda_spec3) + device->scr.cmds = UNSTUFF_BITS(resp, 32, 2); + + /* Unknown SCR structure version. */ + if (UNSTUFF_BITS(resp, 60, 4)) + return 0; + else + return 1; +} + +static void sdmmc_sd_decode_ssr(sdmmc_device_t *device, uint8_t *ssr) +{ + uint8_t tmp[64]; + uint32_t resp1[4]; + uint32_t resp2[4]; + + /* This must be swapped. */ + for (int i = 0; i < 64; i += 4) + { + tmp[i + 3] = ssr[i]; + tmp[i + 2] = ssr[i + 1]; + tmp[i + 1] = ssr[i + 2]; + tmp[i] = ssr[i + 3]; + } + + resp1[3] = *(uint32_t *)&tmp[12]; + resp1[2] = *(uint32_t *)&tmp[8]; + resp1[1] = *(uint32_t *)&tmp[4]; + resp1[0] = *(uint32_t *)&tmp[0]; + resp2[3] = *(uint32_t *)&tmp[28]; + resp2[2] = *(uint32_t *)&tmp[24]; + resp2[1] = *(uint32_t *)&tmp[20]; + resp2[0] = *(uint32_t *)&tmp[16]; + + device->ssr.dat_bus_width = ((UNSTUFF_BITS(resp1, 126, 2) & SD_BUS_WIDTH_4) ? 4 : 1); + device->ssr.speed_class = UNSTUFF_BITS(resp1, 56, 8); + + if (device->ssr.speed_class < 4) + device->ssr.speed_class <<= 1; + else if (device->ssr.speed_class == 4) + device->ssr.speed_class = 10; + + device->ssr.uhs_speed_grade = UNSTUFF_BITS(resp1, 12, 4); + device->ssr.video_speed_class = UNSTUFF_BITS(resp1, 0, 8); + device->ssr.app_perf_class = UNSTUFF_BITS(resp2, 80, 4); +} + +static int sdmmc_sd_send_app_cmd(sdmmc_device_t *device, sdmmc_command_t *cmd, sdmmc_request_t *req, uint32_t resp_mask, uint32_t resp_state) +{ + /* Try to send the APP command. */ + if (!sdmmc_device_send_r1_cmd(device, MMC_APP_CMD, (device->rca << 16), false, resp_mask, resp_state)) + return 0; + + /* Send the actual command. */ + if (!sdmmc_send_cmd(device->sdmmc, cmd, req, 0)) + return 0; + + return 1; +} + +static int sdmmc_sd_send_if_cond(sdmmc_device_t *device, bool *is_sd_ver2) +{ + sdmmc_command_t cmd = {}; + + cmd.opcode = SD_SEND_IF_COND; + /* We set the bit if the host supports voltages between 2.7 and 3.6 V */ + cmd.arg = 0x1AA; + cmd.flags = SDMMC_RSP_R7; + + /* Command failed, this means SD Card is not version 2. */ + if (!sdmmc_send_cmd(device->sdmmc, &cmd, 0, 0)) + { + *is_sd_ver2 = false; + return 1; + } + + uint32_t resp = 0; + + /* Try to load back the response. */ + if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R7, &resp)) + return 0; + + /* Check if we got a valid response. */ + if ((resp & 0xFF) == 0xAA) + { + *is_sd_ver2 = true; + return 1; + } + + return 0; +} + +static int sdmmc_sd_send_op_cond(sdmmc_device_t *device, bool is_sd_ver2, bool is_uhs_en) +{ + sdmmc_command_t cmd = {}; + + /* Program a large timeout. */ + uint32_t timebase = get_time(); + bool is_timeout = false; + + while (!is_timeout) + { + /* Set this since most cards do not answer if some reserved bits in the OCR are set. */ + uint32_t arg = SD_OCR_VDD_32_33; + + /* Request support for SDXC power control and SDHC block mode cards. */ + if (is_sd_ver2) + { + arg |= SD_OCR_XPC; + arg |= SD_OCR_CCS; + } + + /* Request support 1.8V switching. */ + if (is_uhs_en) + arg |= SD_OCR_S18R; + + cmd.opcode = SD_APP_OP_COND; + cmd.arg = arg; + cmd.flags = SDMMC_RSP_R3; + + /* Try to send the command. */ + if (!sdmmc_sd_send_app_cmd(device, &cmd, 0, is_sd_ver2 ? 0 : 0x400000, 0xFFFFFFFF)) + return 0; + + uint32_t resp = 0; + + /* Try to load back the response. */ + if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R3, &resp)) + return 0; + + /* Card Power up bit is set. */ + if (resp & MMC_CARD_BUSY) + { + /* We have a SDHC block mode card. */ + if (resp & SD_OCR_CCS) + device->is_block_sdhc = true; + + /* We asked for low voltage support and the card accepted. */ + if (is_uhs_en && (resp & SD_ROCR_S18A)) + { + /* Voltage switching is only valid for SDMMC1. */ + if (device->sdmmc->controller == SDMMC_1) + { + /* Failed to issue voltage switching command. */ + if (!sdmmc_device_send_r1_cmd(device, SD_SWITCH_VOLTAGE, 0, false, 0, R1_STATE_READY)) + return 0; + + /* Delay a bit before asking for the voltage switch. */ + udelay(1000); + + /* Tell the driver to switch the voltage. */ + if (!sdmmc_switch_voltage(device->sdmmc)) + return 0; + + /* We are now running at 1.8V. */ + device->is_180v = true; + } + } + + return 1; + } + + /* Keep checking if timeout expired. */ + is_timeout = (get_time_since(timebase) > 2000000); + + /* Delay for an appropriate period. */ + udelay(10000); + } + + return 0; +} + +static int sdmmc_sd_send_relative_addr(sdmmc_device_t *device) +{ + sdmmc_command_t cmd = {}; + + cmd.opcode = SD_SEND_RELATIVE_ADDR; + cmd.arg = 0; + cmd.flags = SDMMC_RSP_R6; + + /* Program a large timeout. */ + uint32_t timebase = get_time(); + bool is_timeout = false; + + while (!is_timeout) + { + /* Try to send the command. */ + if (!sdmmc_send_cmd(device->sdmmc, &cmd, 0, 0)) + return 0; + + uint32_t resp = 0; + + /* Try to load back the response. */ + if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R6, &resp)) + return 0; + + /* Save the RCA. */ + if (resp >> 16) + { + device->rca = (resp >> 16); + return 1; + } + + /* Keep checking if timeout expired. */ + is_timeout = (get_time_since(timebase) > 2000000); + + /* Delay for an appropriate period. */ + udelay(1000); + } + + return 0; +} + +static int sdmmc_sd_send_scr(sdmmc_device_t *device, uint8_t *scr) +{ + sdmmc_command_t cmd = {}; + sdmmc_request_t req = {}; + + cmd.opcode = SD_APP_SEND_SCR; + cmd.arg = 0; + cmd.flags = SDMMC_RSP_R1; + + req.data = scr; + req.blksz = 8; + req.num_blocks = 1; + req.is_read = true; + req.is_multi_block = false; + req.is_auto_cmd12 = false; + + /* Try to send the APP command. */ + if (!sdmmc_sd_send_app_cmd(device, &cmd, &req, 0, R1_STATE_TRAN)) + return 0; + + uint32_t resp = 0; + + /* Try to load back the response. */ + if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R1, &resp)) + return 0; + + /* Evaluate the response. */ + if (is_sdmmc_device_r1_error(resp)) + return 0; + else + return 1; +} + +static int sdmmc_sd_set_clr_card_detect(sdmmc_device_t *device) +{ + /* Try to send the APP command. */ + if (!sdmmc_device_send_r1_cmd(device, MMC_APP_CMD, (device->rca << 16), false, 0, R1_STATE_TRAN)) + return 0; + + /* Try to send the command. */ + return sdmmc_device_send_r1_cmd(device, SD_APP_SET_CLR_CARD_DETECT, 0, false, 0, R1_STATE_TRAN); +} + +static int sdmmc_sd_set_bus_width(sdmmc_device_t *device) +{ + /* Try to send the APP command. */ + if (!sdmmc_device_send_r1_cmd(device, MMC_APP_CMD, (device->rca << 16), false, 0, R1_STATE_TRAN)) + return 0; + + /* Try to send the command. */ + return sdmmc_device_send_r1_cmd(device, SD_APP_SET_BUS_WIDTH, SD_BUS_WIDTH_4, false, 0, R1_STATE_TRAN); +} + +static int sdmmc_sd_switch(sdmmc_device_t *device, uint32_t mode, uint32_t group, uint8_t value, uint8_t *data) +{ + sdmmc_command_t cmd = {}; + sdmmc_request_t req = {}; + + cmd.opcode = SD_SWITCH; + cmd.arg = ((mode << 31) | 0x00FFFFFF); + cmd.arg &= ~(0xF << (group * 4)); + cmd.arg |= (value << (group * 4)); + cmd.flags = SDMMC_RSP_R1; + + req.data = data; + req.blksz = 64; + req.num_blocks = 1; + req.is_read = true; + req.is_multi_block = false; + req.is_auto_cmd12 = false; + + /* Try to send the command. */ + if (!sdmmc_send_cmd(device->sdmmc, &cmd, &req, 0)) + return 0; + + uint32_t resp = 0; + + /* Try to load back the response. */ + if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R1, &resp)) + return 0; + + /* Evaluate the response. */ + if (is_sdmmc_device_r1_error(resp)) + return 0; + else + return 1; +} + +static int sdmmc_sd_set_current_limit(sdmmc_device_t *device, uint8_t *status) +{ + /* Start with the highest possible limit. */ + uint32_t current_limit = SD_SET_CURRENT_LIMIT_800; + + /* Try each limit. */ + while (current_limit > SD_SET_CURRENT_NO_CHANGE) + { + /* Switch the current limit. */ + if (!sdmmc_sd_switch(device, SD_SWITCH_SET, 3, current_limit, status)) + return 0; + + /* Current limit was set successfully. */ + if (((status[15] >> 4) & 0x0F) == current_limit) + break; + + current_limit--; + } + + return 1; +} + +static int sdmmc_sd_switch_hs(sdmmc_device_t *device, uint32_t type, uint8_t *status) +{ + /* Test if the card supports high-speed mode. */ + if (!sdmmc_sd_switch(device, 0, 0, type, status)) + return 0; + + uint32_t res_type = (status[16] & 0xF); + + /* This high-speed mode type is not supported. */ + if (res_type != type) + return 0; + + if ((((uint16_t)status[0] << 8) | status[1]) < 0x320) + { + /* Try to switch to high-speed mode. */ + if (!sdmmc_sd_switch(device, 1, 0, type, status)) + return 0; + + /* Something failed when switching to high-speed mode. */ + if ((status[16] & 0xF) != res_type) + return 0; + } + + return 1; +} + +static int sdmmc_sd_switch_hs_low(sdmmc_device_t *device, uint8_t *status) +{ + /* Adjust the current limit. */ + if (!sdmmc_sd_set_current_limit(device, status)) + return 0; + + /* Invalid bus width. */ + if (device->sdmmc->bus_width != SDMMC_BUS_WIDTH_4BIT) + return 0; + + /* Get the supported high-speed type. */ + if (!sdmmc_sd_switch(device, 0, 0, 0xF, status)) + return 0; + + /* High-speed SDR104 is supported. */ + if (status[13] & SD_MODE_UHS_SDR104) + { + /* Switch to high-speed. */ + if (!sdmmc_sd_switch_hs(device, UHS_SDR104_BUS_SPEED, status)) + return 0; + + /* Reconfigure the internal clock. */ + if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SDR104)) + return 0; + + /* Run tuning. */ + if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_SDR104, MMC_SEND_TUNING_BLOCK)) + return 0; + } + else if (status[13] & SD_MODE_UHS_SDR50) /* High-speed SDR50 is supported. */ + { + /* Switch to high-speed. */ + if (!sdmmc_sd_switch_hs(device, UHS_SDR50_BUS_SPEED, status)) + return 0; + + /* Reconfigure the internal clock. */ + if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SDR50)) + return 0; + + /* Run tuning. */ + if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_SDR50, MMC_SEND_TUNING_BLOCK)) + return 0; + } + else if (status[13] & SD_MODE_UHS_SDR12) /* High-speed SDR12 is supported. */ + { + /* Switch to high-speed. */ + if (!sdmmc_sd_switch_hs(device, UHS_SDR12_BUS_SPEED, status)) + return 0; + + /* Reconfigure the internal clock. */ + if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SDR12)) + return 0; + + /* Run tuning. */ + if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_SDR12, MMC_SEND_TUNING_BLOCK)) + return 0; + } + else + return 0; + + /* Peek the SD card's status. */ + return sdmmc_device_send_status(device); +} + +static int sdmmc_sd_switch_hs_high(sdmmc_device_t *device, uint8_t *status) +{ + /* Get the supported high-speed type. */ + if (!sdmmc_sd_switch(device, 0, 0, 0xF, status)) + return 0; + + /* High-speed is supported. */ + if (status[13] & 2) + { + /* Switch to high-speed. */ + if (!sdmmc_sd_switch_hs(device, SDHCI_CTRL_UHS_SDR25, status)) + return 0; + + /* Reconfigure the internal clock. */ + if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SDR25)) + return 0; + + /* Peek the SD card's status. */ + return sdmmc_device_send_status(device); + } + + /* Nothing to do. */ + return 1; +} + +static int sdmmc_sd_status(sdmmc_device_t *device, uint8_t *ssr) +{ + sdmmc_command_t cmd = {}; + sdmmc_request_t req = {}; + + cmd.opcode = SD_APP_SD_STATUS; + cmd.arg = 0; + cmd.flags = SDMMC_RSP_R1; + + req.data = ssr; + req.blksz = 64; + req.num_blocks = 1; + req.is_read = true; + req.is_multi_block = false; + req.is_auto_cmd12 = false; + + /* Try to send the APP command. */ + if (!sdmmc_sd_send_app_cmd(device, &cmd, &req, 0, R1_STATE_TRAN)) + return 0; + + uint32_t resp = 0; + + /* Try to load back the response. */ + if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R1, &resp)) + return 0; + + /* Evaluate the response. */ + if (is_sdmmc_device_r1_error(resp)) + return 0; + else + return 1; +} + +int sdmmc_device_sd_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth bus_width, SdmmcBusSpeed bus_speed) +{ + bool is_sd_ver2 = false; + uint32_t cid[4] = {0}; + uint32_t csd[4] = {0}; + uint8_t scr[8] = {0}; + uint8_t ssr[64] = {0}; + uint8_t switch_status[512] = {0}; + + /* Initialize our device's struct. */ + memset(device, 0, sizeof(sdmmc_device_t)); + + /* Try to initialize the driver. */ + if (!sdmmc_init(sdmmc, SDMMC_1, SDMMC_VOLTAGE_3V3, SDMMC_BUS_WIDTH_1BIT, SDMMC_SPEED_INIT_SDR)) + { + sdmmc_error(sdmmc, "Failed to initialize the SDMMC driver!"); + return 0; + } + + /* Bind the underlying driver. */ + device->sdmmc = sdmmc; + + sdmmc_info(sdmmc, "SDMMC driver was successfully initialized for SD!"); + + /* Apply at least 74 clock cycles. The card should be ready afterwards. */ + udelay((74000 + sdmmc->internal_divider - 1) / sdmmc->internal_divider); + + /* Instruct the SD card to go idle. */ + if (!sdmmc_device_go_idle(device)) + { + sdmmc_error(sdmmc, "Failed to go idle!"); + return 0; + } + + sdmmc_info(sdmmc, "SD card went idle!"); + + /* Get the SD card's interface operating condition. */ + if (!sdmmc_sd_send_if_cond(device, &is_sd_ver2)) + { + sdmmc_error(sdmmc, "Failed to send if cond!"); + return 0; + } + + sdmmc_info(sdmmc, "Sent if cond to SD card!"); + + /* Get the SD card's operating conditions. */ + if (!sdmmc_sd_send_op_cond(device, is_sd_ver2, (bus_width == SDMMC_BUS_WIDTH_4BIT) && (bus_speed == SDMMC_SPEED_SDR104))) + { + sdmmc_error(sdmmc, "Failed to send op cond!"); + return 0; + } + + sdmmc_info(sdmmc, "Sent op cond to SD card!"); + + /* Get the SD card's CID. */ + if (!sdmmc_device_send_cid(device, cid)) + { + sdmmc_error(sdmmc, "Failed to get CID!"); + return 0; + } + + sdmmc_info(sdmmc, "Got CID from SD card!"); + + /* Decode and save the CID. */ + sdmmc_sd_decode_cid(device, cid); + + /* Get the SD card's RCA. */ + if (!sdmmc_sd_send_relative_addr(device)) + { + sdmmc_error(sdmmc, "Failed to get RCA!"); + return 0; + } + + sdmmc_info(sdmmc, "Got RCA (0x%08x) from SD card!", device->rca); + + /* Get the SD card's CSD. */ + if (!sdmmc_device_send_csd(device, csd)) + { + sdmmc_error(sdmmc, "Failed to get CSD!"); + return 0; + } + + sdmmc_info(sdmmc, "Got CSD from SD card!"); + + /* Decode and save the CSD. */ + if (!sdmmc_sd_decode_csd(device, csd)) + sdmmc_warn(sdmmc, "Got unknown CSD structure (0x%08x)!", device->csd.structure); + + /* If we never switched to 1.8V, change the bus speed mode. */ + if (!device->is_180v) + { + /* Reconfigure the internal clock. */ + if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_UNK6)) + { + sdmmc_error(sdmmc, "Failed to apply the correct bus speed!"); + return 0; + } + + sdmmc_info(sdmmc, "Speed mode has been adjusted!"); + } + + /* Select the SD card. */ + if (!sdmmc_device_select_card(device)) + { + sdmmc_error(sdmmc, "Failed to select SD card!"); + return 0; + } + + sdmmc_info(sdmmc, "SD card is now selected!"); + + /* Change the SD card's block length. */ + if (!sdmmc_device_set_blocklen(device, 512)) + { + sdmmc_error(sdmmc, "Failed to set SD card's block length!"); + return 0; + } + + sdmmc_info(sdmmc, "SD card's block length is now 512!"); + + /* It's a good practice to disconnect the pull-up resistor with ACMD42. */ + if (!sdmmc_sd_set_clr_card_detect(device)) + { + sdmmc_error(sdmmc, "Failed to disconnect the pull-up resistor!"); + return 0; + } + + sdmmc_info(sdmmc, "Pull-up resistor is now disconnected!"); + + /* Get the SD card's SCR. */ + if (!sdmmc_sd_send_scr(device, scr)) + { + sdmmc_error(sdmmc, "Failed to get SCR!"); + return 0; + } + + sdmmc_info(sdmmc, "Got SCR from SD card!"); + + /* Decode and save the SCR. */ + if (!sdmmc_sd_decode_scr(device, scr)) + { + sdmmc_error(sdmmc, "Got unknown SCR structure!"); + return 0; + } + + /* Switch to wider bus (if supported). */ + if ((bus_width == SDMMC_BUS_WIDTH_4BIT) + && (device->scr.bus_widths & SD_SCR_BUS_WIDTH_4) + && (device->scr.sda_vsn & (SD_SCR_SPEC_VER_1 | SD_SCR_SPEC_VER_2))) + { + if (!sdmmc_sd_set_bus_width(device)) + { + sdmmc_error(sdmmc, "Failed to switch to wider bus!"); + return 0; + } + + sdmmc_select_bus_width(device->sdmmc, SDMMC_BUS_WIDTH_4BIT); + sdmmc_info(sdmmc, "Switched to wider bus!"); + } + + if (device->is_180v) + { + /* Switch to high-speed from low voltage (if possible). */ + if (!sdmmc_sd_switch_hs_low(device, switch_status)) + { + sdmmc_error(sdmmc, "Failed to switch to high-speed!"); + return 0; + } + + sdmmc_info(sdmmc, "Switched to high-speed!"); + } + else if ((device->scr.sda_vsn & (SD_SCR_SPEC_VER_1 | SD_SCR_SPEC_VER_2)) && ((bus_speed != SDMMC_SPEED_UNK6))) + { + /* Switch to high-speed from high voltage (if possible). */ + if (!sdmmc_sd_switch_hs_high(device, switch_status)) + { + sdmmc_error(sdmmc, "Failed to switch to high-speed!"); + return 0; + } + + sdmmc_info(sdmmc, "Switched to high-speed!"); + } + + /* Correct any inconsistent states. */ + sdmmc_adjust_sd_clock(sdmmc); + + /* Get the SD card's SSR. */ + if (!sdmmc_sd_status(device, ssr)) + { + sdmmc_error(sdmmc, "Failed to get SSR!"); + return 0; + } + + sdmmc_info(sdmmc, "Got SSR from SD card!"); + + /* Decode and save the SSR. */ + sdmmc_sd_decode_ssr(device, scr); + + return 1; +} + +/* + MMC device functions. +*/ + +static void sdmmc_mmc_decode_cid(sdmmc_device_t *device, uint32_t *cid) +{ + switch (device->csd.mmca_vsn) + { + case 0: /* MMC v1.0 - v1.2 */ + case 1: /* MMC v1.4 */ + device->cid.prod_name[6] = UNSTUFF_BITS(cid, 48, 8); + device->cid.manfid = UNSTUFF_BITS(cid, 104, 24); + device->cid.hwrev = UNSTUFF_BITS(cid, 44, 4); + device->cid.fwrev = UNSTUFF_BITS(cid, 40, 4); + device->cid.serial = UNSTUFF_BITS(cid, 16, 24); + break; + case 2: /* MMC v2.0 - v2.2 */ + case 3: /* MMC v3.1 - v3.3 */ + case 4: /* MMC v4 */ + device->cid.manfid = UNSTUFF_BITS(cid, 120, 8); + device->cid.oemid = UNSTUFF_BITS(cid, 104, 8); + device->cid.prv = UNSTUFF_BITS(cid, 48, 8); + device->cid.serial = UNSTUFF_BITS(cid, 16, 32); + break; + default: + break; + } + + device->cid.prod_name[0] = UNSTUFF_BITS(cid, 96, 8); + device->cid.prod_name[1] = UNSTUFF_BITS(cid, 88, 8); + device->cid.prod_name[2] = UNSTUFF_BITS(cid, 80, 8); + device->cid.prod_name[3] = UNSTUFF_BITS(cid, 72, 8); + device->cid.prod_name[4] = UNSTUFF_BITS(cid, 64, 8); + device->cid.prod_name[5] = UNSTUFF_BITS(cid, 56, 8); + + device->cid.month = UNSTUFF_BITS(cid, 12, 4); + device->cid.year = (UNSTUFF_BITS(cid, 8, 4) + 1997); + + if ((device->ext_csd.rev >= 5) && (device->cid.year < 2010)) + device->cid.year += 16; +} + +static int sdmmc_mmc_decode_csd(sdmmc_device_t *device, uint32_t *csd) +{ + unsigned int e, m, a, b; + + device->csd.structure = UNSTUFF_BITS(csd, 126, 2); + + if (!device->csd.structure) { + return 0; + } + + device->csd.mmca_vsn = UNSTUFF_BITS(csd, 122, 4); + + m = UNSTUFF_BITS(csd, 115, 4); + e = UNSTUFF_BITS(csd, 112, 3); + device->csd.taac_ns = ((taac_exp[e] * taac_mant[m] + 9) / 10); + device->csd.taac_clks = (UNSTUFF_BITS(csd, 104, 8) * 100); + + m = UNSTUFF_BITS(csd, 99, 4); + e = UNSTUFF_BITS(csd, 96, 3); + device->csd.max_dtr = (tran_exp[e] * tran_mant[m]); + device->csd.cmdclass = UNSTUFF_BITS(csd, 84, 12); + + e = UNSTUFF_BITS(csd, 47, 3); + m = UNSTUFF_BITS(csd, 62, 12); + device->csd.capacity = ((1 + m) << (e + 2)); + + device->csd.read_blkbits = UNSTUFF_BITS(csd, 80, 4); + device->csd.read_partial = UNSTUFF_BITS(csd, 79, 1); + device->csd.write_misalign = UNSTUFF_BITS(csd, 78, 1); + device->csd.read_misalign = UNSTUFF_BITS(csd, 77, 1); + device->csd.dsr_imp = UNSTUFF_BITS(csd, 76, 1); + device->csd.r2w_factor = UNSTUFF_BITS(csd, 26, 3); + device->csd.write_blkbits = UNSTUFF_BITS(csd, 22, 4); + device->csd.write_partial = UNSTUFF_BITS(csd, 21, 1); + + if (device->csd.write_blkbits >= 9) + { + a = UNSTUFF_BITS(csd, 42, 5); + b = UNSTUFF_BITS(csd, 37, 5); + device->csd.erase_size = ((a + 1) * (b + 1)); + device->csd.erase_size <<= (device->csd.write_blkbits - 9); + } + + return 1; +} + +static void sdmmc_mmc_decode_ext_csd(sdmmc_device_t *device, uint8_t *ext_csd) +{ + device->ext_csd.rev = ext_csd[EXT_CSD_REV]; + device->ext_csd.raw_ext_csd_structure = ext_csd[EXT_CSD_STRUCTURE]; + device->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE]; + device->ext_csd.raw_rpmb_size_mult = ext_csd[EXT_CSD_RPMB_MULT]; + device->ext_csd.raw_sectors[0] = ext_csd[EXT_CSD_SEC_CNT + 0]; + device->ext_csd.raw_sectors[1] = ext_csd[EXT_CSD_SEC_CNT + 1]; + device->ext_csd.raw_sectors[2] = ext_csd[EXT_CSD_SEC_CNT + 2]; + device->ext_csd.raw_sectors[3] = ext_csd[EXT_CSD_SEC_CNT + 3]; + device->ext_csd.bkops = ext_csd[EXT_CSD_BKOPS_SUPPORT]; + device->ext_csd.man_bkops_en = ext_csd[EXT_CSD_BKOPS_EN]; + device->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS]; +} + +static int sdmmc_mmc_send_op_cond(sdmmc_device_t *device, SdmmcBusVoltage bus_voltage) +{ + sdmmc_command_t cmd = {}; + + /* Program a large timeout. */ + uint32_t timebase = get_time(); + bool is_timeout = false; + + while (!is_timeout) + { + /* Set high capacity bit. */ + uint32_t arg = SD_OCR_CCS; + + /* Set voltage bits. */ + if (bus_voltage == SDMMC_VOLTAGE_1V8) + arg |= MMC_VDD_165_195; + else if (bus_voltage == SDMMC_VOLTAGE_3V3) + arg |= (MMC_VDD_33_34 | MMC_VDD_32_33 | MMC_VDD_31_32 | MMC_VDD_30_31 | MMC_VDD_29_30 | MMC_VDD_28_29 | MMC_VDD_27_28); + else + return 0; + + cmd.opcode = MMC_SEND_OP_COND; + cmd.arg = arg; + cmd.flags = SDMMC_RSP_R3; + + /* Try to send the command. */ + if (!sdmmc_send_cmd(device->sdmmc, &cmd, 0, 0)) + return 0; + + uint32_t resp = 0; + + /* Try to load back the response. */ + if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R3, &resp)) + return 0; + + /* Card Power up bit is set. */ + if (resp & MMC_CARD_BUSY) + { + /* We have a SDHC block mode card. */ + if (resp & SD_OCR_CCS) + device->is_block_sdhc = true; + + return 1; + } + + /* Keep checking if timeout expired. */ + is_timeout = (get_time_since(timebase) > 2000000); + + /* Delay for an appropriate period. */ + udelay(10000); + } + + return 0; +} + +static int sdmmc_mmc_send_ext_csd(sdmmc_device_t *device, uint8_t *ext_csd) +{ + sdmmc_command_t cmd = {}; + sdmmc_request_t req = {}; + + cmd.opcode = MMC_SEND_EXT_CSD; + cmd.arg = 0; + cmd.flags = SDMMC_RSP_R1; + + req.data = ext_csd; + req.blksz = 512; + req.num_blocks = 1; + req.is_read = true; + req.is_multi_block = false; + req.is_auto_cmd12 = false; + + /* Try to send the command. */ + if (!sdmmc_send_cmd(device->sdmmc, &cmd, &req, 0)) + return 0; + + uint32_t resp = 0; + + /* Try to load back the response. */ + if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R1, &resp)) + return 0; + + /* Evaluate the response. */ + if (is_sdmmc_device_r1_error(resp)) + return 0; + else + return 1; +} + +static int sdmmc_mmc_set_relative_addr(sdmmc_device_t *device) +{ + /* Try to send the command. */ + return sdmmc_device_send_r1_cmd(device, MMC_SET_RELATIVE_ADDR, (device->rca << 16), false, 0, 0xFFFFFFFF); +} + +static int sdmmc_mmc_switch(sdmmc_device_t *device, uint32_t arg) +{ + /* Try to send the command. */ + return sdmmc_device_send_r1_cmd(device, MMC_SWITCH, arg, true, 0, 0xFFFFFFFF); +} + +static int sdmmc_mmc_select_bus_width(sdmmc_device_t *device, SdmmcBusWidth bus_width) +{ + uint32_t arg = 0; + + /* Choose the argument for the switch command. */ + switch (bus_width) + { + case SDMMC_BUS_WIDTH_1BIT: + return 1; + case SDMMC_BUS_WIDTH_4BIT: + arg = (((MMC_SWITCH_MODE_WRITE_BYTE) << 24) | ((EXT_CSD_BUS_WIDTH) << 16) | ((EXT_CSD_BUS_WIDTH_4) << 8)); + break; + case SDMMC_BUS_WIDTH_8BIT: + arg = (((MMC_SWITCH_MODE_WRITE_BYTE) << 24) | ((EXT_CSD_BUS_WIDTH) << 16) | ((EXT_CSD_BUS_WIDTH_8) << 8)); + break; + default: + return 0; + } + + /* Try to switch the bus width. */ + if (sdmmc_mmc_switch(device, arg) && sdmmc_device_send_status(device)) + { + sdmmc_select_bus_width(device->sdmmc, bus_width); + return 1; + } + + return 0; +} + +static int sdmmc_mmc_select_hs(sdmmc_device_t *device, bool ignore_status) +{ + uint32_t arg = (((MMC_SWITCH_MODE_WRITE_BYTE) << 24) | ((EXT_CSD_HS_TIMING) << 16) | ((EXT_CSD_TIMING_HS) << 8)); + + /* Try to switch to HS. */ + if (!sdmmc_mmc_switch(device, arg)) + return 0; + + /* Check the status if necessary. */ + if (!ignore_status && !sdmmc_device_send_status(device)) + return 0; + + /* Reconfigure the internal clock. */ + if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_HS52)) + return 0; + + /* Check the status if necessary. */ + if (!ignore_status && !sdmmc_device_send_status(device)) + return 0; + + return 1; +} + +static int sdmmc_mmc_select_hs200(sdmmc_device_t *device) +{ + uint32_t arg = (((MMC_SWITCH_MODE_WRITE_BYTE) << 24) | ((EXT_CSD_HS_TIMING) << 16) | ((EXT_CSD_TIMING_HS200) << 8)); + + /* Try to switch to HS200. */ + if (!sdmmc_mmc_switch(device, arg)) + return 0; + + /* Reconfigure the internal clock. */ + if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_HS200)) + return 0; + + /* Execute tuning procedure. */ + if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_HS200, MMC_SEND_TUNING_BLOCK_HS200)) + return 0; + + /* Peek the current status. */ + return sdmmc_device_send_status(device); +} + +static int sdmmc_mmc_select_hs400(sdmmc_device_t *device) +{ + uint32_t arg = 0; + + /* Switch to HS200 first. */ + if (!sdmmc_mmc_select_hs200(device)) + return 0; + + /* Fetch and set the tuning tap value. */ + sdmmc_set_tuning_tap_val(device->sdmmc); + + /* Switch to HS. */ + if (!sdmmc_mmc_select_hs(device, true)) + return 0; + + arg = (((MMC_SWITCH_MODE_WRITE_BYTE) << 24) | ((EXT_CSD_BUS_WIDTH) << 16) | ((EXT_CSD_DDR_BUS_WIDTH_8) << 8)); + + /* Try to switch to 8bit bus. */ + if (!sdmmc_mmc_switch(device, arg)) + return 0; + + arg = (((MMC_SWITCH_MODE_WRITE_BYTE) << 24) | ((EXT_CSD_HS_TIMING) << 16) | ((EXT_CSD_TIMING_HS400) << 8)); + + /* Try to switch to HS400. */ + if (!sdmmc_mmc_switch(device, arg)) + return 0; + + /* Reconfigure the internal clock. */ + if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_HS400)) + return 0; + + /* Peek the current status. */ + return sdmmc_device_send_status(device); +} + +static int sdmmc_mmc_select_timing(sdmmc_device_t *device, SdmmcBusSpeed bus_speed) +{ + if ((bus_speed == SDMMC_SPEED_HS400) && + (device->sdmmc->bus_width == SDMMC_BUS_WIDTH_8BIT) && + (device->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_HS400_1_8V)) + { + /* Switch to HS400. */ + return sdmmc_mmc_select_hs400(device); + } + else if (((bus_speed == SDMMC_SPEED_HS400) || (bus_speed == SDMMC_SPEED_HS200)) && + ((device->sdmmc->bus_width == SDMMC_BUS_WIDTH_8BIT) || (device->sdmmc->bus_width == SDMMC_BUS_WIDTH_4BIT)) && + (device->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_HS200_1_8V)) + { + /* Switch to HS200. */ + return sdmmc_mmc_select_hs200(device); + } + else if (device->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_HS_52) + { + /* Switch to HS. */ + return sdmmc_mmc_select_hs(device, false); + } + + return 0; +} + +static int sdmmc_mmc_select_bkops(sdmmc_device_t *device) +{ + uint32_t arg = (((MMC_SWITCH_MODE_SET_BITS) << 24) | ((EXT_CSD_BKOPS_EN) << 16) | ((EXT_CSD_BKOPS_LEVEL_2) << 8)); + + /* Try to enable bkops. */ + 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}; + + /* Initialize our device's struct. */ + memset(device, 0, sizeof(sdmmc_device_t)); + + /* Try to initialize the driver. */ + if (!sdmmc_init(sdmmc, SDMMC_4, SDMMC_VOLTAGE_1V8, SDMMC_BUS_WIDTH_1BIT, SDMMC_SPEED_INIT_HS)) + { + sdmmc_error(sdmmc, "Failed to initialize the SDMMC driver!"); + return 0; + } + + /* Bind the underlying driver. */ + device->sdmmc = sdmmc; + + sdmmc_info(sdmmc, "SDMMC driver was successfully initialized for eMMC!"); + + /* Apply at least 74 clock cycles. eMMC should be ready afterwards. */ + udelay((74000 + sdmmc->internal_divider - 1) / sdmmc->internal_divider); + + /* Instruct the eMMC to go idle. */ + if (!sdmmc_device_go_idle(device)) + { + sdmmc_error(sdmmc, "Failed to go idle!"); + return 0; + } + + sdmmc_info(sdmmc, "eMMC went idle!"); + + /* Get the eMMC's operating conditions. */ + if (!sdmmc_mmc_send_op_cond(device, SDMMC_VOLTAGE_1V8)) + { + sdmmc_error(sdmmc, "Failed to send op cond!"); + return 0; + } + + sdmmc_info(sdmmc, "Sent op cond to eMMC!"); + + /* Get the eMMC's CID. */ + if (!sdmmc_device_send_cid(device, cid)) + { + sdmmc_error(sdmmc, "Failed to get CID!"); + return 0; + } + + sdmmc_info(sdmmc, "Got CID from eMMC!"); + + /* Get the eMMC's RCA. */ + if (!sdmmc_mmc_set_relative_addr(device)) + { + sdmmc_error(sdmmc, "Failed to get RCA!"); + return 0; + } + + sdmmc_info(sdmmc, "Got RCA (0x%08x) from eMMC!", device->rca); + + /* Get the eMMC card's CSD. */ + if (!sdmmc_device_send_csd(device, csd)) + { + sdmmc_error(sdmmc, "Failed to get CSD!"); + return 0; + } + + sdmmc_info(sdmmc, "Got CSD from eMMC!"); + + /* Decode and save the CSD. */ + if (!sdmmc_mmc_decode_csd(device, csd)) + sdmmc_warn(sdmmc, "Got unknown CSD structure (0x%08x)!", device->csd.structure); + + /* Reconfigure the internal clock. */ + if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_HS26)) + { + sdmmc_error(sdmmc, "Failed to apply the correct bus speed!"); + return 0; + } + + sdmmc_info(sdmmc, "Speed mode has been adjusted!"); + + /* Select the eMMC card. */ + if (!sdmmc_device_select_card(device)) + { + sdmmc_error(sdmmc, "Failed to select eMMC card!"); + return 0; + } + + sdmmc_info(sdmmc, "eMMC card is now selected!"); + + /* Change the eMMC's block length. */ + if (!sdmmc_device_set_blocklen(device, 512)) + { + sdmmc_error(sdmmc, "Failed to set eMMC's block length!"); + return 0; + } + + sdmmc_info(sdmmc, "eMMC's block length is now 512!"); + + /* Only specification version 4 and later support the next features. */ + if (device->csd.mmca_vsn < CSD_SPEC_VER_4) + return 1; + + /* Change the eMMC's bus width. */ + if (!sdmmc_mmc_select_bus_width(device, bus_width)) + { + sdmmc_error(sdmmc, "Failed to set eMMC's bus width!"); + return 0; + } + + sdmmc_info(sdmmc, "eMMC's bus width has been adjusted!"); + + /* Get the eMMC's extended CSD. */ + if (!sdmmc_mmc_send_ext_csd(device, ext_csd)) + { + sdmmc_error(sdmmc, "Failed to get EXT_CSD!"); + return 0; + } + + sdmmc_info(sdmmc, "Got EXT_CSD from eMMC!"); + + /* Decode and save the extended CSD. */ + sdmmc_mmc_decode_ext_csd(device, ext_csd); + + /* 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)) + { + sdmmc_mmc_select_bkops(device); + sdmmc_info(sdmmc, "BKOPS is enabled!"); + } + else + sdmmc_info(sdmmc, "BKOPS is disabled!"); + + /* Switch to high speed mode. */ + if (!sdmmc_mmc_select_timing(device, bus_speed)) + { + sdmmc_error(sdmmc, "Failed to switch to high speed mode!"); + return 0; + } + + sdmmc_info(sdmmc, "Switched to high speed mode!"); + + /* Correct any inconsistent states. */ + sdmmc_adjust_sd_clock(sdmmc); + + return 1; +} diff --git a/fusee/fusee-primary/src/sdmmc/sdmmc.h b/fusee/fusee-primary/src/sdmmc/sdmmc.h new file mode 100644 index 000000000..6e955644b --- /dev/null +++ b/fusee/fusee-primary/src/sdmmc/sdmmc.h @@ -0,0 +1,160 @@ +#ifndef FUSEE_SDMMC_H +#define FUSEE_SDMMC_H + +#include "sdmmc_core.h" + +/* Structure for storing the MMC CID (adapted from Linux headers) */ +typedef struct { + uint32_t manfid; + uint8_t prod_name[8]; + uint8_t prv; + uint32_t serial; + uint16_t oemid; + uint16_t year; + uint8_t hwrev; + uint8_t fwrev; + uint8_t month; +} mmc_cid_t; + +/* Structure for storing the MMC CSD (adapted from Linux headers) */ +typedef struct { + uint8_t structure; + uint8_t mmca_vsn; + uint16_t cmdclass; + uint16_t taac_clks; + uint32_t taac_ns; + uint32_t c_size; + uint32_t r2w_factor; + uint32_t max_dtr; + uint32_t erase_size; /* In sectors */ + uint32_t read_blkbits; + uint32_t write_blkbits; + uint32_t capacity; + uint32_t read_partial:1, + read_misalign:1, + write_partial:1, + write_misalign:1, + dsr_imp:1; +} mmc_csd_t; + +/* Structure for storing the MMC extended CSD (adapted from Linux headers) */ +typedef struct { + uint8_t rev; + uint8_t erase_group_def; + uint8_t sec_feature_support; + uint8_t rel_sectors; + uint8_t rel_param; + uint8_t part_config; + uint8_t cache_ctrl; + uint8_t rst_n_function; + uint8_t max_packed_writes; + uint8_t max_packed_reads; + uint8_t packed_event_en; + uint32_t part_time; /* Units: ms */ + uint32_t sa_timeout; /* Units: 100ns */ + uint32_t generic_cmd6_time; /* Units: 10ms */ + uint32_t power_off_longtime; /* Units: ms */ + uint8_t power_off_notification; /* state */ + uint32_t hs_max_dtr; + uint32_t hs200_max_dtr; + uint32_t sectors; + uint32_t hc_erase_size; /* In sectors */ + uint32_t hc_erase_timeout; /* In milliseconds */ + uint32_t sec_trim_mult; /* Secure trim multiplier */ + uint32_t sec_erase_mult; /* Secure erase multiplier */ + uint32_t trim_timeout; /* In milliseconds */ + uint32_t partition_setting_completed; /* enable bit */ + uint64_t enhanced_area_offset; /* Units: Byte */ + uint32_t enhanced_area_size; /* Units: KB */ + uint32_t cache_size; /* Units: KB */ + uint32_t hpi_en; /* HPI enablebit */ + uint32_t hpi; /* HPI support bit */ + uint32_t hpi_cmd; /* cmd used as HPI */ + uint32_t bkops; /* background support bit */ + uint32_t man_bkops_en; /* manual bkops enable bit */ + uint32_t auto_bkops_en; /* auto bkops enable bit */ + uint32_t data_sector_size; /* 512 bytes or 4KB */ + uint32_t data_tag_unit_size; /* DATA TAG UNIT size */ + uint32_t boot_ro_lock; /* ro lock support */ + uint32_t boot_ro_lockable; + uint32_t ffu_capable; /* Firmware upgrade support */ + uint32_t cmdq_en; /* Command Queue enabled */ + uint32_t cmdq_support; /* Command Queue supported */ + uint32_t cmdq_depth; /* Command Queue depth */ + uint8_t fwrev[8]; /* FW version */ + uint8_t raw_exception_status; /* 54 */ + uint8_t raw_partition_support; /* 160 */ + uint8_t raw_rpmb_size_mult; /* 168 */ + uint8_t raw_erased_mem_count; /* 181 */ + uint8_t strobe_support; /* 184 */ + uint8_t raw_ext_csd_structure; /* 194 */ + uint8_t raw_card_type; /* 196 */ + uint8_t raw_driver_strength; /* 197 */ + uint8_t out_of_int_time; /* 198 */ + uint8_t raw_pwr_cl_52_195; /* 200 */ + uint8_t raw_pwr_cl_26_195; /* 201 */ + uint8_t raw_pwr_cl_52_360; /* 202 */ + uint8_t raw_pwr_cl_26_360; /* 203 */ + uint8_t raw_s_a_timeout; /* 217 */ + uint8_t raw_hc_erase_gap_size; /* 221 */ + uint8_t raw_erase_timeout_mult; /* 223 */ + uint8_t raw_hc_erase_grp_size; /* 224 */ + uint8_t raw_sec_trim_mult; /* 229 */ + uint8_t raw_sec_erase_mult; /* 230 */ + uint8_t raw_sec_feature_support; /* 231 */ + uint8_t raw_trim_mult; /* 232 */ + uint8_t raw_pwr_cl_200_195; /* 236 */ + uint8_t raw_pwr_cl_200_360; /* 237 */ + uint8_t raw_pwr_cl_ddr_52_195; /* 238 */ + uint8_t raw_pwr_cl_ddr_52_360; /* 239 */ + uint8_t raw_pwr_cl_ddr_200_360; /* 253 */ + uint8_t raw_bkops_status; /* 246 */ + uint8_t raw_sectors[4]; /* 212 - 4 bytes */ + uint8_t pre_eol_info; /* 267 */ + uint8_t device_life_time_est_typ_a; /* 268 */ + uint8_t device_life_time_est_typ_b; /* 269 */ + uint32_t feature_support; +} mmc_ext_csd_t; + +/* Structure for storing the SD SCR (adapted from Linux headers) */ +typedef struct { + uint8_t sda_vsn; + uint8_t sda_spec3; + uint8_t bus_widths; + uint8_t cmds; +} sd_scr_t; + +/* Structure for storing the SD SSR (adapted from Linux headers) */ +typedef struct { + uint8_t dat_bus_width; + uint8_t secured_mode; + uint16_t sd_card_type; + uint8_t speed_class; + uint8_t uhs_speed_grade; + uint8_t uhs_au_size; + uint8_t video_speed_class; + uint8_t app_perf_class; +} sd_ssr_t; + +/* Structure describing a SDMMC device's context. */ +typedef struct { + /* Underlying driver context. */ + sdmmc_t *sdmmc; + + bool is_180v; + bool is_block_sdhc; + uint32_t rca; + mmc_cid_t cid; + mmc_csd_t csd; + mmc_ext_csd_t ext_csd; + sd_scr_t scr; + sd_ssr_t ssr; +} sdmmc_device_t; + +int sdmmc_device_sd_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth bus_width, SdmmcBusSpeed bus_speed); +int sdmmc_device_mmc_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth bus_width, SdmmcBusSpeed bus_speed); +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); + +#endif \ No newline at end of file diff --git a/fusee/fusee-primary/src/sdmmc/sdmmc_core.c b/fusee/fusee-primary/src/sdmmc/sdmmc_core.c new file mode 100644 index 000000000..4637d5fc5 --- /dev/null +++ b/fusee/fusee-primary/src/sdmmc/sdmmc_core.c @@ -0,0 +1,1971 @@ +#include +#include +#include +#include +#include + +#include "sdmmc_core.h" +#include "../car.h" +#include "../pinmux.h" +#include "../timers.h" +#include "../apb_misc.h" +#include "../gpio.h" +#include "../pmc.h" +#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) +{ + g_sdmmc_log_level = log_level; +} + +static void sdmmc_print(sdmmc_t *sdmmc, SdmmcLogLevel log_level, char *fmt, va_list list) +{ + if (log_level > g_sdmmc_log_level) + return; + + switch (log_level) { + case SDMMC_LOG_ERROR: + printk("%s [ERROR]: ", sdmmc->name); + break; + case SDMMC_LOG_WARN: + printk("%s [WARN]: ", sdmmc->name); + break; + case SDMMC_LOG_INFO: + printk("%s [INFO]: ", sdmmc->name); + break; + case SDMMC_LOG_DEBUG: + printk("%s [DEBUG]: ", sdmmc->name); + break; + default: + break; + } + + vprintk(fmt, list); + printk("\n"); +} + +void sdmmc_error(sdmmc_t *sdmmc, char *fmt, ...) +{ + va_list list; + + va_start(list, fmt); + sdmmc_print(sdmmc, SDMMC_LOG_ERROR, fmt, list); + va_end(list); +} + +void sdmmc_warn(sdmmc_t *sdmmc, char *fmt, ...) +{ + va_list list; + + va_start(list, fmt); + sdmmc_print(sdmmc, SDMMC_LOG_WARN, fmt, list); + va_end(list); +} + +void sdmmc_info(sdmmc_t *sdmmc, char *fmt, ...) +{ + va_list list; + + va_start(list, fmt); + sdmmc_print(sdmmc, SDMMC_LOG_INFO, fmt, list); + va_end(list); +} + +void sdmmc_debug(sdmmc_t *sdmmc, char *fmt, ...) +{ + va_list list; + + va_start(list, fmt); + sdmmc_print(sdmmc, SDMMC_LOG_DEBUG, fmt, list); + va_end(list); +} + +void sdmmc_dump_regs(sdmmc_t *sdmmc) +{ + sdmmc_debug(sdmmc, "dma_address: 0x%08" PRIX32, sdmmc->regs->dma_address); + sdmmc_debug(sdmmc, "block_size: 0x%04" PRIX16, sdmmc->regs->block_size); + sdmmc_debug(sdmmc, "block_count: 0x%04" PRIX16, sdmmc->regs->block_count); + sdmmc_debug(sdmmc, "argument: 0x%08" PRIX32, sdmmc->regs->argument); + sdmmc_debug(sdmmc, "transfer_mode: 0x%04" PRIX16, sdmmc->regs->transfer_mode); + sdmmc_debug(sdmmc, "command: 0x%04" PRIX16, sdmmc->regs->command); + sdmmc_debug(sdmmc, "response[0]: 0x%08" PRIX32, sdmmc->regs->response[0]); + sdmmc_debug(sdmmc, "response[1]: 0x%08" PRIX32, sdmmc->regs->response[1]); + sdmmc_debug(sdmmc, "response[2]: 0x%08" PRIX32, sdmmc->regs->response[2]); + sdmmc_debug(sdmmc, "response[3]: 0x%08" PRIX32, sdmmc->regs->response[3]); + sdmmc_debug(sdmmc, "buffer: 0x%08" PRIX32, sdmmc->regs->buffer); + sdmmc_debug(sdmmc, "present_state: 0x%08" PRIX32, sdmmc->regs->present_state); + sdmmc_debug(sdmmc, "host_control: 0x%02" PRIX8, sdmmc->regs->host_control); + sdmmc_debug(sdmmc, "power_control: 0x%02" PRIX8, sdmmc->regs->power_control); + sdmmc_debug(sdmmc, "block_gap_control: 0x%02" PRIX8, sdmmc->regs->block_gap_control); + sdmmc_debug(sdmmc, "wake_up_control: 0x%02" PRIX8, sdmmc->regs->wake_up_control); + sdmmc_debug(sdmmc, "clock_control: 0x%04" PRIX16, sdmmc->regs->clock_control); + sdmmc_debug(sdmmc, "timeout_control: 0x%02" PRIX8, sdmmc->regs->timeout_control); + sdmmc_debug(sdmmc, "software_reset: 0x%02" PRIX8, sdmmc->regs->software_reset); + sdmmc_debug(sdmmc, "int_status: 0x%08" PRIX32, sdmmc->regs->int_status); + sdmmc_debug(sdmmc, "int_enable: 0x%08" PRIX32, sdmmc->regs->int_enable); + sdmmc_debug(sdmmc, "signal_enable: 0x%08" PRIX32, sdmmc->regs->signal_enable); + sdmmc_debug(sdmmc, "acmd12_err: 0x%04" PRIX16, sdmmc->regs->acmd12_err); + sdmmc_debug(sdmmc, "host_control2: 0x%04" PRIX16, sdmmc->regs->host_control2); + sdmmc_debug(sdmmc, "capabilities: 0x%08" PRIX32, sdmmc->regs->capabilities); + sdmmc_debug(sdmmc, "capabilities_1: 0x%08" PRIX32, sdmmc->regs->capabilities_1); + 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_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); + sdmmc_debug(sdmmc, "preset_for_default: 0x%04" PRIX16, sdmmc->regs->preset_for_default); + sdmmc_debug(sdmmc, "preset_for_high: 0x%04" PRIX16, sdmmc->regs->preset_for_high); + sdmmc_debug(sdmmc, "preset_for_sdr12: 0x%04" PRIX16, sdmmc->regs->preset_for_sdr12); + sdmmc_debug(sdmmc, "preset_for_sdr25: 0x%04" PRIX16, sdmmc->regs->preset_for_sdr25); + sdmmc_debug(sdmmc, "preset_for_sdr50: 0x%04" PRIX16, sdmmc->regs->preset_for_sdr50); + sdmmc_debug(sdmmc, "preset_for_sdr104: 0x%04" PRIX16, sdmmc->regs->preset_for_sdr104); + sdmmc_debug(sdmmc, "preset_for_ddr50: 0x%04" PRIX16, sdmmc->regs->preset_for_ddr50); + sdmmc_debug(sdmmc, "slot_int_status: 0x%04" PRIX16, sdmmc->regs->slot_int_status); + sdmmc_debug(sdmmc, "host_version: 0x%04" PRIX16, sdmmc->regs->host_version); + sdmmc_debug(sdmmc, "vendor_clock_cntrl: 0x%08" PRIX32, sdmmc->regs->vendor_clock_cntrl); + sdmmc_debug(sdmmc, "vendor_sys_sw_cntrl: 0x%08" PRIX32, sdmmc->regs->vendor_sys_sw_cntrl); + sdmmc_debug(sdmmc, "vendor_err_intr_status: 0x%08" PRIX32, sdmmc->regs->vendor_err_intr_status); + sdmmc_debug(sdmmc, "vendor_cap_overrides: 0x%08" PRIX32, sdmmc->regs->vendor_cap_overrides); + sdmmc_debug(sdmmc, "vendor_boot_cntrl: 0x%08" PRIX32, sdmmc->regs->vendor_boot_cntrl); + sdmmc_debug(sdmmc, "vendor_boot_ack_timeout: 0x%08" PRIX32, sdmmc->regs->vendor_boot_ack_timeout); + sdmmc_debug(sdmmc, "vendor_boot_dat_timeout: 0x%08" PRIX32, sdmmc->regs->vendor_boot_dat_timeout); + sdmmc_debug(sdmmc, "vendor_debounce_count: 0x%08" PRIX32, sdmmc->regs->vendor_debounce_count); + sdmmc_debug(sdmmc, "vendor_misc_cntrl: 0x%08" PRIX32, sdmmc->regs->vendor_misc_cntrl); + sdmmc_debug(sdmmc, "max_current_override: 0x%08" PRIX32, sdmmc->regs->max_current_override); + sdmmc_debug(sdmmc, "max_current_override_hi: 0x%08" PRIX32, sdmmc->regs->max_current_override_hi); + sdmmc_debug(sdmmc, "vendor_io_trim_cntrl: 0x%08" PRIX32, sdmmc->regs->vendor_io_trim_cntrl); + sdmmc_debug(sdmmc, "vendor_dllcal_cfg: 0x%08" PRIX32, sdmmc->regs->vendor_dllcal_cfg); + sdmmc_debug(sdmmc, "vendor_dll_ctrl0: 0x%08" PRIX32, sdmmc->regs->vendor_dll_ctrl0); + sdmmc_debug(sdmmc, "vendor_dll_ctrl1: 0x%08" PRIX32, sdmmc->regs->vendor_dll_ctrl1); + sdmmc_debug(sdmmc, "vendor_dllcal_cfg_sta: 0x%08" PRIX32, sdmmc->regs->vendor_dllcal_cfg_sta); + sdmmc_debug(sdmmc, "vendor_tuning_cntrl0: 0x%08" PRIX32, sdmmc->regs->vendor_tuning_cntrl0); + sdmmc_debug(sdmmc, "vendor_tuning_cntrl1: 0x%08" PRIX32, sdmmc->regs->vendor_tuning_cntrl1); + sdmmc_debug(sdmmc, "vendor_tuning_status0: 0x%08" PRIX32, sdmmc->regs->vendor_tuning_status0); + sdmmc_debug(sdmmc, "vendor_tuning_status1: 0x%08" PRIX32, sdmmc->regs->vendor_tuning_status1); + sdmmc_debug(sdmmc, "vendor_clk_gate_hysteresis_count: 0x%08" PRIX32, sdmmc->regs->vendor_clk_gate_hysteresis_count); + sdmmc_debug(sdmmc, "vendor_preset_val0: 0x%08" PRIX32, sdmmc->regs->vendor_preset_val0); + sdmmc_debug(sdmmc, "vendor_preset_val1: 0x%08" PRIX32, sdmmc->regs->vendor_preset_val1); + sdmmc_debug(sdmmc, "vendor_preset_val2: 0x%08" PRIX32, sdmmc->regs->vendor_preset_val2); + sdmmc_debug(sdmmc, "sdmemcomppadctrl: 0x%08" PRIX32, sdmmc->regs->sdmemcomppadctrl); + sdmmc_debug(sdmmc, "auto_cal_config: 0x%08" PRIX32, sdmmc->regs->auto_cal_config); + sdmmc_debug(sdmmc, "auto_cal_interval: 0x%08" PRIX32, sdmmc->regs->auto_cal_interval); + sdmmc_debug(sdmmc, "auto_cal_status: 0x%08" PRIX32, sdmmc->regs->auto_cal_status); + sdmmc_debug(sdmmc, "io_spare: 0x%08" PRIX32, sdmmc->regs->io_spare); + sdmmc_debug(sdmmc, "sdmmca_mccif_fifoctrl: 0x%08" PRIX32, sdmmc->regs->sdmmca_mccif_fifoctrl); + sdmmc_debug(sdmmc, "timeout_wcoal_sdmmca: 0x%08" PRIX32, sdmmc->regs->timeout_wcoal_sdmmca); +} + +typedef struct { + uint32_t clk_source_val; + uint32_t clk_div_val; +} sdmmc_clk_source_t; + +static sdmmc_clk_source_t sdmmc_clk_sources[4] = {0}; + +/* Check if the SDMMC device clock is held in reset. */ +static bool is_sdmmc_clk_rst(SdmmcControllerNum controller) +{ + volatile tegra_car_t *car = car_get_regs(); + + switch (controller) { + case SDMMC_1: + return (car->rst_dev_l & CLK_L_SDMMC1); + case SDMMC_2: + return (car->rst_dev_l & CLK_L_SDMMC2); + case SDMMC_3: + return (car->rst_dev_u & CLK_U_SDMMC3); + case SDMMC_4: + return (car->rst_dev_l & CLK_L_SDMMC4); + } + + return false; +} + +/* Put the SDMMC device clock in reset. */ +static void sdmmc_clk_set_rst(SdmmcControllerNum controller) +{ + volatile tegra_car_t *car = car_get_regs(); + + switch (controller) { + case SDMMC_1: + car->rst_dev_l_set = CLK_L_SDMMC1; + break; + case SDMMC_2: + car->rst_dev_l_set = CLK_L_SDMMC2; + break; + case SDMMC_3: + car->rst_dev_u_set = CLK_U_SDMMC3; + break; + case SDMMC_4: + car->rst_dev_l_set = CLK_L_SDMMC4; + break; + } +} + +/* Take the SDMMC device clock out of reset. */ +static void sdmmc_clk_clear_rst(SdmmcControllerNum controller) +{ + volatile tegra_car_t *car = car_get_regs(); + + switch (controller) { + case SDMMC_1: + car->rst_dev_l_clr = CLK_L_SDMMC1; + break; + case SDMMC_2: + car->rst_dev_l_clr = CLK_L_SDMMC2; + break; + case SDMMC_3: + car->rst_dev_u_clr = CLK_U_SDMMC3; + break; + case SDMMC_4: + car->rst_dev_l_clr = CLK_L_SDMMC4; + break; + } +} + +/* Check if the SDMMC device clock is enabled. */ +static bool is_sdmmc_clk_enb(SdmmcControllerNum controller) +{ + volatile tegra_car_t *car = car_get_regs(); + + switch (controller) { + case SDMMC_1: + return (car->clk_out_enb_l & CLK_L_SDMMC1); + case SDMMC_2: + return (car->clk_out_enb_l & CLK_L_SDMMC2); + case SDMMC_3: + return (car->clk_out_enb_u & CLK_U_SDMMC3); + case SDMMC_4: + return (car->clk_out_enb_l & CLK_L_SDMMC4); + } + + return false; +} + +/* Enable the SDMMC device clock. */ +static void sdmmc_clk_set_enb(SdmmcControllerNum controller) +{ + volatile tegra_car_t *car = car_get_regs(); + + switch (controller) { + case SDMMC_1: + car->clk_enb_l_set = CLK_L_SDMMC1; + break; + case SDMMC_2: + car->clk_enb_l_set = CLK_L_SDMMC2; + break; + case SDMMC_3: + car->clk_enb_u_set = CLK_U_SDMMC3; + break; + case SDMMC_4: + car->clk_enb_l_set = CLK_L_SDMMC4; + break; + } +} + +/* Disable the SDMMC device clock. */ +static void sdmmc_clk_clear_enb(SdmmcControllerNum controller) +{ + volatile tegra_car_t *car = car_get_regs(); + + switch (controller) { + case SDMMC_1: + car->clk_enb_l_clr = CLK_L_SDMMC1; + break; + case SDMMC_2: + car->clk_enb_l_clr = CLK_L_SDMMC2; + break; + case SDMMC_3: + car->clk_enb_u_clr = CLK_U_SDMMC3; + break; + case SDMMC_4: + car->clk_enb_l_clr = CLK_L_SDMMC4; + break; + } +} + +/* Get the appropriate SDMMC maximum frequency. */ +static int sdmmc_get_sdclk_freq(SdmmcBusSpeed bus_speed) +{ + switch (bus_speed) + { + case SDMMC_SPEED_INIT_HS: + case SDMMC_SPEED_HS26: + return 26000; + case SDMMC_SPEED_HS52: + return 52000; + case SDMMC_SPEED_HS200: + case SDMMC_SPEED_HS400: + case SDMMC_SPEED_SDR104: + return 200000; + case SDMMC_SPEED_INIT_SDR: + case SDMMC_SPEED_UNK6: + case SDMMC_SPEED_SDR12: + return 25000; + case SDMMC_SPEED_SDR25: + return 50000; + case SDMMC_SPEED_SDR50: + return 100000; + case SDMMC_SPEED_DDR50: + return 40800; + case SDMMC_SPEED_UNK14: + return 200000; + default: + return 0; + } +} + +/* Get the appropriate SDMMC divider for the SDCLK. */ +static int sdmmc_get_sdclk_div(SdmmcBusSpeed bus_speed) +{ + switch (bus_speed) + { + case SDMMC_SPEED_INIT_HS: + return 66; + case SDMMC_SPEED_INIT_SDR: + // TODO: TRM says return 64? + case SDMMC_SPEED_HS26: + case SDMMC_SPEED_HS52: + case SDMMC_SPEED_HS200: + case SDMMC_SPEED_HS400: + case SDMMC_SPEED_UNK6: + case SDMMC_SPEED_SDR25: + case SDMMC_SPEED_SDR12: + case SDMMC_SPEED_SDR50: + case SDMMC_SPEED_SDR104: + case SDMMC_SPEED_DDR50: + return 1; + case SDMMC_SPEED_UNK14: + return 2; + default: + return 0; + } +} + +/* Set the device clock source and CAR divider. */ +static int sdmmc_clk_set_source(SdmmcControllerNum controller, uint32_t clk_freq) +{ + volatile tegra_car_t *car = car_get_regs(); + + uint32_t car_div = 0; + uint32_t out_freq = 0; + + switch (clk_freq) + { + case 25000: + out_freq = 24728; + car_div = SDMMC_CAR_DIVIDER_SDR12; + break; + case 26000: + out_freq = 25500; + car_div = SDMMC_CAR_DIVIDER_HS26; + break; + case 40800: + out_freq = 40800; + car_div = SDMMC_CAR_DIVIDER_DDR50; + break; + case 50000: + out_freq = 48000; + car_div = SDMMC_CAR_DIVIDER_SDR25; + break; + case 52000: + out_freq = 51000; + car_div = SDMMC_CAR_DIVIDER_HS52; + break; + case 100000: + out_freq = 90667; + car_div = SDMMC_CAR_DIVIDER_SDR50; + break; + case 200000: + out_freq = 163200; + car_div = SDMMC_CAR_DIVIDER_HS200; + break; + case 208000: + out_freq = 204000; + car_div = SDMMC_CAR_DIVIDER_SDR104; + break; + default: + return 0; + } + + sdmmc_clk_sources[controller].clk_source_val = clk_freq; + sdmmc_clk_sources[controller].clk_div_val = out_freq; + + switch (controller) + { + case SDMMC_1: + car->clk_src[CLK_SOURCE_SDMMC1] = (CLK_SOURCE_FIRST | car_div); + break; + case SDMMC_2: + car->clk_src[CLK_SOURCE_SDMMC2] = (CLK_SOURCE_FIRST | car_div); + break; + case SDMMC_3: + car->clk_src[CLK_SOURCE_SDMMC3] = (CLK_SOURCE_FIRST | car_div); + break; + case SDMMC_4: + car->clk_src[CLK_SOURCE_SDMMC4] = (CLK_SOURCE_FIRST | car_div); + break; + } + + return out_freq; +} + +/* Adjust the device clock source value. */ +static int sdmmc_clk_adjust_source(SdmmcControllerNum controller, uint32_t clk_source_val) +{ + uint32_t out_val = 0; + + if (sdmmc_clk_sources[controller].clk_source_val == clk_source_val) + out_val = sdmmc_clk_sources[controller].clk_div_val; + else + { + bool was_sdmmc_clk_enb = is_sdmmc_clk_enb(controller); + + /* Clock was already enabled. Disable it. */ + if (was_sdmmc_clk_enb) + sdmmc_clk_clear_enb(controller); + + out_val = sdmmc_clk_set_source(controller, clk_source_val); + + /* Clock was already enabled. Enable it back. */ + if (was_sdmmc_clk_enb) + sdmmc_clk_set_enb(controller); + + /* Dummy read for value refreshing. */ + is_sdmmc_clk_rst(controller); + } + + return out_val; +} + +/* Enable the SD clock if possible. */ +static void sdmmc_enable_sd_clock(sdmmc_t *sdmmc) +{ + if ((sdmmc->has_sd) && !(sdmmc->regs->clock_control & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + sdmmc->regs->clock_control |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + sdmmc->is_sd_clk_enabled = true; +} + +/* Disable the SD clock. */ +static void sdmmc_disable_sd_clock(sdmmc_t *sdmmc) +{ + sdmmc->is_sd_clk_enabled = false; + sdmmc->regs->clock_control &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; +} + +/* Automatically enable or disable the SD clock. */ +void sdmmc_adjust_sd_clock(sdmmc_t *sdmmc) +{ + if (!(sdmmc->has_sd) && (sdmmc->regs->clock_control & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + sdmmc_disable_sd_clock(sdmmc); + else if (sdmmc->is_sd_clk_enabled && !(sdmmc->regs->clock_control & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + sdmmc_enable_sd_clock(sdmmc); +} + +/* Return the clock control value. Used for dummy reads. */ +static int sdmmc_get_sd_clock_control(sdmmc_t *sdmmc) +{ + return sdmmc->regs->clock_control; +} + +/* Start the SDMMC clock. */ +static void sdmmc_clk_start(SdmmcControllerNum controller, uint32_t clk_source_val) +{ + /* Clock was already enabled. Disable it. */ + if (is_sdmmc_clk_enb(controller)) + sdmmc_clk_clear_enb(controller); + + /* Put the device clock in reset. */ + sdmmc_clk_set_rst(controller); + + /* Configure the device clock source. */ + uint32_t clk_div = sdmmc_clk_set_source(controller, clk_source_val); + + /* Enable the device clock. */ + sdmmc_clk_set_enb(controller); + + /* Dummy read for value refreshing. */ + is_sdmmc_clk_rst(controller); + + /* Synchronize. */ + udelay((100000 + clk_div - 1) / clk_div); + + /* Take the device clock out of reset. */ + sdmmc_clk_clear_rst(controller); + + /* Dummy read for value refreshing. */ + is_sdmmc_clk_rst(controller); +} + +/* Stop the SDMMC clock. */ +static void sdmmc_clk_stop(SdmmcControllerNum controller) +{ + /* Put the device clock in reset. */ + sdmmc_clk_set_rst(controller); + + /* Disable the device clock. */ + sdmmc_clk_clear_enb(controller); + + /* Dummy read for value refreshing. */ + is_sdmmc_clk_rst(controller); +} + +/* Configure clock trimming. */ +static void sdmmc_vendor_clock_cntrl_config(sdmmc_t *sdmmc) +{ + /* Clear the I/O conditioning constants. */ + sdmmc->regs->vendor_clock_cntrl &= ~(SDMMC_CLOCK_TRIM_MASK | SDMMC_CLOCK_TAP_MASK); + + /* Per the TRM, set the PADPIPE clock enable */ + sdmmc->regs->vendor_clock_cntrl |= SDMMC_CLOCK_PADPIPE_CLKEN_OVERRIDE; + + /* Set the appropriate trim value. */ + switch (sdmmc->controller) { + case SDMMC_1: + sdmmc->regs->vendor_clock_cntrl |= SDMMC_CLOCK_TRIM_SDMMC1; + break; + case SDMMC_2: + sdmmc->regs->vendor_clock_cntrl |= SDMMC_CLOCK_TRIM_SDMMC2; + break; + case SDMMC_3: + sdmmc->regs->vendor_clock_cntrl |= SDMMC_CLOCK_TRIM_SDMMC3; + break; + case SDMMC_4: + sdmmc->regs->vendor_clock_cntrl |= SDMMC_CLOCK_TRIM_SDMMC4; + break; + } +} + +/* Configure automatic calibration. */ +static int sdmmc_autocal_config(sdmmc_t *sdmmc, SdmmcBusVoltage voltage) +{ + switch (sdmmc->controller) { + case SDMMC_1: + case SDMMC_3: + switch (voltage) { + case SDMMC_VOLTAGE_1V8: + sdmmc->regs->auto_cal_config &= ~SDMMC_AUTOCAL_PDPU_CONFIG_MASK; + sdmmc->regs->auto_cal_config |= SDMMC_AUTOCAL_PDPU_SDMMC1_1V8; + break; + case SDMMC_VOLTAGE_3V3: + sdmmc->regs->auto_cal_config &= ~SDMMC_AUTOCAL_PDPU_CONFIG_MASK; + sdmmc->regs->auto_cal_config |= SDMMC_AUTOCAL_PDPU_SDMMC1_3V3; + break; + default: + sdmmc_error(sdmmc, "microsd does not support voltage %d", voltage); + return 0; + } + + break; + + case SDMMC_2: + case SDMMC_4: + if (voltage != SDMMC_VOLTAGE_1V8) { + sdmmc_error(sdmmc, "eMMC can only run at 1V8, but sdmmc struct claims voltage %d", voltage); + return 0; + } + + sdmmc->regs->auto_cal_config &= ~SDMMC_AUTOCAL_PDPU_CONFIG_MASK; + sdmmc->regs->auto_cal_config |= SDMMC_AUTOCAL_PDPU_SDMMC4_1V8; + break; + } + + return 1; +} + +/* Run automatic calibration. */ +static void sdmmc_autocal_run(sdmmc_t *sdmmc, SdmmcBusVoltage voltage) +{ + bool restart_sd_clock = false; + volatile tegra_padctl_t *padctl = padctl_get_regs(); + + /* SD clock is enabled. Disable it and restart later. */ + if (sdmmc->is_sd_clk_enabled) + { + restart_sd_clock = true; + sdmmc_disable_sd_clock(sdmmc); + } + + /* Set PAD_E_INPUT_OR_E_PWRD */ + if (!(sdmmc->regs->sdmemcomppadctrl & 0x80000000)) + { + sdmmc->regs->sdmemcomppadctrl |= 0x80000000; + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Delay. */ + udelay(1); + } + + /* Start automatic calibration. */ + sdmmc->regs->auto_cal_config |= (SDMMC_AUTOCAL_START | SDMMC_AUTOCAL_ENABLE); + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Delay. */ + udelay(1); + + /* Get current time. */ + uint32_t timebase = get_time(); + + /* Wait until the autocal is complete. */ + while ((sdmmc->regs->auto_cal_status & SDMMC_AUTOCAL_ACTIVE)) { + /* Ensure we haven't timed out. */ + if (get_time_since(timebase) > SDMMC_AUTOCAL_TIMEOUT) { + sdmmc_error(sdmmc, "autocal timed out!"); + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Upon timeout, fall back to standard values. */ + if (sdmmc->controller == SDMMC_1) { + uint32_t drvup = (voltage == SDMMC_VOLTAGE_3V3) ? 0x12 : 0x11; + uint32_t drvdn = (voltage == SDMMC_VOLTAGE_3V3) ? 0x12 : 0x15; + uint32_t value = padctl->sdmmc1_pad_cfgpadctrl; + value &= ~(SDMMC1_PAD_CAL_DRVUP_MASK | SDMMC1_PAD_CAL_DRVDN_MASK); + value |= (drvup << SDMMC1_PAD_CAL_DRVUP_SHIFT); + value |= (drvdn << SDMMC1_PAD_CAL_DRVDN_SHIFT); + padctl->sdmmc1_pad_cfgpadctrl = value; + } else if (sdmmc->controller == SDMMC_4) { + uint32_t value = padctl->emmc4_pad_cfgpadctrl; + value &= ~(CFG2TMC_EMMC4_PAD_DRVUP_COMP_MASK | CFG2TMC_EMMC4_PAD_DRVDN_COMP_MASK); + value |= (0x10 << CFG2TMC_EMMC4_PAD_DRVUP_COMP_SHIFT); + value |= (0x10 << CFG2TMC_EMMC4_PAD_DRVDN_COMP_SHIFT); + padctl->emmc4_pad_cfgpadctrl = value; + } + + /* Manually clear the autocal enable bit. */ + sdmmc->regs->auto_cal_config &= ~SDMMC_AUTOCAL_ENABLE; + break; + } + } + + /* Clear PAD_E_INPUT_OR_E_PWRD (relevant for eMMC only) */ + sdmmc->regs->sdmemcomppadctrl &= ~(0x80000000); + + /* If requested, enable the SD clock. */ + if (restart_sd_clock) + sdmmc_enable_sd_clock(sdmmc); +} + +static int sdmmc_int_clk_enable(sdmmc_t *sdmmc) +{ + /* Enable the internal clock. */ + sdmmc->regs->clock_control |= TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE; + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Program a timeout of 2000ms. */ + uint32_t timebase = get_time(); + bool is_timeout = false; + + /* Wait for the clock to stabilize. */ + while (!(sdmmc->regs->clock_control & TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE) && !is_timeout) { + /* Keep checking if timeout expired. */ + is_timeout = (get_time_since(timebase) > 2000000); + } + + /* Clock failed to stabilize. */ + if (is_timeout) { + sdmmc_error(sdmmc, "clock never stabilized!"); + return 0; + } + + /* Configure clock control and host control 2. */ + sdmmc->regs->host_control2 &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; + sdmmc->regs->clock_control &= ~TEGRA_MMC_CLKCON_PROG_CLOCK_MODE; + sdmmc->regs->host_control2 |= SDHCI_HOST_VERSION_4_EN; + + /* Ensure 64bit addressing is supported. */ + if (!(sdmmc->regs->capabilities & SDHCI_CAN_64BIT)) { + sdmmc_error(sdmmc, "64bit addressing is unsupported!"); + return 0; + } + + /* Enable 64bit addressing. */ + sdmmc->regs->host_control2 |= SDHCI_ADDRESSING_64BIT_EN; + + /* Use SDMA by default. */ + sdmmc->regs->host_control &= ~SDHCI_CTRL_DMA_MASK; + + /* Change to ADMA if requested. */ + if (sdmmc->use_adma && (sdmmc->regs->capabilities & SDHCI_CAN_DO_ADMA2)) { + 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. */ + sdmmc->regs->timeout_control &= 0xF0; + sdmmc->regs->timeout_control |= 0x0E; + + return 1; +} + +void sdmmc_select_bus_width(sdmmc_t *sdmmc, SdmmcBusWidth width) +{ + if (width == SDMMC_BUS_WIDTH_1BIT) + { + sdmmc->regs->host_control &= ~(SDHCI_CTRL_4BITBUS | SDHCI_CTRL_8BITBUS); + sdmmc->bus_width = SDMMC_BUS_WIDTH_1BIT; + } + else if (width == SDMMC_BUS_WIDTH_4BIT) + { + sdmmc->regs->host_control |= SDHCI_CTRL_4BITBUS; + sdmmc->regs->host_control &= ~SDHCI_CTRL_8BITBUS; + sdmmc->bus_width = SDMMC_BUS_WIDTH_4BIT; + } + else if (width == SDMMC_BUS_WIDTH_8BIT) + { + sdmmc->regs->host_control |= SDHCI_CTRL_8BITBUS; + sdmmc->bus_width = SDMMC_BUS_WIDTH_8BIT; + } + else + sdmmc_error(sdmmc, "Invalid bus width specified!"); +} + +void sdmmc_select_voltage(sdmmc_t *sdmmc, SdmmcBusVoltage voltage) +{ + if (voltage == SDMMC_VOLTAGE_NONE) + { + sdmmc->regs->power_control &= ~TEGRA_MMC_PWRCTL_SD_BUS_POWER; + sdmmc->bus_voltage = SDMMC_VOLTAGE_NONE; + } + else if (voltage == SDMMC_VOLTAGE_1V8) + { + sdmmc->regs->power_control |= TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8; + sdmmc->regs->power_control |= TEGRA_MMC_PWRCTL_SD_BUS_POWER; + sdmmc->bus_voltage = SDMMC_VOLTAGE_1V8; + } + else if (voltage == SDMMC_VOLTAGE_3V3) + { + sdmmc->regs->power_control |= TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3; + sdmmc->regs->power_control |= TEGRA_MMC_PWRCTL_SD_BUS_POWER; + sdmmc->bus_voltage = SDMMC_VOLTAGE_3V3; + } + else + sdmmc_error(sdmmc, "Invalid power state specified!"); +} + +static void sdmmc_tap_config(sdmmc_t *sdmmc, SdmmcBusSpeed bus_speed) +{ + if (bus_speed == SDMMC_SPEED_HS400) + { + /* Clear and set DQS_TRIM_VAL (used in HS400) */ + sdmmc->regs->vendor_cap_overrides &= ~(0x3F00); + sdmmc->regs->vendor_cap_overrides |= 0x2800; + } + + /* Clear TAP_VAL_UPDATED_BY_HW */ + sdmmc->regs->vendor_tuning_cntrl0 &= ~(0x20000); + + if (bus_speed == SDMMC_SPEED_HS400) + { + /* We must have obtained the tap value from the tuning procedure here. */ + if (sdmmc->is_tuning_tap_val_set) + { + /* Clear and set the tap value. */ + sdmmc->regs->vendor_clock_cntrl &= ~(0xFF0000); + sdmmc->regs->vendor_clock_cntrl |= (sdmmc->tap_val << 16); + } + } + else + { + /* Use the recommended values. */ + switch (sdmmc->controller) + { + case SDMMC_1: + sdmmc->tap_val = 4; + break; + case SDMMC_2: + case SDMMC_4: + sdmmc->tap_val = 0; + break; + case SDMMC_3: + sdmmc->tap_val = 3; + break; + } + + /* Clear and set the tap value. */ + sdmmc->regs->vendor_clock_cntrl &= ~(0xFF0000); + sdmmc->regs->vendor_clock_cntrl |= (sdmmc->tap_val << 16); + } +} + +static int sdmmc_dllcal_run(sdmmc_t *sdmmc) +{ + bool shutdown_sd_clock = false; + + /* SD clock is disabled. Enable it. */ + if (!sdmmc->is_sd_clk_enabled) + { + shutdown_sd_clock = true; + sdmmc_enable_sd_clock(sdmmc); + } + + /* Set the CALIBRATE bit. */ + sdmmc->regs->vendor_dllcal_cfg |= 0x80000000; + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Program a timeout of 5ms. */ + uint32_t timebase = get_time(); + bool is_timeout = false; + + /* Wait for CALIBRATE to be cleared. */ + while ((sdmmc->regs->vendor_dllcal_cfg & 0x80000000) && !is_timeout) { + /* Keep checking if timeout expired. */ + is_timeout = (get_time_since(timebase) > 5000); + } + + /* Calibration failed. */ + if (is_timeout) { + sdmmc_error(sdmmc, "DLLCAL failed!"); + return 0; + } + + /* Program a timeout of 10ms. */ + timebase = get_time(); + is_timeout = false; + + /* Wait for DLL_CAL_ACTIVE to be cleared. */ + while ((sdmmc->regs->vendor_dllcal_cfg_sta & 0x80000000) && !is_timeout) { + /* Keep checking if timeout expired. */ + is_timeout = (get_time_since(timebase) > 10000); + } + + /* Clock failed to stabilize. */ + if (is_timeout) { + sdmmc_error(sdmmc, "ERROR: DLLCAL failed!"); + return 0; + } + + /* If requested, disable the SD clock. */ + if (shutdown_sd_clock) + sdmmc_disable_sd_clock(sdmmc); + + return 1; +} + +int sdmmc_select_speed(sdmmc_t *sdmmc, SdmmcBusSpeed bus_speed) +{ + bool restart_sd_clock = false; + + /* SD clock is enabled. Disable it and restart later. */ + if (sdmmc->is_sd_clk_enabled) + { + restart_sd_clock = true; + sdmmc_disable_sd_clock(sdmmc); + } + + /* Configure tap values as necessary. */ + sdmmc_tap_config(sdmmc, bus_speed); + + /* Set the appropriate host speed. */ + switch (bus_speed) { + /* 400kHz initialization mode and a few others. */ + case SDMMC_SPEED_INIT_HS: + case SDMMC_SPEED_HS26: + case SDMMC_SPEED_INIT_SDR: + case SDMMC_SPEED_UNK6: + sdmmc->regs->host_control &= ~(SDHCI_CTRL_HISPD); + sdmmc->regs->host_control2 &= ~(SDHCI_CTRL_VDD_180); + break; + + /* 50MHz high speed (SD) and 52MHz high speed (MMC). */ + case SDMMC_SPEED_SDR25: + case SDMMC_SPEED_HS52: + sdmmc->regs->host_control |= SDHCI_CTRL_HISPD; + sdmmc->regs->host_control2 &= ~(SDHCI_CTRL_VDD_180); + break; + + /* 200MHz UHS-I (SD) and other modes due to errata. */ + case SDMMC_SPEED_HS200: + case SDMMC_SPEED_SDR104: + case SDMMC_SPEED_DDR50: + case SDMMC_SPEED_SDR50: + case SDMMC_SPEED_UNK14: + sdmmc->regs->host_control2 &= SDHCI_CTRL_UHS_MASK; + sdmmc->regs->host_control2 |= (SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_VDD_180); + break; + + /* 200MHz single-data rate (MMC). */ + case SDMMC_SPEED_HS400: + sdmmc->regs->host_control2 &= SDHCI_CTRL_UHS_MASK; + sdmmc->regs->host_control2 |= (SDHCI_CTRL_HS400 | SDHCI_CTRL_VDD_180); + break; + + /* 25MHz default speed (SD). */ + case SDMMC_SPEED_SDR12: + sdmmc->regs->host_control2 &= SDHCI_CTRL_UHS_MASK; + sdmmc->regs->host_control2 |= (SDHCI_CTRL_UHS_SDR12 | SDHCI_CTRL_VDD_180); + break; + + default: + sdmmc_error(sdmmc, "Switching to unsupported speed!"); + return 0; + } + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Get the clock's frequency and divider. */ + uint32_t freq_val = sdmmc_get_sdclk_freq(bus_speed); + uint32_t div_val = sdmmc_get_sdclk_div(bus_speed); + + /* Adjust the CAR side of the clock. */ + uint32_t out_freq_val = sdmmc_clk_adjust_source(sdmmc->controller, freq_val); + + /* Save the internal divider value. */ + sdmmc->internal_divider = ((out_freq_val + div_val - 1) / div_val); + + uint16_t div_val_lo = div_val >> 1; + uint16_t div_val_hi = 0; + + if (div_val_lo > 0xFF) + div_val_hi = (div_val_lo >> 8); + + /* Set the clock control divider values. */ + sdmmc->regs->clock_control &= ~((SDHCI_DIV_HI_MASK | SDHCI_DIV_MASK) << 6); + sdmmc->regs->clock_control |= ((div_val_hi << SDHCI_DIVIDER_HI_SHIFT) | (div_val_lo << SDHCI_DIVIDER_SHIFT)); + + /* If requested, enable the SD clock. */ + if (restart_sd_clock) + sdmmc_enable_sd_clock(sdmmc); + + /* Run DLLCAL for HS400 only */ + if (bus_speed == SDMMC_SPEED_HS400) + return sdmmc_dllcal_run(sdmmc); + + return 1; +} + +static int sdmmc1_config() +{ + volatile tegra_pinmux_t *pinmux = pinmux_get_regs(); + volatile tegra_padctl_t *padctl = padctl_get_regs(); + volatile tegra_pmc_t *pmc = pmc_get_regs(); + + /* Set up the card detect pin as a GPIO input */ + pinmux->pz1 = PINMUX_SELECT_FUNCTION1 | PINMUX_PULL_UP | PINMUX_INPUT; + padctl->vgpio_gpio_mux_sel = 0; + gpio_configure_mode(GPIO_MICROSD_CARD_DETECT, GPIO_MODE_GPIO); + gpio_configure_direction(GPIO_MICROSD_CARD_DETECT, GPIO_DIRECTION_INPUT); + udelay(100); + + /* Check the GPIO. */ + if (gpio_read(GPIO_MICROSD_CARD_DETECT)) + return 0; + + padctl->sdmmc1_clk_lpbk_control = 1; + + /* Set up the SDMMC1 pinmux. */ + pinmux->sdmmc1_clk = PINMUX_DRIVE_2X | PINMUX_PARKED | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT; + pinmux->sdmmc1_cmd = PINMUX_DRIVE_2X | PINMUX_PARKED | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT | PINMUX_PULL_UP; + pinmux->sdmmc1_dat3 = PINMUX_DRIVE_2X | PINMUX_PARKED | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT | PINMUX_PULL_UP; + pinmux->sdmmc1_dat2 = PINMUX_DRIVE_2X | PINMUX_PARKED | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT | PINMUX_PULL_UP; + pinmux->sdmmc1_dat1 = PINMUX_DRIVE_2X | PINMUX_PARKED | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT | PINMUX_PULL_UP; + pinmux->sdmmc1_dat0 = PINMUX_DRIVE_2X | PINMUX_PARKED | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT | PINMUX_PULL_UP; + + /* Ensure the PMC is prepared for the SDMMC1 card to receive power. */ + pmc->no_iopower &= ~PMC_CONTROL_SDMMC1; + pmc->pwr_det_val |= PMC_CONTROL_SDMMC1; + + /* Configure the enable line for the SD card power. */ + pinmux->dmic3_clk = PINMUX_SELECT_FUNCTION1 | PINMUX_PULL_DOWN | PINMUX_INPUT; + gpio_configure_mode(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_MODE_GPIO); + gpio_write(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_LEVEL_HIGH); + gpio_configure_direction(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_DIRECTION_OUTPUT); + + udelay(1000); + + /* Set up SD card voltages. */ + max77620_regulator_set_voltage(REGULATOR_LDO2, 3300000); + max77620_regulator_enable(REGULATOR_LDO2, 1); + + udelay(1000); + + padctl->sdmmc1_pad_cfgpadctrl = 0x10000000; + + udelay(1000); + + return 1; +} + +static int sdmmc2_config() +{ + return 1; +} + +static int sdmmc3_config() +{ + return 1; +} + +static int sdmmc4_config() +{ + return 1; +} + +static int sdmmc_init_controller(sdmmc_t *sdmmc, SdmmcControllerNum controller) +{ + /* Sanitize input number for the controller. */ + if ((controller < SDMMC_1) || (controller > SDMMC_4)) + return 0; + + /* Clear up memory for our struct. */ + memset(sdmmc, 0, sizeof(sdmmc_t)); + + /* Bind the appropriate controller and it's register space to our struct. */ + sdmmc->controller = controller; + sdmmc->regs = sdmmc_get_regs(controller); + + /* Set up per-device pointers and properties. */ + switch (sdmmc->controller) { + case SDMMC_1: + /* Controller properties. */ + sdmmc->name = "uSD"; + sdmmc->has_sd = true; + sdmmc->is_clk_running = false; + sdmmc->is_sd_clk_enabled = false; + sdmmc->is_tuning_tap_val_set = false; + sdmmc->use_adma = false; + sdmmc->dma_bounce_buf = (uint8_t*)SDMMC_BOUNCE_BUFFER_ADDRESS; + sdmmc->tap_val = 0; + sdmmc->internal_divider = 0; + sdmmc->bus_voltage = SDMMC_VOLTAGE_NONE; + + /* Function pointers. */ + sdmmc->sdmmc_config = sdmmc1_config; + break; + + case SDMMC_2: + /* Controller properties. */ + sdmmc->name = "GC"; + sdmmc->has_sd = true; + sdmmc->is_clk_running = false; + sdmmc->is_sd_clk_enabled = false; + sdmmc->is_tuning_tap_val_set = false; + sdmmc->use_adma = false; + sdmmc->dma_bounce_buf = (uint8_t*)SDMMC_BOUNCE_BUFFER_ADDRESS; + sdmmc->tap_val = 0; + sdmmc->internal_divider = 0; + sdmmc->bus_voltage = SDMMC_VOLTAGE_NONE; + + /* Function pointers. */ + sdmmc->sdmmc_config = sdmmc2_config; + break; + + case SDMMC_3: + /* Controller properties. */ + sdmmc->name = "UNUSED"; + sdmmc->has_sd = true; + sdmmc->is_clk_running = false; + sdmmc->is_sd_clk_enabled = false; + sdmmc->is_tuning_tap_val_set = false; + sdmmc->use_adma = false; + sdmmc->dma_bounce_buf = (uint8_t*)SDMMC_BOUNCE_BUFFER_ADDRESS; + sdmmc->tap_val = 0; + sdmmc->internal_divider = 0; + sdmmc->bus_voltage = SDMMC_VOLTAGE_NONE; + + /* Function pointers. */ + sdmmc->sdmmc_config = sdmmc3_config; + break; + + case SDMMC_4: + /* Controller properties. */ + sdmmc->name = "eMMC"; + sdmmc->has_sd = true; + sdmmc->is_clk_running = false; + sdmmc->is_sd_clk_enabled = false; + sdmmc->is_tuning_tap_val_set = false; + sdmmc->use_adma = false; + sdmmc->dma_bounce_buf = (uint8_t*)SDMMC_BOUNCE_BUFFER_ADDRESS; + sdmmc->tap_val = 0; + sdmmc->internal_divider = 0; + sdmmc->bus_voltage = SDMMC_VOLTAGE_NONE; + + /* Function pointers. */ + sdmmc->sdmmc_config = sdmmc4_config; + break; + } + + return 1; +} + +int sdmmc_init(sdmmc_t *sdmmc, SdmmcControllerNum controller, SdmmcBusVoltage bus_voltage, SdmmcBusWidth bus_width, SdmmcBusSpeed bus_speed) +{ + /* Initialize our controller structure. */ + if (!sdmmc_init_controller(sdmmc, controller)) { + sdmmc_error(sdmmc, "Failed to initialize SDMMC%d", controller + 1); + return 0; + } + + /* Perform initial configuration steps if necessary. */ + if (!sdmmc->sdmmc_config()) { + sdmmc_error(sdmmc, "Failed to configure controller!"); + return 0; + } + + /* Initialize the clock status. */ + sdmmc->is_clk_running = false; + + /* Clock is enabled and out of reset. Shouldn't happen. */ + if (!is_sdmmc_clk_rst(controller) && is_sdmmc_clk_enb(controller)) { + /* Disable the SD clock. */ + sdmmc_disable_sd_clock(sdmmc); + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + } + + /* Sort out the clock's frequency. */ + uint32_t clk_freq_val = sdmmc_get_sdclk_freq(bus_speed); + + /* Start the SDMMC clock. */ + sdmmc_clk_start(controller, clk_freq_val); + + /* Update the clock status. */ + sdmmc->is_clk_running = true; + + // Set IO_SPARE[19] (one cycle delay) + sdmmc->regs->io_spare |= 0x80000; + + // Clear SEL_VREG + sdmmc->regs->vendor_io_trim_cntrl &= ~(0x04); + + /* Configure vendor clocking. */ + sdmmc_vendor_clock_cntrl_config(sdmmc); + + // Set SDMMC2TMC_CFG_SDMEMCOMP_VREF_SEL to 0x07 + sdmmc->regs->sdmemcomppadctrl &= 0x0F; + sdmmc->regs->sdmemcomppadctrl |= 0x07; + + /* Configure autocal offsets. */ + if (!sdmmc_autocal_config(sdmmc, bus_voltage)) { + sdmmc_error(sdmmc, "Failed to configure automatic calibration!"); + return 0; + } + + /* Do autocal. */ + sdmmc_autocal_run(sdmmc, bus_voltage); + + /* Enable the internal clock. */ + if (!sdmmc_int_clk_enable(sdmmc)) { + sdmmc_error(sdmmc, "Failed to enable the internal clock!"); + return 0; + } + + /* Select the desired bus width. */ + sdmmc_select_bus_width(sdmmc, bus_width); + + /* Select the desired voltage. */ + sdmmc_select_voltage(sdmmc, bus_voltage); + + /* Enable the internal clock. */ + if (!sdmmc_select_speed(sdmmc, bus_speed)) { + sdmmc_error(sdmmc, "Failed to apply the correct bus speed!"); + return 0; + } + + /* Correct any inconsistent states. */ + sdmmc_adjust_sd_clock(sdmmc); + + /* Enable the SD clock. */ + sdmmc_enable_sd_clock(sdmmc); + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + return 1; +} + +void sdmmc_finish(sdmmc_t *sdmmc) +{ + /* Stop everything. */ + if (sdmmc->is_clk_running) + { + /* Disable the SD clock. */ + sdmmc_disable_sd_clock(sdmmc); + + /* Disable SD power. */ + sdmmc_select_voltage(sdmmc, SDMMC_VOLTAGE_NONE); + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Stop the SD clock. */ + sdmmc_clk_stop(sdmmc->controller); + + /* Clock is no longer running by now. */ + sdmmc->is_clk_running = false; + } +} + +static void sdmmc_do_sw_reset(sdmmc_t *sdmmc) +{ + /* Assert a software reset. */ + sdmmc->regs->software_reset |= (TEGRA_MMC_SWRST_SW_RESET_FOR_CMD_LINE | TEGRA_MMC_SWRST_SW_RESET_FOR_DAT_LINE); + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Program a timeout of 100ms. */ + uint32_t timebase = get_time(); + bool is_timeout = false; + + /* Wait for the register to be cleared. */ + while ((sdmmc->regs->software_reset & (TEGRA_MMC_SWRST_SW_RESET_FOR_CMD_LINE | TEGRA_MMC_SWRST_SW_RESET_FOR_DAT_LINE)) && !is_timeout) { + /* Keep checking if timeout expired. */ + is_timeout = (get_time_since(timebase) > 100000); + } +} + +static int sdmmc_wait_for_inhibit(sdmmc_t *sdmmc, bool wait_for_dat) +{ + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Program a timeout of 10ms. */ + uint32_t timebase = get_time(); + bool is_timeout = false; + + /* Wait on CMD inhibit to be cleared. */ + while ((sdmmc->regs->present_state & TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD) && !is_timeout) { + /* Keep checking if timeout expired. */ + is_timeout = (get_time_since(timebase) > 10000); + } + + /* Bit was never released. Reset. */ + if (is_timeout) + { + sdmmc_do_sw_reset(sdmmc); + return 0; + } + + if (wait_for_dat) + { + /* Program a timeout of 10ms. */ + timebase = get_time(); + is_timeout = false; + + /* Wait on DAT inhibit to be cleared. */ + while ((sdmmc->regs->present_state & TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT) && !is_timeout) { + /* Keep checking if timeout expired. */ + is_timeout = (get_time_since(timebase) > 10000); + } + + /* Bit was never released, reset. */ + if (is_timeout) + { + sdmmc_do_sw_reset(sdmmc); + return 0; + } + } + + return 1; +} + +static int sdmmc_wait_busy(sdmmc_t *sdmmc) +{ + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Program a timeout of 10ms. */ + uint32_t timebase = get_time(); + bool is_timeout = false; + + /* Wait on DAT0 level mask to be set. */ + while (!(sdmmc->regs->present_state & SDHCI_DATA_0_LVL_MASK) && !is_timeout) { + /* Keep checking if timeout expired. */ + is_timeout = (get_time_since(timebase) > 10000); + } + + /* Bit was never released. Reset. */ + if (is_timeout) + { + sdmmc_do_sw_reset(sdmmc); + return 0; + } + + return 1; +} + +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)); + + /* Refresh status. */ + sdmmc->regs->int_status = sdmmc->regs->int_status; +} + +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)); +} + +static bool sdmmc_intr_check_status(sdmmc_t *sdmmc, u16 status_mask) +{ + bool is_masked = (sdmmc->regs->int_status & status_mask); + + /* Mask status. */ + if (is_masked) + sdmmc->regs->int_status &= status_mask; + + return is_masked; +} + +static bool sdmmc_intr_check_error(sdmmc_t *sdmmc) +{ + bool is_error = (sdmmc->regs->int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT); + + /* Refresh status. */ + if (is_error) + sdmmc->regs->int_status = sdmmc->regs->int_status; + + return is_error; +} + +static int sdmmc_dma_init(sdmmc_t *sdmmc, sdmmc_request_t *req) +{ + /* Invalid block count or size. */ + if (!req->blksz || !req->num_blocks) + return 0; + + uint32_t blkcnt = req->num_blocks; + + /* Truncate block count. Length can't be over 65536 bytes. */ + if (blkcnt >= 0xFFFF) + blkcnt = 0xFFFF; + + /* Point to our bounce buffer. */ + uint32_t dma_base_addr = (uint32_t)sdmmc->dma_bounce_buf; + + /* DMA buffer address must be aligned to 4 bytes. */ + if ((4 - (dma_base_addr & 0x03)) & 0x03) + return 0; + + /* Write our address to the registers. */ + if (sdmmc->use_adma) + { + /* Set ADMA registers. */ + sdmmc->regs->adma_address = dma_base_addr; + sdmmc->regs->upper_adma_address = 0; + } + else + { + /* Set SDMA register. */ + sdmmc->regs->dma_address = dma_base_addr; + } + + /* Store the next DMA block address for updating. */ + sdmmc->next_dma_addr = ((dma_base_addr + 0x80000) & 0xFFF80000); + + /* Set the block size ORed with the DMA boundary mask. */ + sdmmc->regs->block_size = req->blksz | 0x7000; + + /* Set the block count. */ + sdmmc->regs->block_count = blkcnt; + + /* Select basic DMA transfer mode. */ + uint32_t transfer_mode = TEGRA_MMC_TRNMOD_DMA_ENABLE; + + /* Select multi block. */ + if (req->is_multi_block) + transfer_mode |= (TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT | TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE); + + /* Select read mode. */ + if (req->is_read) + transfer_mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; + + /* Select AUTO_CMD12. */ + if (req->is_auto_cmd12) + { + transfer_mode &= ~(TEGRA_MMC_TRNMOD_AUTO_CMD12 & TEGRA_MMC_TRNMOD_AUTO_CMD23); + transfer_mode |= TEGRA_MMC_TRNMOD_AUTO_CMD12; + } + + /* Set the transfer mode in the register. */ + sdmmc->regs->transfer_mode = transfer_mode; + + return blkcnt; +} + +static int sdmmc_dma_update(sdmmc_t *sdmmc) +{ + u16 blkcnt = 0; + + /* Loop until all blocks have been consumed. */ + do + { + /* Update block count. */ + blkcnt = sdmmc->regs->block_count; + + /* Program a large timeout. */ + uint32_t timebase = get_time(); + bool is_timeout = false; + + /* Watch over the DMA transfer. */ + while (!is_timeout) + { + /* An error has been raised. Reset. */ + if (sdmmc_intr_check_error(sdmmc)) + { + sdmmc_do_sw_reset(sdmmc); + return 0; + } + + /* We have a DMA interrupt. Restart the transfer where it was interrupted. */ + if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_DMA_INTERRUPT)) + { + if (sdmmc->use_adma) + { + /* Update ADMA registers. */ + sdmmc->regs->adma_address = sdmmc->next_dma_addr; + sdmmc->regs->upper_adma_address = 0; + } + else + { + /* Update SDMA register. */ + sdmmc->regs->dma_address = sdmmc->next_dma_addr; + } + + 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. */ + is_timeout = (get_time_since(timebase) > 2000000); + } + } while (sdmmc->regs->block_count < blkcnt); + + /* Should never get here. Reset. */ + sdmmc_do_sw_reset(sdmmc); + return 0; +} + +static void sdmmc_set_cmd_flags(sdmmc_t *sdmmc, sdmmc_command_t *cmd, bool is_dma) +{ + u16 cmd_reg_flags = 0; + + /* Select length flags based on response type. */ + if (!(cmd->flags & SDMMC_RSP_PRESENT)) + cmd_reg_flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE; + else if (cmd->flags & SDMMC_RSP_136) + cmd_reg_flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136; + else if (cmd->flags & SDMMC_RSP_BUSY) + cmd_reg_flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY; + else + cmd_reg_flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48; + + /* Select CRC flag based on response type. */ + if (cmd->flags & SDMMC_RSP_CRC) + cmd_reg_flags |= TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; + + /* Select opcode flag based on response type. */ + if (cmd->flags & SDMMC_RSP_OPCODE) + cmd_reg_flags |= TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK; + + /* Select data present flag. */ + if (is_dma) + cmd_reg_flags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER; + + /* Set the CMD's argument, opcode and flags. */ + sdmmc->regs->argument = cmd->arg; + sdmmc->regs->command = ((cmd->opcode << 8) | cmd_reg_flags); +} + +static int sdmmc_wait_for_cmd(sdmmc_t *sdmmc) +{ + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Program a large timeout. */ + uint32_t timebase = get_time(); + bool is_timeout = false; + + /* Set this for error checking. */ + bool is_err = false; + + /* Wait for CMD to finish. */ + while (!is_err && !is_timeout) { + /* Command is done. */ + if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_CMD_COMPLETE)) + return 1; + + /* Check for any raised errors. */ + is_err = sdmmc_intr_check_error(sdmmc); + + /* Keep checking if timeout expired. */ + is_timeout = (get_time_since(timebase) > 2000000); + } + + /* Should never get here. Reset. */ + sdmmc_do_sw_reset(sdmmc); + return 0; +} + +static int sdmmc_save_response(sdmmc_t *sdmmc, uint32_t flags) +{ + /* We have a valid response. */ + if (flags & SDMMC_RSP_PRESENT) + { + if (flags & SDMMC_RSP_136) + { + /* CRC is stripped so we need to do some shifting. */ + for (int i = 0; i < 4; i++) { + sdmmc->resp[i] = (sdmmc->regs->response[3 - i] << 0x08); + + if (i != 0) + sdmmc->resp[i - 1] |= ((sdmmc->regs->response[3 - i] >> 24) & 0xFF); + } + } + else + { + /* Card is still busy. */ + if (flags & SDMMC_RSP_BUSY) + { + /* Wait for DAT0 level mask. */ + if (!sdmmc_wait_busy(sdmmc)) + return 0; + } + + /* Save our response. */ + sdmmc->resp[0] = sdmmc->regs->response[0]; + } + + return 1; + } + + /* Invalid response. */ + return 0; +} + +int sdmmc_load_response(sdmmc_t *sdmmc, uint32_t flags, uint32_t *resp) +{ + /* Make sure our output buffer is valid. */ + if (!resp) + return 0; + + /* We have a valid response. */ + if (flags & SDMMC_RSP_PRESENT) + { + if (flags & SDMMC_RSP_136) + { + resp[0] = sdmmc->resp[0]; + resp[1] = sdmmc->resp[1]; + resp[2] = sdmmc->resp[2]; + resp[3] = sdmmc->resp[3]; + } + else + resp[0] = sdmmc->resp[0]; + + return 1; + } + + /* Invalid response. */ + return 0; +} + +int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, uint32_t *num_blocks_out) +{ + uint32_t cmd_result = 0; + bool shutdown_sd_clock = false; + + /* Run automatic calibration on each command submission for SDMMC1. */ + if ((sdmmc->controller == SDMMC_1) && !(sdmmc->has_sd)) + sdmmc_autocal_run(sdmmc, sdmmc->bus_voltage); + + /* SD clock is disabled. Enable it. */ + if (!sdmmc->is_sd_clk_enabled) + { + shutdown_sd_clock = true; + sdmmc_enable_sd_clock(sdmmc); + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Provide 8 clock cycles after enabling the clock. */ + udelay((8000 + sdmmc->internal_divider - 1) / sdmmc->internal_divider); + } + + /* Determine if we should wait for data inhibit. */ + bool wait_for_dat = (req || (cmd->flags & SDMMC_RSP_BUSY)); + + /* Wait for CMD and DAT inhibit. */ + if (!sdmmc_wait_for_inhibit(sdmmc, wait_for_dat)) + return 0; + + uint32_t dma_blkcnt = 0; + bool is_dma = false; + + /* This is a data transfer. */ + if (req) + { + is_dma = true; + dma_blkcnt = sdmmc_dma_init(sdmmc, req); + + if (!dma_blkcnt) + { + sdmmc_error(sdmmc, "Failed to initialize the DMA transfer!"); + return 0; + } + + /* If this is a write operation, copy the data into our bounce buffer. */ + if (!req->is_read) + memcpy((void *)sdmmc->dma_bounce_buf, (void *)req->data, req->blksz * req->num_blocks); + } + + /* Enable interrupts. */ + sdmmc_intr_enable(sdmmc); + + /* Parse and set the CMD's flags. */ + sdmmc_set_cmd_flags(sdmmc, cmd, is_dma); + + /* Wait for the CMD to finish. */ + cmd_result = sdmmc_wait_for_cmd(sdmmc); + + sdmmc_debug(sdmmc, "CMD(%d): %08X, %08X, %08X, %08X", cmd_result, sdmmc->regs->response[0], sdmmc->regs->response[1], sdmmc->regs->response[2], sdmmc->regs->response[3]); + + if (cmd_result) + { + /* Save response, if necessary. */ + sdmmc_save_response(sdmmc, cmd->flags); + + /* Process the DMA request. */ + if (req) + { + if (!sdmmc_dma_update(sdmmc)) + { + sdmmc_error(sdmmc, "Failed to process the DMA transfer!"); + return 0; + } + + /* If this is a read operation, copy the data from our bounce buffer. */ + if (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); + } + } + } + + /* Disable interrupts. */ + sdmmc_intr_disable(sdmmc); + + if (cmd_result) + { + if (req) + { + /* Save back the number of DMA blocks. */ + if (num_blocks_out) + *num_blocks_out = dma_blkcnt; + + /* Save the response for AUTO_CMD12. */ + if (req->is_auto_cmd12) + sdmmc->resp_auto_cmd12 = sdmmc->regs->response[3]; + } + + /* Wait for DAT0 to be 0. */ + if (req || (cmd->flags & SDMMC_RSP_BUSY)) + cmd_result = sdmmc_wait_busy(sdmmc); + } + + /* Provide 8 clock cycles before disabling the clock. */ + udelay((8000 + sdmmc->internal_divider - 1) / sdmmc->internal_divider); + + if (shutdown_sd_clock) + sdmmc_disable_sd_clock(sdmmc); + + return cmd_result; +} + +int sdmmc_switch_voltage(sdmmc_t *sdmmc) +{ + volatile tegra_pmc_t *pmc = pmc_get_regs(); + + /* Reconfigure the internal clock. */ + if (!sdmmc_select_speed(sdmmc, SDMMC_SPEED_SDR12)) + { + sdmmc_error(sdmmc, "Failed to apply the correct bus speed for low voltage support!"); + return 0; + } + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Reconfigure the regulator. */ + max77620_regulator_set_voltage(REGULATOR_LDO2, 1800000); + pmc->pwr_det_val &= ~(PMC_CONTROL_SDMMC1); + + /* Reconfigure autocal offsets. */ + if (!sdmmc_autocal_config(sdmmc, SDMMC_VOLTAGE_1V8)) + { + sdmmc_error(sdmmc, "Failed to configure automatic calibration for low voltage support!"); + return 0; + } + + /* Do autocal again. */ + sdmmc_autocal_run(sdmmc, SDMMC_VOLTAGE_1V8); + + /* Change the desired voltage. */ + sdmmc_select_voltage(sdmmc, SDMMC_VOLTAGE_1V8); + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Wait a while. */ + udelay(5000); + + /* Host control 2 flag should be set by now. */ + if (sdmmc->regs->host_control2 & SDHCI_CTRL_VDD_180) + { + /* Enable the SD clock. */ + sdmmc_enable_sd_clock(sdmmc); + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Wait a while. */ + udelay(1000); + + /* Data level is up. Voltage switching is done.*/ + if (sdmmc->regs->present_state & SDHCI_DATA_LVL_MASK) + return 1; + } + + return 0; +} + +static int sdmmc_send_tuning(sdmmc_t *sdmmc, uint32_t opcode) +{ + /* Nothing to do. */ + if (!sdmmc->has_sd) + return 0; + + /* Wait for CMD and DAT inhibit. */ + if (!sdmmc_wait_for_inhibit(sdmmc, true)) + return 0; + + /* Select the right size for sending the tuning block. */ + if (sdmmc->bus_width == SDMMC_BUS_WIDTH_4BIT) + sdmmc->regs->block_size = 0x40; + else if (sdmmc->bus_width == SDMMC_BUS_WIDTH_8BIT) + sdmmc->regs->block_size = 0x80; + else + return 0; + + /* Select the block count and transfer mode. */ + sdmmc->regs->block_count = 1; + sdmmc->regs->transfer_mode = TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; + + /* Manually enable the Buffer Read Ready interrupt. */ + sdmmc->regs->int_enable |= TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY; + + /* Refresh status. */ + sdmmc->regs->int_status = sdmmc->regs->int_status; + + /* Disable the SD clock. */ + sdmmc_disable_sd_clock(sdmmc); + + /* Prepare the tuning command. */ + sdmmc_command_t cmd = {}; + cmd.opcode = opcode; + cmd.arg = 0; + cmd.flags = SDMMC_RSP_R1; + + /* Parse and set the CMD's flags. */ + sdmmc_set_cmd_flags(sdmmc, &cmd, true); + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Wait a while. */ + udelay(1); + + /* Reset. */ + sdmmc_do_sw_reset(sdmmc); + + /* Enable back the SD clock. */ + sdmmc_enable_sd_clock(sdmmc); + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Program a 50ms timeout. */ + uint32_t timebase = get_time(); + bool is_timeout = false; + + /* Wait for Buffer Read Ready interrupt. */ + while (!is_timeout) + { + /* Buffer Read Ready was asserted. */ + if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY)) + { + /* Manually disable the Buffer Read Ready interrupt. */ + sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY); + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Provide 8 clock cycles. */ + udelay((8000 + sdmmc->internal_divider - 1) / sdmmc->internal_divider); + + return 1; + } + + /* Keep checking if timeout expired. */ + is_timeout = (get_time_since(timebase) > 5000); + } + + /* Reset. */ + sdmmc_do_sw_reset(sdmmc); + + /* Manually disable the Buffer Read Ready interrupt. */ + sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY); + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Provide 8 clock cycles. */ + udelay((8000 + sdmmc->internal_divider - 1) / sdmmc->internal_divider); + + return 0; +} + +void sdmmc_set_tuning_tap_val(sdmmc_t *sdmmc) +{ + sdmmc->tap_val = ((sdmmc->regs->vendor_clock_cntrl & 0xFF0000) >> 16); + sdmmc->is_tuning_tap_val_set = true; +} + +int sdmmc_execute_tuning(sdmmc_t *sdmmc, SdmmcBusSpeed bus_speed, uint32_t opcode) +{ + uint32_t max_tuning_loop = 0; + uint32_t tuning_cntrl_flag = 0; + + sdmmc->regs->vendor_tuning_cntrl1 = 0; + + switch (bus_speed) + { + case SDMMC_SPEED_HS200: + case SDMMC_SPEED_HS400: + case SDMMC_SPEED_SDR104: + max_tuning_loop = 0x80; + tuning_cntrl_flag = 0x4000; + break; + case SDMMC_SPEED_SDR50: + case SDMMC_SPEED_DDR50: + case SDMMC_SPEED_UNK14: + max_tuning_loop = 0x100; + tuning_cntrl_flag = 0x8000; + break; + default: + return 0; + } + + sdmmc->regs->vendor_tuning_cntrl0 &= ~(0xE000); + sdmmc->regs->vendor_tuning_cntrl0 |= tuning_cntrl_flag; + + sdmmc->regs->vendor_tuning_cntrl0 &= ~(0x1FC0); + sdmmc->regs->vendor_tuning_cntrl0 |= 0x40; + + sdmmc->regs->vendor_tuning_cntrl0 |= 0x20000; + + /* Start tuning. */ + sdmmc->regs->host_control2 |= SDHCI_CTRL_EXEC_TUNING; + + /* Repeat until Execute Tuning is set to 0 or the number of loops reaches the maximum value. */ + for (uint32_t i = 0; i < max_tuning_loop; i++) + { + sdmmc_send_tuning(sdmmc, opcode); + + /* Tuning is done. */ + if (!(sdmmc->regs->host_control2 & SDHCI_CTRL_EXEC_TUNING)) + break; + } + + /* Success! */ + if (sdmmc->regs->host_control2 & SDHCI_CTRL_TUNED_CLK) + return 1; + + return 0; +} + +int sdmmc_abort(sdmmc_t *sdmmc, uint32_t opcode) +{ + uint32_t result = 0; + uint32_t cmd_result = 0; + bool shutdown_sd_clock = false; + + /* SD clock is disabled. Enable it. */ + if (!sdmmc->is_sd_clk_enabled) + { + shutdown_sd_clock = true; + sdmmc_enable_sd_clock(sdmmc); + + /* Force a register read to refresh the clock control value. */ + sdmmc_get_sd_clock_control(sdmmc); + + /* Provide 8 clock cycles after enabling the clock. */ + udelay((8000 + sdmmc->internal_divider - 1) / sdmmc->internal_divider); + } + + /* Wait for CMD and DAT inhibit. */ + if (sdmmc_wait_for_inhibit(sdmmc, false)) + { + /* Enable interrupts. */ + sdmmc_intr_enable(sdmmc); + + /* Prepare the command. */ + sdmmc_command_t cmd = {}; + cmd.opcode = opcode; + cmd.arg = 0; + cmd.flags = SDMMC_RSP_R1B; + + /* Parse and set the CMD's flags. */ + sdmmc_set_cmd_flags(sdmmc, &cmd, false); + + /* Wait for the CMD to finish. */ + cmd_result = sdmmc_wait_for_cmd(sdmmc); + + /* Disable interrupts. */ + sdmmc_intr_disable(sdmmc); + + if (cmd_result) + { + /* Save response, if necessary. */ + sdmmc_save_response(sdmmc, cmd.flags); + + /* Wait for DAT0 to be 0. */ + result = sdmmc_wait_busy(sdmmc); + } + } + + /* Provide 8 clock cycles before disabling the clock. */ + udelay((8000 + sdmmc->internal_divider - 1) / sdmmc->internal_divider); + + /* Disable the SD clock if requested. */ + if (shutdown_sd_clock) + sdmmc_disable_sd_clock(sdmmc); + + return result; +} \ No newline at end of file diff --git a/fusee/fusee-primary/src/sdmmc/sdmmc_core.h b/fusee/fusee-primary/src/sdmmc/sdmmc_core.h new file mode 100644 index 000000000..038804ffd --- /dev/null +++ b/fusee/fusee-primary/src/sdmmc/sdmmc_core.h @@ -0,0 +1,287 @@ +#ifndef FUSEE_SDMMC_CORE_H +#define FUSEE_SDMMC_CORE_H + +#include "sdmmc_tegra.h" + +/* 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 (0x02 << 24) +#define SDMMC_CLOCK_TRIM_SDMMC2 (0x08 << 24) +#define SDMMC_CLOCK_TRIM_SDMMC3 (0x03 << 24) +#define SDMMC_CLOCK_TRIM_SDMMC4 (0x08 << 24) +#define SDMMC_CLOCK_PADPIPE_CLKEN_OVERRIDE (1 << 3) + +/* Autocal configuration */ +#define SDMMC_AUTOCAL_PDPU_CONFIG_MASK 0x7F7F +#define SDMMC_AUTOCAL_PDPU_SDMMC1_1V8 0x7B7B +#define SDMMC_AUTOCAL_PDPU_SDMMC1_3V3 0x7D00 +#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) + +/* Internal logging */ +typedef enum { + SDMMC_LOG_NONE = 0, + SDMMC_LOG_ERROR = 1, + SDMMC_LOG_WARN = 2, + SDMMC_LOG_INFO = 3, + SDMMC_LOG_DEBUG = 4 +} SdmmcLogLevel; + +/* SDMMC controllers */ +typedef enum { + SDMMC_1 = 0, + SDMMC_2 = 1, + SDMMC_3 = 2, + SDMMC_4 = 3 +} SdmmcControllerNum; + +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_INIT_HS = 0, + SDMMC_SPEED_HS26 = 1, + SDMMC_SPEED_HS52 = 2, + SDMMC_SPEED_HS200 = 3, + SDMMC_SPEED_HS400 = 4, + SDMMC_SPEED_INIT_SDR = 5, + SDMMC_SPEED_UNK6 = 6, + SDMMC_SPEED_SDR25 = 7, + SDMMC_SPEED_SDR12 = 8, + SDMMC_SPEED_UNK9 = 9, + SDMMC_SPEED_SDR50 = 10, + SDMMC_SPEED_SDR104 = 11, + SDMMC_SPEED_UNK12 = 12, + SDMMC_SPEED_DDR50 = 13, + SDMMC_SPEED_UNK14 = 14, +} SdmmcBusSpeed; + +typedef enum { + SDMMC_CAR_DIVIDER_SDR12 = 31, // (16.5 * 2) - 2 + SDMMC_CAR_DIVIDER_SDR25 = 15, // (8.5 * 2) - 2 + SDMMC_CAR_DIVIDER_SDR50 = 7, // (4.5 * 2) - 2 + SDMMC_CAR_DIVIDER_SDR104 = 2, // (2 * 2) - 2 + SDMMC_CAR_DIVIDER_DDR50 = 18, // (5 * 2 * 2) - 2 + SDMMC_CAR_DIVIDER_HS26 = 30, // (16 * 2) - 2 + SDMMC_CAR_DIVIDER_HS52 = 14, // (8 * 2) - 2 + SDMMC_CAR_DIVIDER_HS200 = 3, // 1 -- NOTE THIS IS WITH RESPECT TO PLLC4_OUT2_LJ + SDMMC_CAR_DIVIDER_HS400 = 3, // 1 -- NOTE THIS IS WITH RESPECT TO PLLC4_OUT2_LJ +} 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_set_log_level(SdmmcLogLevel log_level); +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 \ No newline at end of file diff --git a/fusee/fusee-primary/src/sdmmc/sdmmc_tegra.h b/fusee/fusee-primary/src/sdmmc/sdmmc_tegra.h new file mode 100644 index 000000000..7a6c141f7 --- /dev/null +++ b/fusee/fusee-primary/src/sdmmc/sdmmc_tegra.h @@ -0,0 +1,154 @@ +#ifndef FUSEE_SDMMC_TEGRA_H +#define FUSEE_SDMMC_TEGRA_H + +#include +#include + +#define TEGRA_MMC_PWRCTL_SD_BUS_POWER (1 << 0) +#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8 (5 << 1) +#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0 (6 << 1) +#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3 (7 << 1) + +#define TEGRA_MMC_HOSTCTL_DMASEL_MASK (3 << 3) +#define TEGRA_MMC_HOSTCTL_DMASEL_SDMA (0 << 3) +#define TEGRA_MMC_HOSTCTL_DMASEL_ADMA2_32BIT (2 << 3) +#define TEGRA_MMC_HOSTCTL_DMASEL_ADMA2_64BIT (3 << 3) + +#define TEGRA_MMC_TRNMOD_DMA_ENABLE (1 << 0) +#define TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE (1 << 1) +#define TEGRA_MMC_TRNMOD_AUTO_CMD12 (1 << 2) +#define TEGRA_MMC_TRNMOD_AUTO_CMD23 (1 << 3) +#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_WRITE (0 << 4) +#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ (1 << 4) +#define TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT (1 << 5) + +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_MASK (3 << 0) +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE (0 << 0) +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136 (1 << 0) +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48 (2 << 0) +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY (3 << 0) + +#define TEGRA_MMC_TRNMOD_CMD_CRC_CHECK (1 << 3) +#define TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK (1 << 4) +#define TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER (1 << 5) + +#define TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD (1 << 0) +#define TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT (1 << 1) + +#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE (1 << 0) +#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE (1 << 1) +#define TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE (1 << 2) +#define TEGRA_MMC_CLKCON_PROG_CLOCK_MODE (1 << 5) + +#define TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT 8 +#define TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_MASK (0xff << 8) + +#define TEGRA_MMC_SWRST_SW_RESET_FOR_ALL (1 << 0) +#define TEGRA_MMC_SWRST_SW_RESET_FOR_CMD_LINE (1 << 1) +#define TEGRA_MMC_SWRST_SW_RESET_FOR_DAT_LINE (1 << 2) + +#define TEGRA_MMC_NORINTSTS_CMD_COMPLETE (1 << 0) +#define TEGRA_MMC_NORINTSTS_XFER_COMPLETE (1 << 1) +#define TEGRA_MMC_NORINTSTS_DMA_INTERRUPT (1 << 3) +#define TEGRA_MMC_NORINTSTS_ERR_INTERRUPT (1 << 15) +#define TEGRA_MMC_NORINTSTS_CMD_TIMEOUT (1 << 16) + +#define TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE (1 << 0) +#define TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE (1 << 1) +#define TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT (1 << 3) +#define TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY (1 << 4) +#define TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY (1 << 5) + +#define TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE (1 << 1) + +typedef struct { + /* 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; + uint32_t response[0x4]; + 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; + uint8_t adma_error; + uint8_t _0x56[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; + uint32_t _0x70[0x23]; + 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[0x20]; + 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; +} tegra_sdmmc_t; + +static inline volatile tegra_sdmmc_t *sdmmc_get_regs(uint32_t idx) +{ + return (volatile tegra_sdmmc_t *)(0x700B0000 + (idx * 0x200)); +} + +#endif \ No newline at end of file diff --git a/fusee/fusee-primary/src/se.h b/fusee/fusee-primary/src/se.h index ad96a8020..a1ef7fa84 100644 --- a/fusee/fusee-primary/src/se.h +++ b/fusee/fusee-primary/src/se.h @@ -1,7 +1,6 @@ #ifndef FUSEE_SE_H #define FUSEE_SE_H -#include "utils.h" #include #define KEYSLOT_SWITCH_LP0TZRAMKEY 0x2 @@ -206,4 +205,4 @@ bool se_rsa2048_pss_verify(const void *signature, size_t signature_size, const v void se_initialize_rng(unsigned int keyslot); void se_generate_random(unsigned int keyslot, void *dst, size_t size); -#endif /* EXOSPHERE_SE_H */ +#endif diff --git a/fusee/fusee-primary/src/stage2.c b/fusee/fusee-primary/src/stage2.c index dded81221..0d933f859 100644 --- a/fusee/fusee-primary/src/stage2.c +++ b/fusee/fusee-primary/src/stage2.c @@ -1,5 +1,6 @@ -#include "utils.h" #include + +#include "utils.h" #include "display/video_fb.h" #include "fs_utils.h" #include "stage2.h" @@ -97,7 +98,7 @@ void load_stage2(const char *bct0) { } else { tmp_addr = config.load_address; } - + if (read_from_file((void *)tmp_addr, size, config.path) != size) { fatal_error("Failed to read stage2 (%s)!\n", config.path); } diff --git a/fusee/fusee-primary/src/stage2.h b/fusee/fusee-primary/src/stage2.h index 50e001c02..a2a5f1dbb 100644 --- a/fusee/fusee-primary/src/stage2.h +++ b/fusee/fusee-primary/src/stage2.h @@ -1,8 +1,7 @@ #ifndef FUSEE_STAGE2_H #define FUSEE_STAGE2_H -#include "utils.h" -#include "sdmmc.h" +#include "sdmmc/sdmmc_core.h" /* TODO: Is there a more concise way to do this? */ #define STAGE2_ARGV_PROGRAM_PATH 0 @@ -22,7 +21,7 @@ typedef struct { typedef struct { uint32_t version; - struct mmc sd_mmc; + sdmmc_t sd_sdmmc; bool display_initialized; char bct0[BCTO_MAX_SIZE]; } stage2_args_t; diff --git a/fusee/fusee-primary/src/supplies.c b/fusee/fusee-primary/src/supplies.c deleted file mode 100644 index 37ccfe95e..000000000 --- a/fusee/fusee-primary/src/supplies.c +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Fusée power supply control code - * ~ktemkin - */ - -#include "lib/driver_utils.h" -#include "supplies.h" - -// FIXME: replace hwinit with our own code -#include "hwinit/max7762x.h" - -/** - * Enables a given power supply. - * - * @param supply The power domain on the Switch that is to be enabled. - * @param use_low_voltage If the supply supports multiple voltages, use the lower one. - * Some devices start in a high power mode, but an can be switched to a lower one. - * Set this to false unless you know what you're doing. - */ -void supply_enable(enum switch_power_supply supply, bool use_low_voltage) -{ - uint32_t voltage = 0; - - switch(supply) { - case SUPPLY_MICROSD: - voltage = use_low_voltage ? SUPPLY_MICROSD_LOW_VOLTAGE : SUPPLY_MICROSD_VOLTAGE; - - max77620_regulator_set_voltage(SUPPLY_MICROSD_REGULATOR, voltage); - max77620_regulator_enable(SUPPLY_MICROSD_REGULATOR, true); - return; - - default: - printk("ERROR: could not enable unknown supply %d!\n", supply); - return; - } -} - diff --git a/fusee/fusee-primary/src/supplies.h b/fusee/fusee-primary/src/supplies.h deleted file mode 100644 index f328a67eb..000000000 --- a/fusee/fusee-primary/src/supplies.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Fusée power supply control code - * ~ktemkin - */ - -#ifndef __FUSEE_SUPPLIES_H__ -#define __FUSEE_SUPPLIES_H__ - -#include "utils.h" - -enum switch_power_supply { - SUPPLY_MICROSD, -}; - - -enum switch_power_constants { - - /* MicroSD card */ - SUPPLY_MICROSD_REGULATOR = 6, - SUPPLY_MICROSD_VOLTAGE = 3300000, - SUPPLY_MICROSD_LOW_VOLTAGE = 1800000, - -}; - - -/** - * Enables a given power supply. - * - * @param supply The power domain on the Switch that is to be enabled. - * @param use_low_voltage If the supply supports multiple voltages, use the lower one. - * Some devices start in a high power mode, but an can be switched to a lower one. - * Set this to false unless you know what you're doing. - */ -void supply_enable(enum switch_power_supply supply, bool use_low_voltage); - -#endif diff --git a/fusee/fusee-primary/src/timers.h b/fusee/fusee-primary/src/timers.h index 3c314e449..c951f0427 100644 --- a/fusee/fusee-primary/src/timers.h +++ b/fusee/fusee-primary/src/timers.h @@ -4,9 +4,7 @@ #include "utils.h" #define TIMERS_BASE 0x60005000 - #define MAKE_TIMERS_REG(n) MAKE_REG32(TIMERS_BASE + n) - #define TIMERUS_CNTR_1US_0 MAKE_REG32(TIMERS_BASE + 0x10) typedef struct { @@ -36,10 +34,9 @@ static inline uint32_t get_time_since(uint32_t 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(unsigned usecs) { + uint32_t start = get_time(); + while (get_time() - start < usecs); } __attribute__ ((noreturn)) void watchdog_reboot(void);