1442d5568SJaehoon Chung /* 2442d5568SJaehoon Chung * (C) Copyright 2012 SAMSUNG Electronics 3442d5568SJaehoon Chung * Jaehoon Chung <jh80.chung@samsung.com> 4442d5568SJaehoon Chung * 51a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 6442d5568SJaehoon Chung */ 7442d5568SJaehoon Chung 8442d5568SJaehoon Chung #include <common.h> 97aedafd6SJaehoon Chung #include <dm.h> 10442d5568SJaehoon Chung #include <malloc.h> 11442d5568SJaehoon Chung #include <sdhci.h> 123577fe8bSPiotr Wilczek #include <fdtdec.h> 133577fe8bSPiotr Wilczek #include <libfdt.h> 143577fe8bSPiotr Wilczek #include <asm/gpio.h> 15442d5568SJaehoon Chung #include <asm/arch/mmc.h> 16b09ed6e4SJaehoon Chung #include <asm/arch/clk.h> 173577fe8bSPiotr Wilczek #include <errno.h> 183577fe8bSPiotr Wilczek #include <asm/arch/pinmux.h> 19442d5568SJaehoon Chung 207aedafd6SJaehoon Chung #ifdef CONFIG_DM_MMC 217aedafd6SJaehoon Chung struct s5p_sdhci_plat { 227aedafd6SJaehoon Chung struct mmc_config cfg; 237aedafd6SJaehoon Chung struct mmc mmc; 247aedafd6SJaehoon Chung }; 257aedafd6SJaehoon Chung 267aedafd6SJaehoon Chung DECLARE_GLOBAL_DATA_PTR; 277aedafd6SJaehoon Chung #endif 287aedafd6SJaehoon Chung 29442d5568SJaehoon Chung static char *S5P_NAME = "SAMSUNG SDHCI"; 30442d5568SJaehoon Chung static void s5p_sdhci_set_control_reg(struct sdhci_host *host) 31442d5568SJaehoon Chung { 32442d5568SJaehoon Chung unsigned long val, ctrl; 33442d5568SJaehoon Chung /* 34442d5568SJaehoon Chung * SELCLKPADDS[17:16] 35442d5568SJaehoon Chung * 00 = 2mA 36442d5568SJaehoon Chung * 01 = 4mA 37442d5568SJaehoon Chung * 10 = 7mA 38442d5568SJaehoon Chung * 11 = 9mA 39442d5568SJaehoon Chung */ 40442d5568SJaehoon Chung sdhci_writel(host, SDHCI_CTRL4_DRIVE_MASK(0x3), SDHCI_CONTROL4); 41442d5568SJaehoon Chung 42442d5568SJaehoon Chung val = sdhci_readl(host, SDHCI_CONTROL2); 438ebde4f0SMatt Reimer val &= SDHCI_CTRL2_SELBASECLK_MASK(3); 44442d5568SJaehoon Chung 45442d5568SJaehoon Chung val |= SDHCI_CTRL2_ENSTAASYNCCLR | 46442d5568SJaehoon Chung SDHCI_CTRL2_ENCMDCNFMSK | 47442d5568SJaehoon Chung SDHCI_CTRL2_ENFBCLKRX | 48442d5568SJaehoon Chung SDHCI_CTRL2_ENCLKOUTHOLD; 49442d5568SJaehoon Chung 50442d5568SJaehoon Chung sdhci_writel(host, val, SDHCI_CONTROL2); 51442d5568SJaehoon Chung 52442d5568SJaehoon Chung /* 53442d5568SJaehoon Chung * FCSEL3[31] FCSEL2[23] FCSEL1[15] FCSEL0[7] 54442d5568SJaehoon Chung * FCSel[1:0] : Rx Feedback Clock Delay Control 55442d5568SJaehoon Chung * Inverter delay means10ns delay if SDCLK 50MHz setting 56442d5568SJaehoon Chung * 01 = Delay1 (basic delay) 57442d5568SJaehoon Chung * 11 = Delay2 (basic delay + 2ns) 58442d5568SJaehoon Chung * 00 = Delay3 (inverter delay) 59442d5568SJaehoon Chung * 10 = Delay4 (inverter delay + 2ns) 60442d5568SJaehoon Chung */ 61b268660cSJaehoon Chung val = SDHCI_CTRL3_FCSEL0 | SDHCI_CTRL3_FCSEL1; 62442d5568SJaehoon Chung sdhci_writel(host, val, SDHCI_CONTROL3); 63442d5568SJaehoon Chung 64442d5568SJaehoon Chung /* 65442d5568SJaehoon Chung * SELBASECLK[5:4] 66442d5568SJaehoon Chung * 00/01 = HCLK 67442d5568SJaehoon Chung * 10 = EPLL 68442d5568SJaehoon Chung * 11 = XTI or XEXTCLK 69442d5568SJaehoon Chung */ 70442d5568SJaehoon Chung ctrl = sdhci_readl(host, SDHCI_CONTROL2); 71442d5568SJaehoon Chung ctrl &= ~SDHCI_CTRL2_SELBASECLK_MASK(0x3); 72442d5568SJaehoon Chung ctrl |= SDHCI_CTRL2_SELBASECLK_MASK(0x2); 73442d5568SJaehoon Chung sdhci_writel(host, ctrl, SDHCI_CONTROL2); 74442d5568SJaehoon Chung } 75442d5568SJaehoon Chung 76f73b33ffSJaehoon Chung static void s5p_set_clock(struct sdhci_host *host, u32 div) 77f73b33ffSJaehoon Chung { 78f73b33ffSJaehoon Chung /* ToDo : Use the Clock Framework */ 79f73b33ffSJaehoon Chung set_mmc_clk(host->index, div); 80f73b33ffSJaehoon Chung } 81f73b33ffSJaehoon Chung 8262226b68SJaehoon Chung static const struct sdhci_ops s5p_sdhci_ops = { 8362226b68SJaehoon Chung .set_clock = &s5p_set_clock, 8462226b68SJaehoon Chung .set_control_reg = &s5p_sdhci_set_control_reg, 8562226b68SJaehoon Chung }; 8662226b68SJaehoon Chung 879b8c9a3cSJaehoon Chung static int s5p_sdhci_core_init(struct sdhci_host *host) 88442d5568SJaehoon Chung { 89442d5568SJaehoon Chung host->name = S5P_NAME; 90442d5568SJaehoon Chung 91b268660cSJaehoon Chung host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE | 92a034ec06SJaehoon Chung SDHCI_QUIRK_32BIT_DMA_ADDR | 93113e5dfcSJaehoon Chung SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_USE_WIDE8; 946d0e34bfSStefan Herbrechtsmeier host->max_clk = 52000000; 95442d5568SJaehoon Chung host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; 9662226b68SJaehoon Chung host->ops = &s5p_sdhci_ops; 97442d5568SJaehoon Chung 989b8c9a3cSJaehoon Chung if (host->bus_width == 8) 99113e5dfcSJaehoon Chung host->host_caps |= MMC_MODE_8BIT; 100442d5568SJaehoon Chung 1017aedafd6SJaehoon Chung #ifndef CONFIG_BLK 1026d0e34bfSStefan Herbrechtsmeier return add_sdhci(host, 0, 400000); 1037aedafd6SJaehoon Chung #else 1047aedafd6SJaehoon Chung return 0; 1057aedafd6SJaehoon Chung #endif 106442d5568SJaehoon Chung } 1073577fe8bSPiotr Wilczek 1089b8c9a3cSJaehoon Chung int s5p_sdhci_init(u32 regbase, int index, int bus_width) 1099b8c9a3cSJaehoon Chung { 1101a9d1731STobias Jakobi struct sdhci_host *host = calloc(1, sizeof(struct sdhci_host)); 1119b8c9a3cSJaehoon Chung if (!host) { 1121a9d1731STobias Jakobi printf("sdhci__host allocation fail!\n"); 1132cb5d67cSJaehoon Chung return -ENOMEM; 1149b8c9a3cSJaehoon Chung } 1159b8c9a3cSJaehoon Chung host->ioaddr = (void *)regbase; 1169b8c9a3cSJaehoon Chung host->index = index; 1179b8c9a3cSJaehoon Chung host->bus_width = bus_width; 1189b8c9a3cSJaehoon Chung 1199b8c9a3cSJaehoon Chung return s5p_sdhci_core_init(host); 1209b8c9a3cSJaehoon Chung } 1219b8c9a3cSJaehoon Chung 1220f925822SMasahiro Yamada #if CONFIG_IS_ENABLED(OF_CONTROL) 1233577fe8bSPiotr Wilczek struct sdhci_host sdhci_host[SDHCI_MAX_HOSTS]; 1243577fe8bSPiotr Wilczek 1253577fe8bSPiotr Wilczek static int do_sdhci_init(struct sdhci_host *host) 1263577fe8bSPiotr Wilczek { 1272308ea7cSTobias Jakobi int dev_id, flag, ret; 1283577fe8bSPiotr Wilczek 1293577fe8bSPiotr Wilczek flag = host->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE; 1303577fe8bSPiotr Wilczek dev_id = host->index + PERIPH_ID_SDMMC0; 1313577fe8bSPiotr Wilczek 13296094d4cSPrzemyslaw Marczak ret = exynos_pinmux_config(dev_id, flag); 13396094d4cSPrzemyslaw Marczak if (ret) { 13496094d4cSPrzemyslaw Marczak printf("external SD not configured\n"); 13596094d4cSPrzemyslaw Marczak return ret; 13696094d4cSPrzemyslaw Marczak } 13796094d4cSPrzemyslaw Marczak 1380347960bSSimon Glass if (dm_gpio_is_valid(&host->pwr_gpio)) { 1390347960bSSimon Glass dm_gpio_set_value(&host->pwr_gpio, 1); 1402308ea7cSTobias Jakobi ret = exynos_pinmux_config(dev_id, flag); 1412308ea7cSTobias Jakobi if (ret) { 1423577fe8bSPiotr Wilczek debug("MMC not configured\n"); 1432308ea7cSTobias Jakobi return ret; 1443577fe8bSPiotr Wilczek } 1453577fe8bSPiotr Wilczek } 1463577fe8bSPiotr Wilczek 1470347960bSSimon Glass if (dm_gpio_is_valid(&host->cd_gpio)) { 1482308ea7cSTobias Jakobi ret = dm_gpio_get_value(&host->cd_gpio); 1492308ea7cSTobias Jakobi if (ret) { 1502308ea7cSTobias Jakobi debug("no SD card detected (%d)\n", ret); 1513577fe8bSPiotr Wilczek return -ENODEV; 1522308ea7cSTobias Jakobi } 1533577fe8bSPiotr Wilczek } 1543577fe8bSPiotr Wilczek 1559b8c9a3cSJaehoon Chung return s5p_sdhci_core_init(host); 1563577fe8bSPiotr Wilczek } 1573577fe8bSPiotr Wilczek 1583577fe8bSPiotr Wilczek static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host) 1593577fe8bSPiotr Wilczek { 1603577fe8bSPiotr Wilczek int bus_width, dev_id; 1613577fe8bSPiotr Wilczek unsigned int base; 1623577fe8bSPiotr Wilczek 1633577fe8bSPiotr Wilczek /* Get device id */ 1643577fe8bSPiotr Wilczek dev_id = pinmux_decode_periph_id(blob, node); 165f0ecfc5eSSeung-Woo Kim if (dev_id < PERIPH_ID_SDMMC0 || dev_id > PERIPH_ID_SDMMC3) { 1663577fe8bSPiotr Wilczek debug("MMC: Can't get device id\n"); 1672cb5d67cSJaehoon Chung return -EINVAL; 1683577fe8bSPiotr Wilczek } 1693577fe8bSPiotr Wilczek host->index = dev_id - PERIPH_ID_SDMMC0; 1703577fe8bSPiotr Wilczek 1713577fe8bSPiotr Wilczek /* Get bus width */ 1723577fe8bSPiotr Wilczek bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0); 1733577fe8bSPiotr Wilczek if (bus_width <= 0) { 1743577fe8bSPiotr Wilczek debug("MMC: Can't get bus-width\n"); 1752cb5d67cSJaehoon Chung return -EINVAL; 1763577fe8bSPiotr Wilczek } 1773577fe8bSPiotr Wilczek host->bus_width = bus_width; 1783577fe8bSPiotr Wilczek 1793577fe8bSPiotr Wilczek /* Get the base address from the device node */ 1803577fe8bSPiotr Wilczek base = fdtdec_get_addr(blob, node, "reg"); 1813577fe8bSPiotr Wilczek if (!base) { 1823577fe8bSPiotr Wilczek debug("MMC: Can't get base address\n"); 1832cb5d67cSJaehoon Chung return -EINVAL; 1843577fe8bSPiotr Wilczek } 1853577fe8bSPiotr Wilczek host->ioaddr = (void *)base; 1863577fe8bSPiotr Wilczek 1870347960bSSimon Glass gpio_request_by_name_nodev(blob, node, "pwr-gpios", 0, &host->pwr_gpio, 1880347960bSSimon Glass GPIOD_IS_OUT); 1890347960bSSimon Glass gpio_request_by_name_nodev(blob, node, "cd-gpios", 0, &host->cd_gpio, 1900347960bSSimon Glass GPIOD_IS_IN); 1913577fe8bSPiotr Wilczek 1923577fe8bSPiotr Wilczek return 0; 1933577fe8bSPiotr Wilczek } 1943577fe8bSPiotr Wilczek 1953577fe8bSPiotr Wilczek static int process_nodes(const void *blob, int node_list[], int count) 1963577fe8bSPiotr Wilczek { 1973577fe8bSPiotr Wilczek struct sdhci_host *host; 198995a54ccSTobias Jakobi int i, node, ret; 1996a9fbb6eSTobias Jakobi int failed = 0; 2003577fe8bSPiotr Wilczek 2013577fe8bSPiotr Wilczek debug("%s: count = %d\n", __func__, count); 2023577fe8bSPiotr Wilczek 2033577fe8bSPiotr Wilczek /* build sdhci_host[] for each controller */ 2043577fe8bSPiotr Wilczek for (i = 0; i < count; i++) { 2053577fe8bSPiotr Wilczek node = node_list[i]; 2063577fe8bSPiotr Wilczek if (node <= 0) 2073577fe8bSPiotr Wilczek continue; 2083577fe8bSPiotr Wilczek 2093577fe8bSPiotr Wilczek host = &sdhci_host[i]; 2103577fe8bSPiotr Wilczek 211995a54ccSTobias Jakobi ret = sdhci_get_config(blob, node, host); 212995a54ccSTobias Jakobi if (ret) { 213995a54ccSTobias Jakobi printf("%s: failed to decode dev %d (%d)\n", __func__, i, ret); 2146a9fbb6eSTobias Jakobi failed++; 2156a9fbb6eSTobias Jakobi continue; 2163577fe8bSPiotr Wilczek } 2176a9fbb6eSTobias Jakobi 218995a54ccSTobias Jakobi ret = do_sdhci_init(host); 21996094d4cSPrzemyslaw Marczak if (ret && ret != -ENODEV) { 220995a54ccSTobias Jakobi printf("%s: failed to initialize dev %d (%d)\n", __func__, i, ret); 2216a9fbb6eSTobias Jakobi failed++; 2223577fe8bSPiotr Wilczek } 2236a9fbb6eSTobias Jakobi } 2246a9fbb6eSTobias Jakobi 2256a9fbb6eSTobias Jakobi /* we only consider it an error when all nodes fail */ 2266a9fbb6eSTobias Jakobi return (failed == count ? -1 : 0); 2273577fe8bSPiotr Wilczek } 2283577fe8bSPiotr Wilczek 2293577fe8bSPiotr Wilczek int exynos_mmc_init(const void *blob) 2303577fe8bSPiotr Wilczek { 2313577fe8bSPiotr Wilczek int count; 2323577fe8bSPiotr Wilczek int node_list[SDHCI_MAX_HOSTS]; 2333577fe8bSPiotr Wilczek 2343577fe8bSPiotr Wilczek count = fdtdec_find_aliases_for_id(blob, "mmc", 2353577fe8bSPiotr Wilczek COMPAT_SAMSUNG_EXYNOS_MMC, node_list, 2363577fe8bSPiotr Wilczek SDHCI_MAX_HOSTS); 2373577fe8bSPiotr Wilczek 2386a9fbb6eSTobias Jakobi return process_nodes(blob, node_list, count); 2393577fe8bSPiotr Wilczek } 2403577fe8bSPiotr Wilczek #endif 2417aedafd6SJaehoon Chung 2427aedafd6SJaehoon Chung #ifdef CONFIG_DM_MMC 2437aedafd6SJaehoon Chung static int s5p_sdhci_probe(struct udevice *dev) 2447aedafd6SJaehoon Chung { 2457aedafd6SJaehoon Chung struct s5p_sdhci_plat *plat = dev_get_platdata(dev); 2467aedafd6SJaehoon Chung struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); 2477aedafd6SJaehoon Chung struct sdhci_host *host = dev_get_priv(dev); 2487aedafd6SJaehoon Chung int ret; 2497aedafd6SJaehoon Chung 250*e160f7d4SSimon Glass ret = sdhci_get_config(gd->fdt_blob, dev_of_offset(dev), host); 2517aedafd6SJaehoon Chung if (ret) 2527aedafd6SJaehoon Chung return ret; 2537aedafd6SJaehoon Chung 2547aedafd6SJaehoon Chung ret = do_sdhci_init(host); 2557aedafd6SJaehoon Chung if (ret) 2567aedafd6SJaehoon Chung return ret; 2577aedafd6SJaehoon Chung 2586d0e34bfSStefan Herbrechtsmeier ret = sdhci_setup_cfg(&plat->cfg, host, 0, 400000); 2597aedafd6SJaehoon Chung if (ret) 2607aedafd6SJaehoon Chung return ret; 2617aedafd6SJaehoon Chung 2627aedafd6SJaehoon Chung host->mmc = &plat->mmc; 2637aedafd6SJaehoon Chung host->mmc->priv = host; 2647aedafd6SJaehoon Chung host->mmc->dev = dev; 2657aedafd6SJaehoon Chung upriv->mmc = host->mmc; 2667aedafd6SJaehoon Chung 2677aedafd6SJaehoon Chung return sdhci_probe(dev); 2687aedafd6SJaehoon Chung } 2697aedafd6SJaehoon Chung 2707aedafd6SJaehoon Chung static int s5p_sdhci_bind(struct udevice *dev) 2717aedafd6SJaehoon Chung { 2727aedafd6SJaehoon Chung struct s5p_sdhci_plat *plat = dev_get_platdata(dev); 2737aedafd6SJaehoon Chung int ret; 2747aedafd6SJaehoon Chung 2757aedafd6SJaehoon Chung ret = sdhci_bind(dev, &plat->mmc, &plat->cfg); 2767aedafd6SJaehoon Chung if (ret) 2777aedafd6SJaehoon Chung return ret; 2787aedafd6SJaehoon Chung 2797aedafd6SJaehoon Chung return 0; 2807aedafd6SJaehoon Chung } 2817aedafd6SJaehoon Chung 2827aedafd6SJaehoon Chung static const struct udevice_id s5p_sdhci_ids[] = { 2837aedafd6SJaehoon Chung { .compatible = "samsung,exynos4412-sdhci"}, 2847aedafd6SJaehoon Chung { } 2857aedafd6SJaehoon Chung }; 2867aedafd6SJaehoon Chung 2877aedafd6SJaehoon Chung U_BOOT_DRIVER(s5p_sdhci_drv) = { 2887aedafd6SJaehoon Chung .name = "s5p_sdhci", 2897aedafd6SJaehoon Chung .id = UCLASS_MMC, 2907aedafd6SJaehoon Chung .of_match = s5p_sdhci_ids, 2917aedafd6SJaehoon Chung .bind = s5p_sdhci_bind, 2927aedafd6SJaehoon Chung .ops = &sdhci_ops, 2937aedafd6SJaehoon Chung .probe = s5p_sdhci_probe, 2947aedafd6SJaehoon Chung .priv_auto_alloc_size = sizeof(struct sdhci_host), 2957aedafd6SJaehoon Chung .platdata_auto_alloc_size = sizeof(struct s5p_sdhci_plat), 2967aedafd6SJaehoon Chung }; 2977aedafd6SJaehoon Chung #endif /* CONFIG_DM_MMC */ 298