From 4b7cee533630202095748ecb396bd9eacf47ff3f Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Fri, 23 Jan 2015 12:12:01 +0200 Subject: mmc: Implement SD/MMC versioning properly The SD/MMC version scheme was buggy when dealing with standard major.minor.change cases. Fix it by using something similar to the linux's kernel versioning method. Signed-off-by: Pantelis Antoniou Tested-by: Jaehoon Chung Reported-by: Stephen Warren Tested-by: Stephen Warren diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c index 4e28c9d..1335e3d 100644 --- a/common/cmd_mmc.c +++ b/common/cmd_mmc.c @@ -85,8 +85,12 @@ static void print_mmcinfo(struct mmc *mmc) printf("Tran Speed: %d\n", mmc->tran_speed); printf("Rd Block Len: %d\n", mmc->read_bl_len); - printf("%s version %d.%d\n", IS_SD(mmc) ? "SD" : "MMC", - (mmc->version >> 8) & 0xf, mmc->version & 0xff); + printf("%s version %d.%d", IS_SD(mmc) ? "SD" : "MMC", + EXTRACT_SDMMC_MAJOR_VERSION(mmc->version), + EXTRACT_SDMMC_MINOR_VERSION(mmc->version)); + if (EXTRACT_SDMMC_CHANGE_VERSION(mmc->version) != 0) + printf(".%d", EXTRACT_SDMMC_CHANGE_VERSION(mmc->version)); + printf("\n"); printf("High Capacity: %s\n", mmc->high_capacity ? "Yes" : "No"); puts("Capacity: "); diff --git a/include/mmc.h b/include/mmc.h index 56d97bb..7d6feca 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -14,24 +14,41 @@ #include #include -#define SD_VERSION_SD 0x20000 -#define SD_VERSION_3 (SD_VERSION_SD | 0x300) -#define SD_VERSION_2 (SD_VERSION_SD | 0x200) -#define SD_VERSION_1_0 (SD_VERSION_SD | 0x100) -#define SD_VERSION_1_10 (SD_VERSION_SD | 0x10a) -#define MMC_VERSION_MMC 0x10000 -#define MMC_VERSION_UNKNOWN (MMC_VERSION_MMC) -#define MMC_VERSION_1_2 (MMC_VERSION_MMC | 0x102) -#define MMC_VERSION_1_4 (MMC_VERSION_MMC | 0x104) -#define MMC_VERSION_2_2 (MMC_VERSION_MMC | 0x202) -#define MMC_VERSION_3 (MMC_VERSION_MMC | 0x300) -#define MMC_VERSION_4 (MMC_VERSION_MMC | 0x400) -#define MMC_VERSION_4_1 (MMC_VERSION_MMC | 0x401) -#define MMC_VERSION_4_2 (MMC_VERSION_MMC | 0x402) -#define MMC_VERSION_4_3 (MMC_VERSION_MMC | 0x403) -#define MMC_VERSION_4_41 (MMC_VERSION_MMC | 0x429) -#define MMC_VERSION_4_5 (MMC_VERSION_MMC | 0x405) -#define MMC_VERSION_5_0 (MMC_VERSION_MMC | 0x500) +/* SD/MMC version bits; 8 flags, 8 major, 8 minor, 8 change */ +#define SD_VERSION_SD (1U << 31) +#define MMC_VERSION_MMC (1U << 30) + +#define MAKE_SDMMC_VERSION(a, b, c) \ + ((((u32)(a)) << 16) | ((u32)(b) << 8) | (u32)(c)) +#define MAKE_SD_VERSION(a, b, c) \ + (SD_VERSION_SD | MAKE_SDMMC_VERSION(a, b, c)) +#define MAKE_MMC_VERSION(a, b, c) \ + (MMC_VERSION_MMC | MAKE_SDMMC_VERSION(a, b, c)) + +#define EXTRACT_SDMMC_MAJOR_VERSION(x) \ + (((u32)(x) >> 16) & 0xff) +#define EXTRACT_SDMMC_MINOR_VERSION(x) \ + (((u32)(x) >> 8) & 0xff) +#define EXTRACT_SDMMC_CHANGE_VERSION(x) \ + ((u32)(x) & 0xff) + +#define SD_VERSION_3 MAKE_SD_VERSION(3, 0, 0) +#define SD_VERSION_2 MAKE_SD_VERSION(2, 0, 0) +#define SD_VERSION_1_0 MAKE_SD_VERSION(1, 0, 0) +#define SD_VERSION_1_10 MAKE_SD_VERSION(1, 10, 0) + +#define MMC_VERSION_UNKNOWN MAKE_MMC_VERSION(0, 0, 0) +#define MMC_VERSION_1_2 MAKE_MMC_VERSION(1, 2, 0) +#define MMC_VERSION_1_4 MAKE_MMC_VERSION(1, 4, 0) +#define MMC_VERSION_2_2 MAKE_MMC_VERSION(2, 2, 0) +#define MMC_VERSION_3 MAKE_MMC_VERSION(3, 0, 0) +#define MMC_VERSION_4 MAKE_MMC_VERSION(4, 0, 0) +#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_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) #define MMC_MODE_HS (1 << 0) #define MMC_MODE_HS_52MHz (1 << 1) @@ -43,7 +60,8 @@ #define SD_DATA_4BIT 0x00040000 -#define IS_SD(x) (x->version & SD_VERSION_SD) +#define IS_SD(x) ((x)->version & SD_VERSION_SD) +#define IS_MMC(x) ((x)->version & SD_VERSION_MMC) #define MMC_DATA_READ 1 #define MMC_DATA_WRITE 2 -- cgit v0.10.2 From afc9e2b509a6db3b716c81c97f9147491ebf76ba Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Wed, 14 Jan 2015 17:37:53 +0900 Subject: mmc: dw_mmc: fixed the wrong bit control If mode is not DDR-mode, then it needs to clear it. Signed-off-by: Jaehoon Chung diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index b18c75d..76fa0b0 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -321,7 +321,7 @@ static void dwmci_set_ios(struct mmc *mmc) if (mmc->ddr_mode) regs |= DWMCI_DDR_MODE; else - regs &= DWMCI_DDR_MODE; + regs &= ~DWMCI_DDR_MODE; dwmci_writel(host, DWMCI_UHS_REG, regs); -- cgit v0.10.2 From 3a33bb1874a75abde8872915f808945b6d20f6f2 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Wed, 4 Feb 2015 15:48:39 +0900 Subject: mmc: exynos_dw_mmc: set to clksel_val into board-init function "clksel_val" is assigned to property of mmc or defined value. But it doesn't write at initial sequence. There is a reason that get the wrong source-clock value. This patch fixed it. Signed-off-by: Jaehoon Chung diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index dfa209b..3936660 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -64,6 +64,10 @@ static void exynos_dwmci_board_init(struct dwmci_host *host) MPSCTRL_NON_SECURE_READ_BIT | MPSCTRL_NON_SECURE_WRITE_BIT | MPSCTRL_VALID); } + + /* Set to clksel_val at initial time */ + if (host->clksel_val) + exynos_dwmci_clksel(host); } static int exynos_dwmci_core_init(struct dwmci_host *host, int index) -- cgit v0.10.2 From 5dab81cea5500558b7bfc43a7364ae7ed706d2fc Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Wed, 4 Feb 2015 15:48:40 +0900 Subject: mmc: exynos_dw_mmc: use the exynos specific data structure Clksel value is exynos specific value. It removed "clksel_val" into dwmci_host and created the "dwmci_exynos_priv_data" structure for exynos specific data. Signed-off-by: Jaehoon Chung diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 3936660..c66c0b0 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -19,8 +19,13 @@ #define DWMMC_MAX_CH_NUM 4 #define DWMMC_MAX_FREQ 52000000 #define DWMMC_MIN_FREQ 400000 -#define DWMMC_MMC0_CLKSEL_VAL 0x03030001 -#define DWMMC_MMC2_CLKSEL_VAL 0x03020001 +#define DWMMC_MMC0_SDR_TIMING_VAL 0x03030001 +#define DWMMC_MMC2_SDR_TIMING_VAL 0x03020001 + +/* Exynos implmentation specific drver private data */ +struct dwmci_exynos_priv_data { + u32 sdr_timing; +}; /* * Function used as callback function to initialise the @@ -28,7 +33,9 @@ */ static void exynos_dwmci_clksel(struct dwmci_host *host) { - dwmci_writel(host, DWMCI_CLKSEL, host->clksel_val); + struct dwmci_exynos_priv_data *priv = host->priv; + + dwmci_writel(host, DWMCI_CLKSEL, priv->sdr_timing); } unsigned int exynos_dwmci_get_clk(struct dwmci_host *host) @@ -55,6 +62,8 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host) static void exynos_dwmci_board_init(struct dwmci_host *host) { + struct dwmci_exynos_priv_data *priv = host->priv; + if (host->quirks & DWMCI_QUIRK_DISABLE_SMU) { dwmci_writel(host, EMMCP_MPSBEGIN0, 0); dwmci_writel(host, EMMCP_SEND0, 0); @@ -65,8 +74,8 @@ static void exynos_dwmci_board_init(struct dwmci_host *host) MPSCTRL_NON_SECURE_WRITE_BIT | MPSCTRL_VALID); } - /* Set to clksel_val at initial time */ - if (host->clksel_val) + /* Set to timing value at initial time */ + if (priv->sdr_timing) exynos_dwmci_clksel(host); } @@ -74,6 +83,7 @@ static int exynos_dwmci_core_init(struct dwmci_host *host, int index) { unsigned int div; unsigned long freq, sclk; + struct dwmci_exynos_priv_data *priv = host->priv; if (host->bus_hz) freq = host->bus_hz; @@ -92,11 +102,11 @@ static int exynos_dwmci_core_init(struct dwmci_host *host, int index) #endif host->board_init = exynos_dwmci_board_init; - if (!host->clksel_val) { + if (!priv->sdr_timing) { if (index == 0) - host->clksel_val = DWMMC_MMC0_CLKSEL_VAL; + priv->sdr_timing = DWMMC_MMC0_SDR_TIMING_VAL; else if (index == 2) - host->clksel_val = DWMMC_MMC2_CLKSEL_VAL; + priv->sdr_timing = DWMMC_MMC2_SDR_TIMING_VAL; } host->caps = MMC_MODE_DDR_52MHz; @@ -122,6 +132,7 @@ static int exynos_dwmci_core_init(struct dwmci_host *host, int index) int exynos_dwmci_add_port(int index, u32 regbase, int bus_width, u32 clksel) { struct dwmci_host *host = NULL; + struct dwmci_exynos_priv_data *priv; host = malloc(sizeof(struct dwmci_host)); if (!host) { @@ -129,11 +140,19 @@ int exynos_dwmci_add_port(int index, u32 regbase, int bus_width, u32 clksel) return -ENOMEM; } + priv = malloc(sizeof(struct dwmci_exynos_priv_data)); + if (!priv) { + error("dwmci_exynos_priv_data malloc fail!\n"); + return -ENOMEM; + } + host->ioaddr = (void *)regbase; host->buswidth = bus_width; if (clksel) - host->clksel_val = clksel; + priv->sdr_timing = clksel; + + host->priv = priv; return exynos_dwmci_core_init(host, index); } @@ -161,7 +180,14 @@ static int exynos_dwmci_get_config(const void *blob, int node, struct dwmci_host *host) { int err = 0; - u32 base, clksel_val, timing[3]; + u32 base, timing[3]; + struct dwmci_exynos_priv_data *priv; + + priv = malloc(sizeof(struct dwmci_exynos_priv_data)); + if (!priv) { + error("dwmci_exynos_priv_data malloc fail!\n"); + return -ENOMEM; + } /* Extract device id for each mmc channel */ host->dev_id = pinmux_decode_periph_id(blob, node); @@ -194,16 +220,24 @@ static int exynos_dwmci_get_config(const void *blob, int node, return -EINVAL; } - clksel_val = (DWMCI_SET_SAMPLE_CLK(timing[0]) | + priv->sdr_timing = (DWMCI_SET_SAMPLE_CLK(timing[0]) | DWMCI_SET_DRV_CLK(timing[1]) | DWMCI_SET_DIV_RATIO(timing[2])); - if (clksel_val) - host->clksel_val = clksel_val; + + /* sdr_timing didn't assigned anything, use the default value */ + if (!priv->sdr_timing) { + if (host->dev_index == 0) + priv->sdr_timing = DWMMC_MMC0_SDR_TIMING_VAL; + else if (host->dev_index == 2) + priv->sdr_timing = DWMMC_MMC2_SDR_TIMING_VAL; + } host->fifoth_val = fdtdec_get_int(blob, node, "fifoth_val", 0); host->bus_hz = fdtdec_get_int(blob, node, "bus_hz", 0); host->div = fdtdec_get_int(blob, node, "div", 0); + host->priv = priv; + return 0; } diff --git a/include/dwmmc.h b/include/dwmmc.h index 109f7c8..86a5491 100644 --- a/include/dwmmc.h +++ b/include/dwmmc.h @@ -141,9 +141,9 @@ struct dwmci_host { int dev_index; int dev_id; int buswidth; - u32 clksel_val; u32 fifoth_val; struct mmc *mmc; + void *priv; void (*clksel)(struct dwmci_host *host); void (*board_init)(struct dwmci_host *host); -- cgit v0.10.2 From 1f3e877defc5a073f8e93c04e03f9a75f34dd595 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 19 Feb 2015 21:03:21 +0100 Subject: sunxi: mmc: Always declare High Capacity capability High Capacity (e)MMC cards work fine on sun4i / sun5i, and not having this capability set causes u-boot to not recognize the eMMC on an Utoo P66 A13 tablet, so always set it thereby fixing this. Signed-off-by: Hans de Goede Acked-by: Ian Campbell diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c index ebfec7c..2233545 100644 --- a/drivers/mmc/sunxi_mmc.c +++ b/drivers/mmc/sunxi_mmc.c @@ -449,11 +449,7 @@ struct mmc *sunxi_mmc_init(int sdc_no) cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; cfg->host_caps = MMC_MODE_4BIT; - cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; -#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \ - defined(CONFIG_MACH_SUN8I) || defined(CONFIG_MACH_SUN9I) - cfg->host_caps |= MMC_MODE_HC; -#endif + cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC; cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; cfg->f_min = 400000; -- cgit v0.10.2 From 64029f7aeec4e843ab99297c24e0ef4f40267a6e Mon Sep 17 00:00:00 2001 From: Przemyslaw Marczak Date: Fri, 20 Feb 2015 12:29:26 +0100 Subject: mmc: exynos dwmmc: check boot mode before init dwmmc Before this commit, the mmc devices were always registered in the same order. So dwmmc channel 0 was registered as mmc 0, channel 1 as mmc 1, etc. In case of possibility to boot from more then one device, the CONFIG_SYS_MMC_ENV_DEV should always point to right mmc device. This can be achieved by init boot device as first, so it will be always registered as mmc 0. Thanks to this, the 'saveenv' command will work fine for all mmc boot devices. Exynos based boards usually uses mmc host channels configuration: - 0, or 0+1 for 8 bit - as a default boot device (usually eMMC) - 2 for 4bit - as an optional boot device (usually SD card slot) And usually the boot order is defined by OM pin configuration, which can be changed in a few ways, eg. - Odroid U3 - eMMC card insertion -> first boot from eMMC - Odroid X2/XU3 - boot priority jumper By this commit, Exynos dwmmc driver will check the OM pin configuration, and then try to init the boot device and register it as mmc 0. Signed-off-by: Przemyslaw Marczak Cc: Minkyu Kang Cc: Jaehoon Chung Cc: Pantelis Antoniou Cc: Simon Glass Cc: Akshay Saraswat diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index c66c0b0..e083745 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -196,7 +197,6 @@ static int exynos_dwmci_get_config(const void *blob, int node, if (host->dev_index == host->dev_id) host->dev_index = host->dev_id - PERIPH_ID_SDMMC0; - /* Get the bus width from the device node */ host->buswidth = fdtdec_get_int(blob, node, "samsung,bus-width", 0); if (host->buswidth <= 0) { @@ -267,12 +267,21 @@ int exynos_dwmmc_init(const void *blob) { int compat_id; int node_list[DWMMC_MAX_CH_NUM]; + int boot_dev_node; int err = 0, count; compat_id = COMPAT_SAMSUNG_EXYNOS_DWMMC; count = fdtdec_find_aliases_for_id(blob, "mmc", compat_id, node_list, DWMMC_MAX_CH_NUM); + + /* For DWMMC always set boot device as mmc 0 */ + if (count >= 3 && get_boot_mode() == BOOT_MODE_SD) { + boot_dev_node = node_list[2]; + node_list[2] = node_list[0]; + node_list[0] = boot_dev_node; + } + err = exynos_dwmci_process_node(blob, node_list, count); return err; -- cgit v0.10.2 From 34dd928492fa020455d9b9f59fe4b643c0708306 Mon Sep 17 00:00:00 2001 From: Przemyslaw Marczak Date: Fri, 20 Feb 2015 12:29:27 +0100 Subject: mmc: print SD/eMMC type for inited mmc devices Depending on the boot priority, the eMMC/SD cards, can be initialized with the same numbers for each boot. To be sure which mmc device is SD and which is eMMC, this info is printed by 'mmc list' command, when the init is done. Signed-off-by: Przemyslaw Marczak Cc: Pantelis Antoniou diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index b8039cd..a13769e 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1693,11 +1693,19 @@ void print_mmc_devices(char separator) { struct mmc *m; struct list_head *entry; + char *mmc_type; list_for_each(entry, &mmc_devices) { m = list_entry(entry, struct mmc, link); + if (m->has_init) + mmc_type = IS_SD(m) ? "SD" : "eMMC"; + else + mmc_type = NULL; + printf("%s: %d", m->cfg->name, m->block_dev.dev); + if (mmc_type) + printf(" (%s)", mmc_type); if (entry->next != &mmc_devices) { printf("%c", separator); -- cgit v0.10.2 From f88a429f1179c9d9ab2883fba0ed0aa13c9bd72e Mon Sep 17 00:00:00 2001 From: Matt Reimer Date: Thu, 19 Feb 2015 11:22:53 -0700 Subject: mmc: sdhci: fix bus width switching on Samsung SoCs Fix bus width switching from 8-bit mode down to 4-bit or 1-bit modes on Samsung SoCs using SDHCI_QUIRK_USE_WIDE8. These SoCs report controller version 2.0 yet they support 8-bit bus widths. If 8-bit mode was previously enabled and then an operation like "mmc dev" caused a switch back down to 4-bit or 1-bit mode, WIDE8 was left set, causing failures. This problem was manifested by "mmc dev" timing out. Signed-off-by: Matt Reimer diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index de88e19..82d7984 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -374,7 +374,8 @@ static void sdhci_set_ios(struct mmc *mmc) (host->quirks & SDHCI_QUIRK_USE_WIDE8)) ctrl |= SDHCI_CTRL_8BITBUS; } else { - if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) + if ((SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) || + (host->quirks & SDHCI_QUIRK_USE_WIDE8)) ctrl &= ~SDHCI_CTRL_8BITBUS; if (mmc->bus_width == 4) ctrl |= SDHCI_CTRL_4BITBUS; -- cgit v0.10.2