diff options
-rw-r--r-- | arch/arm/dts/am57xx-beagle-x15.dts | 6 | ||||
-rw-r--r-- | arch/arm/dts/am57xx-idk-common.dtsi | 2 | ||||
-rw-r--r-- | arch/arm/dts/dra7.dtsi | 22 | ||||
-rw-r--r-- | arch/arm/include/asm/arch-omap5/clock.h | 2 | ||||
-rw-r--r-- | arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h | 3 | ||||
-rw-r--r-- | arch/arm/include/asm/arch-omap5/sys_proto.h | 7 | ||||
-rw-r--r-- | arch/arm/include/asm/omap_mmc.h | 59 | ||||
-rw-r--r-- | arch/arm/mach-omap2/omap5/dra7xx_iodelay.c | 30 | ||||
-rw-r--r-- | arch/arm/mach-omap2/omap5/hw_data.c | 10 | ||||
-rw-r--r-- | board/ti/am57xx/board.c | 30 | ||||
-rw-r--r-- | board/ti/dra7xx/evm.c | 29 | ||||
-rw-r--r-- | configs/dra7xx_evm_defconfig | 3 | ||||
-rw-r--r-- | configs/dra7xx_hs_evm_defconfig | 3 | ||||
-rw-r--r-- | drivers/mmc/mmc.c | 71 | ||||
-rw-r--r-- | drivers/mmc/omap_hsmmc.c | 954 | ||||
-rw-r--r-- | include/configs/am57xx_evm.h | 2 | ||||
-rw-r--r-- | include/configs/dra7xx_evm.h | 2 | ||||
-rw-r--r-- | include/mmc.h | 1 |
18 files changed, 1125 insertions, 111 deletions
diff --git a/arch/arm/dts/am57xx-beagle-x15.dts b/arch/arm/dts/am57xx-beagle-x15.dts index d668910..8d9bdf1 100644 --- a/arch/arm/dts/am57xx-beagle-x15.dts +++ b/arch/arm/dts/am57xx-beagle-x15.dts @@ -25,6 +25,11 @@ pinctrl-1 = <&mmc1_pins_hs>; vmmc-supply = <&ldo1_reg>; + /delete-property/ sd-uhs-sdr104; + /delete-property/ sd-uhs-sdr50; + /delete-property/ sd-uhs-ddr50; + /delete-property/ sd-uhs-sdr25; + /delete-property/ sd-uhs-sdr12; }; &mmc2 { @@ -32,6 +37,7 @@ pinctrl-0 = <&mmc2_pins_default>; pinctrl-1 = <&mmc2_pins_hs>; pinctrl-2 = <&mmc2_pins_ddr_3_3v_rev11 &mmc2_iodelay_ddr_3_3v_rev11_conf>; + /delete-property/ mmc-hs200-1_8v; }; /* errata i880 "Ethernet RGMII2 Limited to 10/100 Mbps" */ diff --git a/arch/arm/dts/am57xx-idk-common.dtsi b/arch/arm/dts/am57xx-idk-common.dtsi index 97aa8e6..fa5a078 100644 --- a/arch/arm/dts/am57xx-idk-common.dtsi +++ b/arch/arm/dts/am57xx-idk-common.dtsi @@ -413,6 +413,8 @@ bus-width = <8>; ti,non-removable; max-frequency = <96000000>; + no-1-8-v; + /delete-property/ mmc-hs200-1_8v; }; &dcan1 { diff --git a/arch/arm/dts/dra7.dtsi b/arch/arm/dts/dra7.dtsi index 02a136a..0f982d8 100644 --- a/arch/arm/dts/dra7.dtsi +++ b/arch/arm/dts/dra7.dtsi @@ -1056,7 +1056,7 @@ }; mmc1: mmc@4809c000 { - compatible = "ti,omap4-hsmmc"; + compatible = "ti,dra7-hsmmc", "ti,omap4-hsmmc"; reg = <0x4809c000 0x400>; interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>; ti,hwmods = "mmc1"; @@ -1067,10 +1067,15 @@ status = "disabled"; pbias-supply = <&pbias_mmc_reg>; max-frequency = <192000000>; + sd-uhs-sdr104; + sd-uhs-sdr50; + sd-uhs-ddr50; + sd-uhs-sdr25; + sd-uhs-sdr12; }; mmc2: mmc@480b4000 { - compatible = "ti,omap4-hsmmc"; + compatible = "ti,dra7-hsmmc", "ti,omap4-hsmmc"; reg = <0x480b4000 0x400>; interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>; ti,hwmods = "mmc2"; @@ -1079,10 +1084,14 @@ dma-names = "tx", "rx"; status = "disabled"; max-frequency = <192000000>; + sd-uhs-sdr25; + sd-uhs-sdr12; + mmc-hs200-1_8v; + mmc-ddr-1_8v; }; mmc3: mmc@480ad000 { - compatible = "ti,omap4-hsmmc"; + compatible = "ti,dra7-hsmmc", "ti,omap4-hsmmc"; reg = <0x480ad000 0x400>; interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>; ti,hwmods = "mmc3"; @@ -1092,10 +1101,13 @@ status = "disabled"; /* Errata i887 limits max-frequency of MMC3 to 64 MHz */ max-frequency = <64000000>; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; }; mmc4: mmc@480d1000 { - compatible = "ti,omap4-hsmmc"; + compatible = "ti,dra7-hsmmc", "ti,omap4-hsmmc"; reg = <0x480d1000 0x400>; interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>; ti,hwmods = "mmc4"; @@ -1104,6 +1116,8 @@ dma-names = "tx", "rx"; status = "disabled"; max-frequency = <192000000>; + sd-uhs-sdr12; + sd-uhs-sdr25; }; mmu0_dsp1: mmu@40d01000 { diff --git a/arch/arm/include/asm/arch-omap5/clock.h b/arch/arm/include/asm/arch-omap5/clock.h index ee2e78b..3d718c0 100644 --- a/arch/arm/include/asm/arch-omap5/clock.h +++ b/arch/arm/include/asm/arch-omap5/clock.h @@ -135,7 +135,7 @@ /* CM_L3INIT_HSMMCn_CLKCTRL */ #define HSMMC_CLKCTRL_CLKSEL_MASK (1 << 24) -#define HSMMC_CLKCTRL_CLKSEL_DIV_MASK (1 << 25) +#define HSMMC_CLKCTRL_CLKSEL_DIV_MASK (3 << 25) /* CM_L3INIT_SATA_CLKCTRL */ #define SATA_CLKCTRL_OPTFCLKEN_MASK (1 << 8) diff --git a/arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h b/arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h index c997004..a8780ee 100644 --- a/arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h +++ b/arch/arm/include/asm/arch-omap5/dra7xx_iodelay.h @@ -83,6 +83,9 @@ void __recalibrate_iodelay(struct pad_conf_entry const *pad, int npads, struct iodelay_cfg_entry const *iodelay, int niodelays); +void late_recalibrate_iodelay(struct pad_conf_entry const *pad, int npads, + struct iodelay_cfg_entry const *iodelay, + int niodelays); int __recalibrate_iodelay_start(void); void __recalibrate_iodelay_end(int ret); diff --git a/arch/arm/include/asm/arch-omap5/sys_proto.h b/arch/arm/include/asm/arch-omap5/sys_proto.h index a6b3557..d43cd7f 100644 --- a/arch/arm/include/asm/arch-omap5/sys_proto.h +++ b/arch/arm/include/asm/arch-omap5/sys_proto.h @@ -35,6 +35,12 @@ struct pad_conf_entry { u32 val; }; +struct mmc_platform_fixups { + const char *hw_rev; + u32 unsupported_caps; + u32 max_freq; +}; + struct omap_sysinfo { char *board_string; }; @@ -71,6 +77,7 @@ void force_emif_self_refresh(void); void get_ioregs(const struct ctrl_ioregs **regs); void srcomp_enable(void); void setup_warmreset_time(void); +const struct mmc_platform_fixups *platform_fixups_mmc(uint32_t addr); static inline u32 div_round_up(u32 num, u32 den) { diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index bf9de9b..3d70148 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -39,7 +39,9 @@ struct hsmmc { unsigned int sysstatus; /* 0x14 */ unsigned char res2[0x14]; unsigned int con; /* 0x2C */ - unsigned char res3[0xD4]; + unsigned int pwcnt; /* 0x30 */ + unsigned int dll; /* 0x34 */ + unsigned char res3[0xcc]; unsigned int blk; /* 0x104 */ unsigned int arg; /* 0x108 */ unsigned int cmd; /* 0x10C */ @@ -53,9 +55,11 @@ struct hsmmc { unsigned int sysctl; /* 0x12C */ unsigned int stat; /* 0x130 */ unsigned int ie; /* 0x134 */ - unsigned char res4[0x8]; + unsigned char res4[0x4]; + unsigned int ac12; /* 0x13C */ unsigned int capa; /* 0x140 */ - unsigned char res5[0x10]; + unsigned int capa2; /* 0x144 */ + unsigned char res5[0xc]; unsigned int admaes; /* 0x154 */ unsigned int admasal; /* 0x158 */ }; @@ -65,6 +69,8 @@ struct omap_hsmmc_plat { struct hsmmc *base_addr; struct mmc mmc; bool cd_inverted; + u32 controller_flags; + const char *hw_rev; }; /* @@ -87,6 +93,7 @@ struct omap_hsmmc_plat { #define WPP_ACTIVEHIGH (0x0 << 8) #define RESERVED_MASK (0x3 << 9) #define CTPL_MMC_SD (0x0 << 11) +#define DDR (0x1 << 19) #define DMA_MASTER (0x1 << 20) #define BLEN_512BYTESLEN (0x200 << 0) #define NBLK_STPCNT (0x0 << 16) @@ -124,8 +131,10 @@ struct omap_hsmmc_plat { #define DTW_8_BITMODE (0x1 << 5) /* CON[DW8]*/ #define SDBP_PWROFF (0x0 << 8) #define SDBP_PWRON (0x1 << 8) +#define SDVS_MASK (0x7 << 9) #define SDVS_1V8 (0x5 << 9) #define SDVS_3V0 (0x6 << 9) +#define SDVS_3V3 (0x7 << 9) #define DMA_SELECT (0x2 << 3) #define ICE_MASK (0x1 << 0) #define ICE_STOP (0x0 << 0) @@ -159,8 +168,20 @@ struct omap_hsmmc_plat { #define IE_CERR (0x01 << 28) #define IE_BADA (0x01 << 29) -#define VS30_3V0SUP (1 << 25) -#define VS18_1V8SUP (1 << 26) +#define VS33_3V3SUP BIT(24) +#define VS30_3V0SUP BIT(25) +#define VS18_1V8SUP BIT(26) + +#define AC12_ET BIT(22) +#define AC12_V1V8_SIGEN BIT(19) +#define AC12_SCLK_SEL BIT(23) +#define AC12_UHSMC_MASK (7 << 16) +#define AC12_UHSMC_DDR50 (4 << 16) +#define AC12_UHSMC_SDR104 (3 << 16) +#define AC12_UHSMC_SDR50 (2 << 16) +#define AC12_UHSMC_SDR25 (1 << 16) +#define AC12_UHSMC_SDR12 (0 << 16) +#define AC12_UHSMC_RES (0x7 << 16) /* Driver definitions */ #define MMCSD_SECTOR_SIZE 512 @@ -172,15 +193,43 @@ struct omap_hsmmc_plat { #define CLK_400KHZ 1 #define CLK_MISC 2 +#define CLKD_MAX 0x3FF /* max clock divisor: 1023 */ + #define RSP_TYPE_NONE (RSP_TYPE_NORSP | CCCE_NOCHECK | CICE_NOCHECK) #define MMC_CMD0 (INDEX(0) | RSP_TYPE_NONE | DP_NO_DATA | DDIR_WRITE) /* Clock Configurations and Macros */ +#ifdef CONFIG_OMAP54XX +#define MMC_CLOCK_REFERENCE 192 /* MHz */ +#else #define MMC_CLOCK_REFERENCE 96 /* MHz */ +#endif + +/* DLL */ +#define DLL_SWT BIT(20) +#define DLL_FORCE_SR_C_SHIFT 13 +#define DLL_FORCE_SR_C_MASK 0x7f +#define DLL_FORCE_VALUE BIT(12) +#define DLL_CALIB BIT(1) + +#define MAX_PHASE_DELAY 0x7c + +/* CAPA2 */ +#define CAPA2_TSDR50 BIT(13) #define mmc_reg_out(addr, mask, val)\ writel((readl(addr) & (~(mask))) | ((val) & (mask)), (addr)) +#define INT_EN_MASK (IE_BADA | IE_CERR | IE_DEB | IE_DCRC |\ + IE_DTO | IE_CIE | IE_CEB | IE_CCRC | IE_ADMAE | IE_CTO |\ + IE_BRR | IE_BWR | IE_TC | IE_CC) + +#define CON_CLKEXTFREE BIT(16) +#define CON_PADEN BIT(15) +#define PSTATE_CLEV BIT(24) +#define PSTATE_DLEV (0xF << 20) +#define PSTATE_DLEV_DAT0 (0x1 << 20) + int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, int wp_gpio); diff --git a/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c b/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c index 8798730..a9a9f75 100644 --- a/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c +++ b/arch/arm/mach-omap2/omap5/dra7xx_iodelay.c @@ -272,3 +272,33 @@ err: __recalibrate_iodelay_end(ret); } + +void late_recalibrate_iodelay(struct pad_conf_entry const *pad, int npads, + struct iodelay_cfg_entry const *iodelay, + int niodelays) +{ + int ret = 0; + + /* unlock IODELAY CONFIG registers */ + writel(CFG_IODELAY_UNLOCK_KEY, (*ctrl)->iodelay_config_base + + CFG_REG_8_OFFSET); + + ret = calibrate_iodelay((*ctrl)->iodelay_config_base); + if (ret) + goto err; + + ret = update_delay_mechanism((*ctrl)->iodelay_config_base); + + /* Configure Mux settings */ + do_set_mux32((*ctrl)->control_padconf_core_base, pad, npads); + + /* Configure Manual IO timing modes */ + ret = do_set_iodelay((*ctrl)->iodelay_config_base, iodelay, niodelays); + if (ret) + goto err; + +err: + /* lock IODELAY CONFIG registers */ + writel(CFG_IODELAY_LOCK_KEY, (*ctrl)->iodelay_config_base + + CFG_REG_8_OFFSET); +} diff --git a/arch/arm/mach-omap2/omap5/hw_data.c b/arch/arm/mach-omap2/omap5/hw_data.c index bb05e19..7fc3836 100644 --- a/arch/arm/mach-omap2/omap5/hw_data.c +++ b/arch/arm/mach-omap2/omap5/hw_data.c @@ -438,17 +438,17 @@ void enable_basic_clocks(void) setbits_le32((*prcm)->cm_l4per_gpio4_clkctrl, GPIO4_CLKCTRL_OPTFCLKEN_MASK); - /* Enable 96 MHz clock for MMC1 & MMC2 */ + /* Enable 192 MHz clock for MMC1 & MMC2 */ setbits_le32((*prcm)->cm_l3init_hsmmc1_clkctrl, HSMMC_CLKCTRL_CLKSEL_MASK); setbits_le32((*prcm)->cm_l3init_hsmmc2_clkctrl, HSMMC_CLKCTRL_CLKSEL_MASK); /* Set the correct clock dividers for mmc */ - setbits_le32((*prcm)->cm_l3init_hsmmc1_clkctrl, - HSMMC_CLKCTRL_CLKSEL_DIV_MASK); - setbits_le32((*prcm)->cm_l3init_hsmmc2_clkctrl, - HSMMC_CLKCTRL_CLKSEL_DIV_MASK); + clrbits_le32((*prcm)->cm_l3init_hsmmc1_clkctrl, + HSMMC_CLKCTRL_CLKSEL_DIV_MASK); + clrbits_le32((*prcm)->cm_l3init_hsmmc2_clkctrl, + HSMMC_CLKCTRL_CLKSEL_DIV_MASK); /* Select 32KHz clock as the source of GPTIMER1 */ setbits_le32((*prcm)->cm_wkup_gptimer1_clkctrl, diff --git a/board/ti/am57xx/board.c b/board/ti/am57xx/board.c index c3f60f6..5bd8778 100644 --- a/board/ti/am57xx/board.c +++ b/board/ti/am57xx/board.c @@ -30,6 +30,7 @@ #include <dwc3-uboot.h> #include <dwc3-omap-uboot.h> #include <ti-usb-phy-uboot.h> +#include <mmc.h> #include "../common/board_detect.h" #include "mux_data.h" @@ -815,6 +816,35 @@ int board_mmc_init(bd_t *bis) omap_mmc_init(1, 0, 0, -1, -1); return 0; } + +static const struct mmc_platform_fixups am57x_es1_1_mmc1_fixups = { + .hw_rev = "rev11", + .unsupported_caps = MMC_CAP(MMC_HS_200) | + MMC_CAP(UHS_SDR104), + .max_freq = 96000000, +}; + +static const struct mmc_platform_fixups am57x_es1_1_mmc23_fixups = { + .hw_rev = "rev11", + .unsupported_caps = MMC_CAP(MMC_HS_200) | + MMC_CAP(UHS_SDR104) | + MMC_CAP(UHS_SDR50), + .max_freq = 48000000, +}; + +const struct mmc_platform_fixups *platform_fixups_mmc(uint32_t addr) +{ + switch (omap_revision()) { + case DRA752_ES1_0: + case DRA752_ES1_1: + if (addr == OMAP_HSMMC1_BASE) + return &am57x_es1_1_mmc1_fixups; + else + return &am57x_es1_1_mmc23_fixups; + default: + return NULL; + } +} #endif #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_OS_BOOT) diff --git a/board/ti/dra7xx/evm.c b/board/ti/dra7xx/evm.c index 519475e..06f061c 100644 --- a/board/ti/dra7xx/evm.c +++ b/board/ti/dra7xx/evm.c @@ -866,6 +866,35 @@ void board_mmc_poweron_ldo(uint voltage) palmas_mmc1_poweron_ldo(LDO1_VOLTAGE, LDO1_CTRL, voltage); } } + +static const struct mmc_platform_fixups dra7x_es1_1_mmc1_fixups = { + .hw_rev = "rev11", + .unsupported_caps = MMC_CAP(MMC_HS_200) | + MMC_CAP(UHS_SDR104), + .max_freq = 96000000, +}; + +static const struct mmc_platform_fixups dra7x_es1_1_mmc23_fixups = { + .hw_rev = "rev11", + .unsupported_caps = MMC_CAP(MMC_HS_200) | + MMC_CAP(UHS_SDR104) | + MMC_CAP(UHS_SDR50), + .max_freq = 48000000, +}; + +const struct mmc_platform_fixups *platform_fixups_mmc(uint32_t addr) +{ + switch (omap_revision()) { + case DRA752_ES1_0: + case DRA752_ES1_1: + if (addr == OMAP_HSMMC1_BASE) + return &dra7x_es1_1_mmc1_fixups; + else + return &dra7x_es1_1_mmc23_fixups; + default: + return NULL; + } +} #endif #ifdef CONFIG_USB_DWC3 diff --git a/configs/dra7xx_evm_defconfig b/configs/dra7xx_evm_defconfig index b13a27e..e79e6d6 100644 --- a/configs/dra7xx_evm_defconfig +++ b/configs/dra7xx_evm_defconfig @@ -49,6 +49,9 @@ CONFIG_DM_GPIO=y CONFIG_PCF8575_GPIO=y CONFIG_DM_I2C=y CONFIG_DM_MMC=y +CONFIG_MMC_IO_VOLTAGE=y +CONFIG_MMC_UHS_SUPPORT=y +CONFIG_MMC_HS200_SUPPORT=y CONFIG_MMC_OMAP_HS=y CONFIG_DM_SPI_FLASH=y CONFIG_SPI_FLASH=y diff --git a/configs/dra7xx_hs_evm_defconfig b/configs/dra7xx_hs_evm_defconfig index 7ccb4f0..bc15fd8f 100644 --- a/configs/dra7xx_hs_evm_defconfig +++ b/configs/dra7xx_hs_evm_defconfig @@ -51,6 +51,9 @@ CONFIG_DM_GPIO=y CONFIG_PCF8575_GPIO=y CONFIG_DM_I2C=y CONFIG_DM_MMC=y +CONFIG_MMC_IO_VOLTAGE=y +CONFIG_MMC_UHS_SUPPORT=y +CONFIG_MMC_HS200_SUPPORT=y CONFIG_MMC_OMAP_HS=y CONFIG_DM_SPI_FLASH=y CONFIG_SPI_FLASH=y diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 255310a..fb303dc 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -213,8 +213,8 @@ static int mmc_select_mode(struct mmc *mmc, enum bus_mode mode) mmc->selected_mode = mode; mmc->tran_speed = mmc_mode2freq(mmc, mode); mmc->ddr_mode = mmc_is_mode_ddr(mode); - debug("selecting mode %s (freq : %d MHz)\n", mmc_mode_name(mode), - mmc->tran_speed / 1000000); + pr_debug("selecting mode %s (freq : %d MHz)\n", mmc_mode_name(mode), + mmc->tran_speed / 1000000); return 0; } @@ -457,7 +457,7 @@ ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, } if (mmc_set_blocklen(mmc, mmc->read_bl_len)) { - debug("%s: Failed to set blocklen\n", __func__); + pr_debug("%s: Failed to set blocklen\n", __func__); return 0; } @@ -465,7 +465,7 @@ ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, cur = (blocks_todo > mmc->cfg->b_max) ? mmc->cfg->b_max : blocks_todo; if (mmc_read_blocks(mmc, dst, start, cur) != cur) { - debug("%s: Failed to read blocks\n", __func__); + pr_debug("%s: Failed to read blocks\n", __func__); return 0; } blocks_todo -= cur; @@ -900,11 +900,11 @@ static int mmc_boot_part_access_chk(struct mmc *mmc, unsigned int part_num) forbidden = MMC_CAP(MMC_HS_200); if (MMC_CAP(mmc->selected_mode) & forbidden) { - debug("selected mode (%s) is forbidden for part %d\n", - mmc_mode_name(mmc->selected_mode), part_num); + pr_debug("selected mode (%s) is forbidden for part %d\n", + mmc_mode_name(mmc->selected_mode), part_num); change = true; } else if (mmc->selected_mode != mmc->best_mode) { - debug("selected mode is not optimal\n"); + pr_debug("selected mode is not optimal\n"); change = true; } @@ -1333,7 +1333,7 @@ static int sd_set_card_speed(struct mmc *mmc, enum bus_mode mode) if (err) return err; - if ((__be32_to_cpu(switch_status[4]) >> 24) != speed) + if (((__be32_to_cpu(switch_status[4]) >> 24) & 0xF) != speed) return -ENOTSUPP; return 0; @@ -1427,7 +1427,7 @@ retry_ssr: mmc->ssr.erase_offset = eo * 1000; } } else { - debug("Invalid Allocation Unit Size.\n"); + pr_debug("Invalid Allocation Unit Size.\n"); } return 0; @@ -1532,18 +1532,18 @@ void mmc_dump_capabilities(const char *text, uint caps) { enum bus_mode mode; - printf("%s: widths [", text); + pr_debug("%s: widths [", text); if (caps & MMC_MODE_8BIT) - printf("8, "); + pr_debug("8, "); if (caps & MMC_MODE_4BIT) - printf("4, "); + pr_debug("4, "); if (caps & MMC_MODE_1BIT) - printf("1, "); - printf("\b\b] modes ["); + pr_debug("1, "); + pr_debug("\b\b] modes ["); for (mode = MMC_LEGACY; mode < MMC_MODES_END; mode++) if (MMC_CAP(mode) & caps) - printf("%s, ", mmc_mode_name(mode)); - printf("\b\b]\n"); + pr_debug("%s, ", mmc_mode_name(mode)); + pr_debug("\b\b]\n"); } #endif @@ -1577,7 +1577,7 @@ static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage) mmc->signal_voltage = signal_voltage; err = mmc_set_ios(mmc); if (err) - debug("unable to set voltage (err %d)\n", err); + pr_debug("unable to set voltage (err %d)\n", err); return err; } @@ -1660,10 +1660,10 @@ static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps) for (w = widths; w < widths + ARRAY_SIZE(widths); w++) { if (*w & caps & mwt->widths) { - debug("trying mode %s width %d (at %d MHz)\n", - mmc_mode_name(mwt->mode), - bus_width(*w), - mmc_mode2freq(mmc, mwt->mode) / 1000000); + pr_debug("trying mode %s width %d (at %d MHz)\n", + mmc_mode_name(mwt->mode), + bus_width(*w), + mmc_mode2freq(mmc, mwt->mode) / 1000000); /* configure the bus width (card + host) */ err = sd_select_bus_width(mmc, bus_width(*w)); @@ -1686,7 +1686,7 @@ static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps) err = mmc_execute_tuning(mmc, mwt->tuning); if (err) { - debug("tuning failed\n"); + pr_debug("tuning failed\n"); goto error; } } @@ -1708,7 +1708,7 @@ error: } } - printf("unable to select a mode\n"); + pr_err("unable to select a mode\n"); return -ENOTSUPP; } @@ -1860,7 +1860,7 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) return 0; if (!mmc->ext_csd) { - debug("No ext_csd found!\n"); /* this should enver happen */ + pr_debug("No ext_csd found!\n"); /* this should enver happen */ return -ENOTSUPP; } @@ -1870,10 +1870,10 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) for_each_supported_width(card_caps & mwt->widths, mmc_is_mode_ddr(mwt->mode), ecbw) { enum mmc_voltage old_voltage; - debug("trying mode %s width %d (at %d MHz)\n", - mmc_mode_name(mwt->mode), - bus_width(ecbw->cap), - mmc_mode2freq(mmc, mwt->mode) / 1000000); + pr_debug("trying mode %s width %d (at %d MHz)\n", + mmc_mode_name(mwt->mode), + bus_width(ecbw->cap), + mmc_mode2freq(mmc, mwt->mode) / 1000000); old_voltage = mmc->signal_voltage; err = mmc_set_lowest_voltage(mmc, mwt->mode, MMC_ALL_SIGNAL_VOLTAGE); @@ -1914,7 +1914,7 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) if (mwt->tuning) { err = mmc_execute_tuning(mmc, mwt->tuning); if (err) { - debug("tuning failed\n"); + pr_debug("tuning failed\n"); goto error; } } @@ -1950,6 +1950,7 @@ static int mmc_startup_v4(struct mmc *mmc) MMC_VERSION_4_1, MMC_VERSION_4_2, MMC_VERSION_4_3, + MMC_VERSION_4_4, MMC_VERSION_4_41, MMC_VERSION_4_5, MMC_VERSION_5_0, @@ -2396,12 +2397,12 @@ static int mmc_power_init(struct mmc *mmc) ret = device_get_supply_regulator(mmc->dev, "vmmc-supply", &mmc->vmmc_supply); if (ret) - debug("%s: No vmmc supply\n", mmc->dev->name); + pr_debug("%s: No vmmc supply\n", mmc->dev->name); ret = device_get_supply_regulator(mmc->dev, "vqmmc-supply", &mmc->vqmmc_supply); if (ret) - debug("%s: No vqmmc supply\n", mmc->dev->name); + pr_debug("%s: No vqmmc supply\n", mmc->dev->name); #endif #else /* !CONFIG_DM_MMC */ /* @@ -2457,7 +2458,7 @@ static int mmc_power_off(struct mmc *mmc) int ret = regulator_set_enable(mmc->vmmc_supply, false); if (ret) { - debug("Error disabling VMMC supply\n"); + pr_debug("Error disabling VMMC supply\n"); return ret; } } @@ -2505,7 +2506,7 @@ int mmc_start_init(struct mmc *mmc) if (no_card) { mmc->has_init = 0; #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - printf("MMC: no card present\n"); + pr_err("MMC: no card present\n"); #endif return -ENOMEDIUM; } @@ -2532,7 +2533,7 @@ int mmc_start_init(struct mmc *mmc) * to use the UHS modes, because we wouldn't be able to * recover from an error during the UHS initialization. */ - debug("Unable to do a full power cycle. Disabling the UHS modes for safety\n"); + pr_debug("Unable to do a full power cycle. Disabling the UHS modes for safety\n"); uhs_en = false; mmc->host_caps &= ~UHS_CAPS; err = mmc_power_on(mmc); @@ -2629,7 +2630,7 @@ int mmc_init(struct mmc *mmc) if (!err) err = mmc_complete_init(mmc); if (err) - printf("%s: %d, time %lu\n", __func__, err, get_timer(start)); + pr_info("%s: %d, time %lu\n", __func__, err, get_timer(start)); return err; } diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index b12d6d9..02970f2 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -34,6 +34,10 @@ #endif #include <asm/io.h> #include <asm/arch/mmc_host_def.h> +#ifdef CONFIG_OMAP54XX +#include <asm/arch/mux_dra7xx.h> +#include <asm/arch/dra7xx_iodelay.h> +#endif #if !defined(CONFIG_SOC_KEYSTONE) #include <asm/gpio.h> #include <asm/arch/sys_proto.h> @@ -42,6 +46,7 @@ #include <asm/arch/mux.h> #endif #include <dm.h> +#include <power/regulator.h> DECLARE_GLOBAL_DATA_PTR; @@ -57,11 +62,23 @@ DECLARE_GLOBAL_DATA_PTR; #define SYSCTL_SRC (1 << 25) #define SYSCTL_SRD (1 << 26) +#ifdef CONFIG_IODELAY_RECALIBRATION +struct omap_hsmmc_pinctrl_state { + struct pad_conf_entry *padconf; + int npads; + struct iodelay_cfg_entry *iodelay; + int niodelays; +}; +#endif + struct omap_hsmmc_data { struct hsmmc *base_addr; #if !CONFIG_IS_ENABLED(DM_MMC) struct mmc_config cfg; #endif + uint bus_width; + uint clock; + ushort last_cmd; #ifdef OMAP_HSMMC_USE_GPIO #if CONFIG_IS_ENABLED(DM_MMC) struct gpio_desc cd_gpio; /* Change Detect GPIO */ @@ -72,11 +89,32 @@ struct omap_hsmmc_data { int wp_gpio; #endif #endif +#if CONFIG_IS_ENABLED(DM_MMC) + enum bus_mode mode; +#endif u8 controller_flags; #ifndef CONFIG_OMAP34XX struct omap_hsmmc_adma_desc *adma_desc_table; uint desc_slot; #endif + const char *hw_rev; + struct udevice *pbias_supply; + uint signal_voltage; +#ifdef CONFIG_IODELAY_RECALIBRATION + struct omap_hsmmc_pinctrl_state *default_pinctrl_state; + struct omap_hsmmc_pinctrl_state *hs_pinctrl_state; + struct omap_hsmmc_pinctrl_state *hs200_1_8v_pinctrl_state; + struct omap_hsmmc_pinctrl_state *ddr_1_8v_pinctrl_state; + struct omap_hsmmc_pinctrl_state *sdr12_pinctrl_state; + struct omap_hsmmc_pinctrl_state *sdr25_pinctrl_state; + struct omap_hsmmc_pinctrl_state *ddr50_pinctrl_state; + struct omap_hsmmc_pinctrl_state *sdr50_pinctrl_state; + struct omap_hsmmc_pinctrl_state *sdr104_pinctrl_state; +#endif +}; + +struct omap_mmc_of_data { + u8 controller_flags; }; #ifndef CONFIG_OMAP34XX @@ -102,6 +140,7 @@ struct omap_hsmmc_adma_desc { /* If we fail after 1 second wait, something is really bad */ #define MAX_RETRY_MS 1000 +#define MMC_TIMEOUT_MS 20 /* DMA transfers can take a long time if a lot a data is transferred. * The timeout must take in account the amount of data. Let's assume @@ -109,11 +148,17 @@ struct omap_hsmmc_adma_desc { * that the bandwidth is always above 3MB/s). */ #define DMA_TIMEOUT_PER_MB 333 +#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0) +#define OMAP_HSMMC_NO_1_8_V BIT(1) #define OMAP_HSMMC_USE_ADMA BIT(2) +#define OMAP_HSMMC_REQUIRE_IODELAY BIT(3) static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size); static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, unsigned int siz); +static void omap_hsmmc_start_clock(struct hsmmc *mmc_base); +static void omap_hsmmc_stop_clock(struct hsmmc *mmc_base); +static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit); static inline struct omap_hsmmc_data *omap_hsmmc_get_data(struct mmc *mmc) { @@ -211,7 +256,8 @@ static unsigned char mmc_board_init(struct mmc *mmc) &prcm_base->iclken1_core); #endif -#if defined(CONFIG_OMAP54XX) || defined(CONFIG_OMAP44XX) +#if (defined(CONFIG_OMAP54XX) || defined(CONFIG_OMAP44XX)) &&\ + !CONFIG_IS_ENABLED(DM_REGULATOR) /* PBIAS config needed for MMC1 only */ if (mmc_get_blk_desc(mmc)->devnum == 0) vmmc_pbias_config(LDO_VOLT_3V0); @@ -248,6 +294,424 @@ void mmc_init_stream(struct hsmmc *mmc_base) writel(readl(&mmc_base->con) & ~INIT_INITSTREAM, &mmc_base->con); } +#if CONFIG_IS_ENABLED(DM_MMC) +#ifdef CONFIG_IODELAY_RECALIBRATION +static void omap_hsmmc_io_recalibrate(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + struct omap_hsmmc_pinctrl_state *pinctrl_state; + + switch (priv->mode) { + case MMC_HS_200: + pinctrl_state = priv->hs200_1_8v_pinctrl_state; + break; + case UHS_SDR104: + pinctrl_state = priv->sdr104_pinctrl_state; + break; + case UHS_SDR50: + pinctrl_state = priv->sdr50_pinctrl_state; + break; + case UHS_DDR50: + pinctrl_state = priv->ddr50_pinctrl_state; + break; + case UHS_SDR25: + pinctrl_state = priv->sdr25_pinctrl_state; + break; + case UHS_SDR12: + pinctrl_state = priv->sdr12_pinctrl_state; + break; + case SD_HS: + case MMC_HS: + case MMC_HS_52: + pinctrl_state = priv->hs_pinctrl_state; + break; + case MMC_DDR_52: + pinctrl_state = priv->ddr_1_8v_pinctrl_state; + default: + pinctrl_state = priv->default_pinctrl_state; + break; + } + + if (!pinctrl_state) + pinctrl_state = priv->default_pinctrl_state; + + if (priv->controller_flags & OMAP_HSMMC_REQUIRE_IODELAY) { + if (pinctrl_state->iodelay) + late_recalibrate_iodelay(pinctrl_state->padconf, + pinctrl_state->npads, + pinctrl_state->iodelay, + pinctrl_state->niodelays); + else + do_set_mux32((*ctrl)->control_padconf_core_base, + pinctrl_state->padconf, + pinctrl_state->npads); + } +} +#endif +static void omap_hsmmc_set_timing(struct mmc *mmc) +{ + u32 val; + struct hsmmc *mmc_base; + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + + mmc_base = priv->base_addr; + + omap_hsmmc_stop_clock(mmc_base); + val = readl(&mmc_base->ac12); + val &= ~AC12_UHSMC_MASK; + priv->mode = mmc->selected_mode; + + if (mmc_is_mode_ddr(priv->mode)) + writel(readl(&mmc_base->con) | DDR, &mmc_base->con); + else + writel(readl(&mmc_base->con) & ~DDR, &mmc_base->con); + + switch (priv->mode) { + case MMC_HS_200: + case UHS_SDR104: + val |= AC12_UHSMC_SDR104; + break; + case UHS_SDR50: + val |= AC12_UHSMC_SDR50; + break; + case MMC_DDR_52: + case UHS_DDR50: + val |= AC12_UHSMC_DDR50; + break; + case SD_HS: + case MMC_HS_52: + case UHS_SDR25: + val |= AC12_UHSMC_SDR25; + break; + case MMC_LEGACY: + case MMC_HS: + case SD_LEGACY: + case UHS_SDR12: + val |= AC12_UHSMC_SDR12; + break; + default: + val |= AC12_UHSMC_RES; + break; + } + writel(val, &mmc_base->ac12); + +#ifdef CONFIG_IODELAY_RECALIBRATION + omap_hsmmc_io_recalibrate(mmc); +#endif + omap_hsmmc_start_clock(mmc_base); +} + +static void omap_hsmmc_conf_bus_power(struct mmc *mmc, uint signal_voltage) +{ + struct hsmmc *mmc_base; + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + u32 hctl, ac12; + + mmc_base = priv->base_addr; + + hctl = readl(&mmc_base->hctl) & ~SDVS_MASK; + ac12 = readl(&mmc_base->ac12) & ~AC12_V1V8_SIGEN; + + switch (signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + hctl |= SDVS_3V0; + break; + case MMC_SIGNAL_VOLTAGE_180: + hctl |= SDVS_1V8; + ac12 |= AC12_V1V8_SIGEN; + break; + } + + writel(hctl, &mmc_base->hctl); + writel(ac12, &mmc_base->ac12); +} + +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) +static int omap_hsmmc_wait_dat0(struct udevice *dev, int state, int timeout) +{ + int ret = -ETIMEDOUT; + u32 con; + bool dat0_high; + bool target_dat0_high = !!state; + struct omap_hsmmc_data *priv = dev_get_priv(dev); + struct hsmmc *mmc_base = priv->base_addr; + + con = readl(&mmc_base->con); + writel(con | CON_CLKEXTFREE | CON_PADEN, &mmc_base->con); + + timeout = DIV_ROUND_UP(timeout, 10); /* check every 10 us. */ + while (timeout--) { + dat0_high = !!(readl(&mmc_base->pstate) & PSTATE_DLEV_DAT0); + if (dat0_high == target_dat0_high) { + ret = 0; + break; + } + udelay(10); + } + writel(con, &mmc_base->con); + + return ret; +} +#endif + +#if CONFIG_IS_ENABLED(MMC_IO_VOLTAGE) +#if CONFIG_IS_ENABLED(DM_REGULATOR) +static int omap_hsmmc_set_io_regulator(struct mmc *mmc, int mV) +{ + int ret = 0; + int uV = mV * 1000; + + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + + if (!mmc->vqmmc_supply) + return 0; + + /* Disable PBIAS */ + ret = regulator_set_enable(priv->pbias_supply, false); + if (ret && ret != -ENOSYS) + return ret; + + /* Turn off IO voltage */ + ret = regulator_set_enable(mmc->vqmmc_supply, false); + if (ret && ret != -ENOSYS) + return ret; + /* Program a new IO voltage value */ + ret = regulator_set_value(mmc->vqmmc_supply, uV); + if (ret) + return ret; + /* Turn on IO voltage */ + ret = regulator_set_enable(mmc->vqmmc_supply, true); + if (ret && ret != -ENOSYS) + return ret; + + /* Program PBIAS voltage*/ + ret = regulator_set_value(priv->pbias_supply, uV); + if (ret && ret != -ENOSYS) + return ret; + /* Enable PBIAS */ + ret = regulator_set_enable(priv->pbias_supply, true); + if (ret && ret != -ENOSYS) + return ret; + + return 0; +} +#endif + +static int omap_hsmmc_set_signal_voltage(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + struct hsmmc *mmc_base = priv->base_addr; + int mv = mmc_voltage_to_mv(mmc->signal_voltage); + u32 capa_mask; + __maybe_unused u8 palmas_ldo_volt; + u32 val; + + if (mv < 0) + return -EINVAL; + + if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_330) { + /* Use 3.0V rather than 3.3V */ + mv = 3000; + capa_mask = VS30_3V0SUP; + palmas_ldo_volt = LDO_VOLT_3V0; + } else if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) { + capa_mask = VS18_1V8SUP; + palmas_ldo_volt = LDO_VOLT_1V8; + } else { + return -EOPNOTSUPP; + } + + val = readl(&mmc_base->capa); + if (!(val & capa_mask)) + return -EOPNOTSUPP; + + priv->signal_voltage = mmc->signal_voltage; + + omap_hsmmc_conf_bus_power(mmc, mmc->signal_voltage); + +#if CONFIG_IS_ENABLED(DM_REGULATOR) + return omap_hsmmc_set_io_regulator(mmc, mv); +#elif (defined(CONFIG_OMAP54XX) || defined(CONFIG_OMAP44XX)) && \ + defined(CONFIG_PALMAS_POWER) + if (mmc_get_blk_desc(mmc)->devnum == 0) + vmmc_pbias_config(palmas_ldo_volt); + return 0; +#else + return 0; +#endif +} +#endif + +static uint32_t omap_hsmmc_set_capabilities(struct mmc *mmc) +{ + struct hsmmc *mmc_base; + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + u32 val; + + mmc_base = priv->base_addr; + val = readl(&mmc_base->capa); + + if (priv->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) { + val |= (VS30_3V0SUP | VS18_1V8SUP); + } else if (priv->controller_flags & OMAP_HSMMC_NO_1_8_V) { + val |= VS30_3V0SUP; + val &= ~VS18_1V8SUP; + } else { + val |= VS18_1V8SUP; + val &= ~VS30_3V0SUP; + } + + writel(val, &mmc_base->capa); + + return val; +} + +#ifdef MMC_SUPPORTS_TUNING +static void omap_hsmmc_disable_tuning(struct mmc *mmc) +{ + struct hsmmc *mmc_base; + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + u32 val; + + mmc_base = priv->base_addr; + val = readl(&mmc_base->ac12); + val &= ~(AC12_SCLK_SEL); + writel(val, &mmc_base->ac12); + + val = readl(&mmc_base->dll); + val &= ~(DLL_FORCE_VALUE | DLL_SWT); + writel(val, &mmc_base->dll); +} + +static void omap_hsmmc_set_dll(struct mmc *mmc, int count) +{ + int i; + struct hsmmc *mmc_base; + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + u32 val; + + mmc_base = priv->base_addr; + val = readl(&mmc_base->dll); + val |= DLL_FORCE_VALUE; + val &= ~(DLL_FORCE_SR_C_MASK << DLL_FORCE_SR_C_SHIFT); + val |= (count << DLL_FORCE_SR_C_SHIFT); + writel(val, &mmc_base->dll); + + val |= DLL_CALIB; + writel(val, &mmc_base->dll); + for (i = 0; i < 1000; i++) { + if (readl(&mmc_base->dll) & DLL_CALIB) + break; + } + val &= ~DLL_CALIB; + writel(val, &mmc_base->dll); +} + +static int omap_hsmmc_execute_tuning(struct udevice *dev, uint opcode) +{ + struct omap_hsmmc_data *priv = dev_get_priv(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct mmc *mmc = upriv->mmc; + struct hsmmc *mmc_base; + u32 val; + u8 cur_match, prev_match = 0; + int ret; + u32 phase_delay = 0; + u32 start_window = 0, max_window = 0; + u32 length = 0, max_len = 0; + + mmc_base = priv->base_addr; + val = readl(&mmc_base->capa2); + + /* clock tuning is not needed for upto 52MHz */ + if (!((mmc->selected_mode == MMC_HS_200) || + (mmc->selected_mode == UHS_SDR104) || + ((mmc->selected_mode == UHS_SDR50) && (val & CAPA2_TSDR50)))) + return 0; + + val = readl(&mmc_base->dll); + val |= DLL_SWT; + writel(val, &mmc_base->dll); + while (phase_delay <= MAX_PHASE_DELAY) { + omap_hsmmc_set_dll(mmc, phase_delay); + + cur_match = !mmc_send_tuning(mmc, opcode, NULL); + + if (cur_match) { + if (prev_match) { + length++; + } else { + start_window = phase_delay; + length = 1; + } + } + + if (length > max_len) { + max_window = start_window; + max_len = length; + } + + prev_match = cur_match; + phase_delay += 4; + } + + if (!max_len) { + ret = -EIO; + goto tuning_error; + } + + val = readl(&mmc_base->ac12); + if (!(val & AC12_SCLK_SEL)) { + ret = -EIO; + goto tuning_error; + } + + phase_delay = max_window + 4 * ((3 * max_len) >> 2); + omap_hsmmc_set_dll(mmc, phase_delay); + + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC); + + return 0; + +tuning_error: + + omap_hsmmc_disable_tuning(mmc); + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC); + + return ret; +} +#endif + +static void omap_hsmmc_send_init_stream(struct udevice *dev) +{ + struct omap_hsmmc_data *priv = dev_get_priv(dev); + struct hsmmc *mmc_base = priv->base_addr; + + mmc_init_stream(mmc_base); +} +#endif + +static void mmc_enable_irq(struct mmc *mmc, struct mmc_cmd *cmd) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + struct hsmmc *mmc_base = priv->base_addr; + u32 irq_mask = INT_EN_MASK; + + /* + * TODO: Errata i802 indicates only DCRC interrupts can occur during + * tuning procedure and DCRC should be disabled. But see occurences + * of DEB, CIE, CEB, CCRC interupts during tuning procedure. These + * interrupts occur along with BRR, so the data is actually in the + * buffer. It has to be debugged why these interrutps occur + */ + if (cmd && mmc_is_tuning_cmd(cmd->cmdidx)) + irq_mask &= ~(IE_DEB | IE_DCRC | IE_CIE | IE_CEB | IE_CCRC); + + writel(irq_mask, &mmc_base->ie); +} + static int omap_hsmmc_init_setup(struct mmc *mmc) { struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); @@ -282,9 +746,16 @@ static int omap_hsmmc_init_setup(struct mmc *mmc) if (reg_val & MADMA_EN) priv->controller_flags |= OMAP_HSMMC_USE_ADMA; #endif + +#if CONFIG_IS_ENABLED(DM_MMC) + reg_val = omap_hsmmc_set_capabilities(mmc); + omap_hsmmc_conf_bus_power(mmc, (reg_val & VS30_3V0SUP) ? + MMC_SIGNAL_VOLTAGE_330 : MMC_SIGNAL_VOLTAGE_180); +#else writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl); writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP, &mmc_base->capa); +#endif reg_val = readl(&mmc_base->con) & RESERVED_MASK; @@ -308,11 +779,11 @@ static int omap_hsmmc_init_setup(struct mmc *mmc) writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl); - writel(IE_BADA | IE_CERR | IE_DEB | IE_DCRC | IE_DTO | IE_CIE | - IE_CEB | IE_CCRC | IE_ADMAE | IE_CTO | IE_BRR | IE_BWR | IE_TC | - IE_CC, &mmc_base->ie); + mmc_enable_irq(mmc, NULL); +#if !CONFIG_IS_ENABLED(DM_MMC) mmc_init_stream(mmc_base); +#endif return 0; } @@ -348,7 +819,7 @@ static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit) if (!(readl(&mmc_base->sysctl) & bit)) { start = get_timer(0); while (!(readl(&mmc_base->sysctl) & bit)) { - if (get_timer(0) - start > MAX_RETRY_MS) + if (get_timer(0) - start > MMC_TIMEOUT_MS) return; } } @@ -484,14 +955,13 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, struct mmc_data *data) { struct omap_hsmmc_data *priv = dev_get_priv(dev); -#ifndef CONFIG_OMAP34XX struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct mmc *mmc = upriv->mmc; #endif -#endif struct hsmmc *mmc_base; unsigned int flags, mmc_stat; ulong start; + priv->last_cmd = cmd->cmdidx; mmc_base = priv->base_addr; @@ -576,6 +1046,8 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, #endif } + mmc_enable_irq(mmc, cmd); + writel(cmd->cmdarg, &mmc_base->arg); udelay(20); /* To fix "No status update" error on eMMC */ writel((cmd->cmdidx << 24) | flags, &mmc_base->cmd); @@ -764,22 +1236,60 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, return 0; } -#if !CONFIG_IS_ENABLED(DM_MMC) -static int omap_hsmmc_set_ios(struct mmc *mmc) +static void omap_hsmmc_stop_clock(struct hsmmc *mmc_base) { - struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); -#else -static int omap_hsmmc_set_ios(struct udevice *dev) + writel(readl(&mmc_base->sysctl) & ~CEN_ENABLE, &mmc_base->sysctl); +} + +static void omap_hsmmc_start_clock(struct hsmmc *mmc_base) { - struct omap_hsmmc_data *priv = dev_get_priv(dev); - struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); - struct mmc *mmc = upriv->mmc; -#endif + writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl); +} + +static void omap_hsmmc_set_clock(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); struct hsmmc *mmc_base; unsigned int dsor = 0; ulong start; mmc_base = priv->base_addr; + omap_hsmmc_stop_clock(mmc_base); + + /* TODO: Is setting DTO required here? */ + mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK), + (ICE_STOP | DTO_15THDTO)); + + if (mmc->clock != 0) { + dsor = DIV_ROUND_UP(MMC_CLOCK_REFERENCE * 1000000, mmc->clock); + if (dsor > CLKD_MAX) + dsor = CLKD_MAX; + } else { + dsor = CLKD_MAX; + } + + mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK, + (dsor << CLKD_OFFSET) | ICE_OSCILLATE); + + start = get_timer(0); + while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) { + if (get_timer(0) - start > MAX_RETRY_MS) { + printf("%s: timedout waiting for ics!\n", __func__); + return; + } + } + + priv->clock = MMC_CLOCK_REFERENCE * 1000000 / dsor; + mmc->clock = priv->clock; + omap_hsmmc_start_clock(mmc_base); +} + +static void omap_hsmmc_set_bus_width(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + struct hsmmc *mmc_base; + + mmc_base = priv->base_addr; /* configue bus width */ switch (mmc->bus_width) { case 8: @@ -803,30 +1313,44 @@ static int omap_hsmmc_set_ios(struct udevice *dev) break; } - /* configure clock with 96Mhz system clock. - */ - if (mmc->clock != 0) { - dsor = (MMC_CLOCK_REFERENCE * 1000000 / mmc->clock); - if ((MMC_CLOCK_REFERENCE * 1000000) / dsor > mmc->clock) - dsor++; - } + priv->bus_width = mmc->bus_width; +} - mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK | CEN_MASK), - (ICE_STOP | DTO_15THDTO)); +#if !CONFIG_IS_ENABLED(DM_MMC) +static int omap_hsmmc_set_ios(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); +#else +static int omap_hsmmc_set_ios(struct udevice *dev) +{ + struct omap_hsmmc_data *priv = dev_get_priv(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct mmc *mmc = upriv->mmc; +#endif + struct hsmmc *mmc_base = priv->base_addr; + int ret = 0; - mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK, - (dsor << CLKD_OFFSET) | ICE_OSCILLATE); + if (priv->bus_width != mmc->bus_width) + omap_hsmmc_set_bus_width(mmc); - start = get_timer(0); - while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) { - if (get_timer(0) - start > MAX_RETRY_MS) { - printf("%s: timedout waiting for ics!\n", __func__); - return -ETIMEDOUT; - } - } - writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl); + if (priv->clock != mmc->clock) + omap_hsmmc_set_clock(mmc); - return 0; + if (mmc->clk_disable) + omap_hsmmc_stop_clock(mmc_base); + else + omap_hsmmc_start_clock(mmc_base); + +#if CONFIG_IS_ENABLED(DM_MMC) + if (priv->mode != mmc->selected_mode) + omap_hsmmc_set_timing(mmc); + +#if CONFIG_IS_ENABLED(MMC_IO_VOLTAGE) + if (priv->signal_voltage != mmc->signal_voltage) + ret = omap_hsmmc_set_signal_voltage(mmc); +#endif +#endif + return ret; } #ifdef OMAP_HSMMC_USE_GPIO @@ -896,6 +1420,13 @@ static const struct dm_mmc_ops omap_hsmmc_ops = { .get_cd = omap_hsmmc_getcd, .get_wp = omap_hsmmc_getwp, #endif +#ifdef MMC_SUPPORTS_TUNING + .execute_tuning = omap_hsmmc_execute_tuning, +#endif + .send_init_stream = omap_hsmmc_send_init_stream, +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) + .wait_dat0 = omap_hsmmc_wait_dat0, +#endif }; #else static const struct mmc_ops omap_hsmmc_ops = { @@ -918,7 +1449,7 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, struct mmc_config *cfg; uint host_caps_val; - priv = malloc(sizeof(*priv)); + priv = calloc(1, sizeof(*priv)); if (priv == NULL) return -1; @@ -990,6 +1521,7 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, if ((get_cpu_family() == CPU_OMAP34XX) && (get_cpu_rev() <= CPU_3XX_ES21)) cfg->b_max = 1; #endif + mmc = mmc_create(cfg, priv); if (mmc == NULL) return -1; @@ -997,41 +1529,321 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, return 0; } #else + +#ifdef CONFIG_IODELAY_RECALIBRATION +static struct pad_conf_entry * +omap_hsmmc_get_pad_conf_entry(const fdt32_t *pinctrl, int count) +{ + int index = 0; + struct pad_conf_entry *padconf; + + padconf = (struct pad_conf_entry *)malloc(sizeof(*padconf) * count); + if (!padconf) { + debug("failed to allocate memory\n"); + return 0; + } + + while (index < count) { + padconf[index].offset = fdt32_to_cpu(pinctrl[2 * index]); + padconf[index].val = fdt32_to_cpu(pinctrl[2 * index + 1]); + index++; + } + + return padconf; +} + +static struct iodelay_cfg_entry * +omap_hsmmc_get_iodelay_cfg_entry(const fdt32_t *pinctrl, int count) +{ + int index = 0; + struct iodelay_cfg_entry *iodelay; + + iodelay = (struct iodelay_cfg_entry *)malloc(sizeof(*iodelay) * count); + if (!iodelay) { + debug("failed to allocate memory\n"); + return 0; + } + + while (index < count) { + iodelay[index].offset = fdt32_to_cpu(pinctrl[3 * index]); + iodelay[index].a_delay = fdt32_to_cpu(pinctrl[3 * index + 1]); + iodelay[index].g_delay = fdt32_to_cpu(pinctrl[3 * index + 2]); + index++; + } + + return iodelay; +} + +static const fdt32_t *omap_hsmmc_get_pinctrl_entry(u32 phandle, + const char *name, int *len) +{ + const void *fdt = gd->fdt_blob; + int offset; + const fdt32_t *pinctrl; + + offset = fdt_node_offset_by_phandle(fdt, phandle); + if (offset < 0) { + debug("failed to get pinctrl node %s.\n", + fdt_strerror(offset)); + return 0; + } + + pinctrl = fdt_getprop(fdt, offset, name, len); + if (!pinctrl) { + debug("failed to get property %s\n", name); + return 0; + } + + return pinctrl; +} + +static uint32_t omap_hsmmc_get_pad_conf_phandle(struct mmc *mmc, + char *prop_name) +{ + const void *fdt = gd->fdt_blob; + const __be32 *phandle; + int node = dev_of_offset(mmc->dev); + + phandle = fdt_getprop(fdt, node, prop_name, NULL); + if (!phandle) { + debug("failed to get property %s\n", prop_name); + return 0; + } + + return fdt32_to_cpu(*phandle); +} + +static uint32_t omap_hsmmc_get_iodelay_phandle(struct mmc *mmc, + char *prop_name) +{ + const void *fdt = gd->fdt_blob; + const __be32 *phandle; + int len; + int count; + int node = dev_of_offset(mmc->dev); + + phandle = fdt_getprop(fdt, node, prop_name, &len); + if (!phandle) { + debug("failed to get property %s\n", prop_name); + return 0; + } + + /* No manual mode iodelay values if count < 2 */ + count = len / sizeof(*phandle); + if (count < 2) + return 0; + + return fdt32_to_cpu(*(phandle + 1)); +} + +static struct pad_conf_entry * +omap_hsmmc_get_pad_conf(struct mmc *mmc, char *prop_name, int *npads) +{ + int len; + int count; + struct pad_conf_entry *padconf; + u32 phandle; + const fdt32_t *pinctrl; + + phandle = omap_hsmmc_get_pad_conf_phandle(mmc, prop_name); + if (!phandle) + return ERR_PTR(-EINVAL); + + pinctrl = omap_hsmmc_get_pinctrl_entry(phandle, "pinctrl-single,pins", + &len); + if (!pinctrl) + return ERR_PTR(-EINVAL); + + count = (len / sizeof(*pinctrl)) / 2; + padconf = omap_hsmmc_get_pad_conf_entry(pinctrl, count); + if (!padconf) + return ERR_PTR(-EINVAL); + + *npads = count; + + return padconf; +} + +static struct iodelay_cfg_entry * +omap_hsmmc_get_iodelay(struct mmc *mmc, char *prop_name, int *niodelay) +{ + int len; + int count; + struct iodelay_cfg_entry *iodelay; + u32 phandle; + const fdt32_t *pinctrl; + + phandle = omap_hsmmc_get_iodelay_phandle(mmc, prop_name); + /* Not all modes have manual mode iodelay values. So its not fatal */ + if (!phandle) + return 0; + + pinctrl = omap_hsmmc_get_pinctrl_entry(phandle, "pinctrl-pin-array", + &len); + if (!pinctrl) + return ERR_PTR(-EINVAL); + + count = (len / sizeof(*pinctrl)) / 3; + iodelay = omap_hsmmc_get_iodelay_cfg_entry(pinctrl, count); + if (!iodelay) + return ERR_PTR(-EINVAL); + + *niodelay = count; + + return iodelay; +} + +static struct omap_hsmmc_pinctrl_state * +omap_hsmmc_get_pinctrl_by_mode(struct mmc *mmc, char *mode) +{ + int index; + int npads = 0; + int niodelays = 0; + const void *fdt = gd->fdt_blob; + int node = dev_of_offset(mmc->dev); + char prop_name[11]; + struct omap_hsmmc_pinctrl_state *pinctrl_state; + + pinctrl_state = (struct omap_hsmmc_pinctrl_state *) + malloc(sizeof(*pinctrl_state)); + if (!pinctrl_state) { + debug("failed to allocate memory\n"); + return 0; + } + + index = fdt_stringlist_search(fdt, node, "pinctrl-names", mode); + if (index < 0) { + debug("fail to find %s mode %s\n", mode, fdt_strerror(index)); + goto err_pinctrl_state; + } + + sprintf(prop_name, "pinctrl-%d", index); + + pinctrl_state->padconf = omap_hsmmc_get_pad_conf(mmc, prop_name, + &npads); + if (IS_ERR(pinctrl_state->padconf)) + goto err_pinctrl_state; + pinctrl_state->npads = npads; + + pinctrl_state->iodelay = omap_hsmmc_get_iodelay(mmc, prop_name, + &niodelays); + if (IS_ERR(pinctrl_state->iodelay)) + goto err_padconf; + pinctrl_state->niodelays = niodelays; + + return pinctrl_state; + +err_padconf: + kfree(pinctrl_state->padconf); + +err_pinctrl_state: + kfree(pinctrl_state); + return 0; +} + +#define OMAP_HSMMC_SETUP_PINCTRL(capmask, mode, optional) \ + do { \ + struct omap_hsmmc_pinctrl_state *s = NULL; \ + char str[20]; \ + if (!(cfg->host_caps & capmask)) \ + break; \ + \ + if (priv->hw_rev) { \ + sprintf(str, "%s-%s", #mode, priv->hw_rev); \ + s = omap_hsmmc_get_pinctrl_by_mode(mmc, str); \ + } \ + \ + if (!s) \ + s = omap_hsmmc_get_pinctrl_by_mode(mmc, #mode); \ + \ + if (!s && !optional) { \ + debug("%s: no pinctrl for %s\n", \ + mmc->dev->name, #mode); \ + cfg->host_caps &= ~(capmask); \ + } else { \ + priv->mode##_pinctrl_state = s; \ + } \ + } while (0) + +static int omap_hsmmc_get_pinctrl_state(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + struct mmc_config *cfg = omap_hsmmc_get_cfg(mmc); + struct omap_hsmmc_pinctrl_state *default_pinctrl; + + if (!(priv->controller_flags & OMAP_HSMMC_REQUIRE_IODELAY)) + return 0; + + default_pinctrl = omap_hsmmc_get_pinctrl_by_mode(mmc, "default"); + if (!default_pinctrl) { + printf("no pinctrl state for default mode\n"); + return -EINVAL; + } + + priv->default_pinctrl_state = default_pinctrl; + + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR104), sdr104, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR50), sdr50, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_DDR50), ddr50, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR25), sdr25, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR12), sdr12, false); + + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(MMC_HS_200), hs200_1_8v, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(MMC_DDR_52), ddr_1_8v, false); + OMAP_HSMMC_SETUP_PINCTRL(MMC_MODE_HS, hs, true); + + return 0; +} +#endif + #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) +#ifdef CONFIG_OMAP54XX +__weak const struct mmc_platform_fixups *platform_fixups_mmc(uint32_t addr) +{ + return NULL; +} +#endif + static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev) { struct omap_hsmmc_plat *plat = dev_get_platdata(dev); + struct omap_mmc_of_data *of_data = (void *)dev_get_driver_data(dev); + struct mmc_config *cfg = &plat->cfg; +#ifdef CONFIG_OMAP54XX + const struct mmc_platform_fixups *fixups; +#endif const void *fdt = gd->fdt_blob; int node = dev_of_offset(dev); - int val; + int ret; plat->base_addr = map_physmem(devfdt_get_addr(dev), sizeof(struct hsmmc *), MAP_NOCACHE); - cfg->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS; - val = fdtdec_get_int(fdt, node, "bus-width", -1); - if (val < 0) { - printf("error: bus-width property missing\n"); - return -ENOENT; - } - - switch (val) { - case 0x8: - cfg->host_caps |= MMC_MODE_8BIT; - case 0x4: - cfg->host_caps |= MMC_MODE_4BIT; - break; - default: - printf("error: invalid bus-width property\n"); - return -ENOENT; - } + ret = mmc_of_parse(dev, cfg); + if (ret < 0) + return ret; + cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; cfg->f_min = 400000; - cfg->f_max = fdtdec_get_int(fdt, node, "max-frequency", 52000000); cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + if (fdtdec_get_bool(fdt, node, "ti,dual-volt")) + plat->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT; + if (fdtdec_get_bool(fdt, node, "no-1-8-v")) + plat->controller_flags |= OMAP_HSMMC_NO_1_8_V; + if (of_data) + plat->controller_flags |= of_data->controller_flags; + +#ifdef CONFIG_OMAP54XX + fixups = platform_fixups_mmc(devfdt_get_addr(dev)); + if (fixups) { + plat->hw_rev = fixups->hw_rev; + cfg->host_caps &= ~fixups->unsupported_caps; + cfg->f_max = fixups->max_freq; + } +#endif #ifdef OMAP_HSMMC_USE_GPIO plat->cd_inverted = fdtdec_get_bool(fdt, node, "cd-inverted"); @@ -1057,9 +1869,14 @@ static int omap_hsmmc_probe(struct udevice *dev) struct omap_hsmmc_data *priv = dev_get_priv(dev); struct mmc_config *cfg = &plat->cfg; struct mmc *mmc; +#ifdef CONFIG_IODELAY_RECALIBRATION + int ret; +#endif cfg->name = "OMAP SD/MMC"; priv->base_addr = plat->base_addr; + priv->controller_flags = plat->controller_flags; + priv->hw_rev = plat->hw_rev; #ifdef OMAP_HSMMC_USE_GPIO priv->cd_inverted = plat->cd_inverted; #endif @@ -1071,7 +1888,10 @@ static int omap_hsmmc_probe(struct udevice *dev) if (mmc == NULL) return -1; #endif - +#if CONFIG_IS_ENABLED(DM_REGULATOR) + device_get_supply_regulator(dev, "pbias-supply", + &priv->pbias_supply); +#endif #if defined(OMAP_HSMMC_USE_GPIO) && CONFIG_IS_ENABLED(OF_CONTROL) gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, GPIOD_IS_IN); gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, GPIOD_IS_IN); @@ -1080,14 +1900,34 @@ static int omap_hsmmc_probe(struct udevice *dev) mmc->dev = dev; upriv->mmc = mmc; +#ifdef CONFIG_IODELAY_RECALIBRATION + ret = omap_hsmmc_get_pinctrl_state(mmc); + /* + * disable high speed modes for the platforms that require IO delay + * and for which we don't have this information + */ + if ((ret < 0) && + (priv->controller_flags & OMAP_HSMMC_REQUIRE_IODELAY)) { + priv->controller_flags &= ~OMAP_HSMMC_REQUIRE_IODELAY; + cfg->host_caps &= ~(MMC_CAP(MMC_HS_200) | MMC_CAP(MMC_DDR_52) | + UHS_CAPS); + } +#endif + return omap_hsmmc_init_setup(mmc); } #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) + +static const struct omap_mmc_of_data dra7_mmc_of_data = { + .controller_flags = OMAP_HSMMC_REQUIRE_IODELAY, +}; + static const struct udevice_id omap_hsmmc_ids[] = { { .compatible = "ti,omap3-hsmmc" }, { .compatible = "ti,omap4-hsmmc" }, { .compatible = "ti,am33xx-hsmmc" }, + { .compatible = "ti,dra7-hsmmc", .data = (ulong)&dra7_mmc_of_data }, { } }; #endif diff --git a/include/configs/am57xx_evm.h b/include/configs/am57xx_evm.h index 7546b3f..65465d1 100644 --- a/include/configs/am57xx_evm.h +++ b/include/configs/am57xx_evm.h @@ -15,9 +15,7 @@ #include <environment/ti/dfu.h> #include <linux/sizes.h> -#ifdef CONFIG_SPL_BUILD #define CONFIG_IODELAY_RECALIBRATION -#endif #define CONFIG_NR_DRAM_BANKS 2 diff --git a/include/configs/dra7xx_evm.h b/include/configs/dra7xx_evm.h index ff90b6d..975e6fd 100644 --- a/include/configs/dra7xx_evm.h +++ b/include/configs/dra7xx_evm.h @@ -14,9 +14,7 @@ #include <environment/ti/dfu.h> -#ifdef CONFIG_SPL_BUILD #define CONFIG_IODELAY_RECALIBRATION -#endif #define CONFIG_VERY_BIG_RAM #define CONFIG_NR_DRAM_BANKS 2 diff --git a/include/mmc.h b/include/mmc.h index a46eaed..86f885b 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -54,6 +54,7 @@ #define MMC_VERSION_4_1 MAKE_MMC_VERSION(4, 1, 0) #define MMC_VERSION_4_2 MAKE_MMC_VERSION(4, 2, 0) #define MMC_VERSION_4_3 MAKE_MMC_VERSION(4, 3, 0) +#define MMC_VERSION_4_4 MAKE_MMC_VERSION(4, 4, 0) #define MMC_VERSION_4_41 MAKE_MMC_VERSION(4, 4, 1) #define MMC_VERSION_4_5 MAKE_MMC_VERSION(4, 5, 0) #define MMC_VERSION_5_0 MAKE_MMC_VERSION(5, 0, 0) |