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 82*62226b68SJaehoon Chung static const struct sdhci_ops s5p_sdhci_ops = { 83*62226b68SJaehoon Chung .set_clock = &s5p_set_clock, 84*62226b68SJaehoon Chung .set_control_reg = &s5p_sdhci_set_control_reg, 85*62226b68SJaehoon Chung }; 86*62226b68SJaehoon 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; 94442d5568SJaehoon Chung host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; 95*62226b68SJaehoon Chung host->ops = &s5p_sdhci_ops; 96442d5568SJaehoon Chung 979b8c9a3cSJaehoon Chung if (host->bus_width == 8) 98113e5dfcSJaehoon Chung host->host_caps |= MMC_MODE_8BIT; 99442d5568SJaehoon Chung 1007aedafd6SJaehoon Chung #ifndef CONFIG_BLK 101a68aac49SJaehoon Chung return add_sdhci(host, 52000000, 400000); 1027aedafd6SJaehoon Chung #else 1037aedafd6SJaehoon Chung return 0; 1047aedafd6SJaehoon Chung #endif 105442d5568SJaehoon Chung } 1063577fe8bSPiotr Wilczek 1079b8c9a3cSJaehoon Chung int s5p_sdhci_init(u32 regbase, int index, int bus_width) 1089b8c9a3cSJaehoon Chung { 1091a9d1731STobias Jakobi struct sdhci_host *host = calloc(1, sizeof(struct sdhci_host)); 1109b8c9a3cSJaehoon Chung if (!host) { 1111a9d1731STobias Jakobi printf("sdhci__host allocation fail!\n"); 1122cb5d67cSJaehoon Chung return -ENOMEM; 1139b8c9a3cSJaehoon Chung } 1149b8c9a3cSJaehoon Chung host->ioaddr = (void *)regbase; 1159b8c9a3cSJaehoon Chung host->index = index; 1169b8c9a3cSJaehoon Chung host->bus_width = bus_width; 1179b8c9a3cSJaehoon Chung 1189b8c9a3cSJaehoon Chung return s5p_sdhci_core_init(host); 1199b8c9a3cSJaehoon Chung } 1209b8c9a3cSJaehoon Chung 1210f925822SMasahiro Yamada #if CONFIG_IS_ENABLED(OF_CONTROL) 1223577fe8bSPiotr Wilczek struct sdhci_host sdhci_host[SDHCI_MAX_HOSTS]; 1233577fe8bSPiotr Wilczek 1243577fe8bSPiotr Wilczek static int do_sdhci_init(struct sdhci_host *host) 1253577fe8bSPiotr Wilczek { 1262308ea7cSTobias Jakobi int dev_id, flag, ret; 1273577fe8bSPiotr Wilczek 1283577fe8bSPiotr Wilczek flag = host->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE; 1293577fe8bSPiotr Wilczek dev_id = host->index + PERIPH_ID_SDMMC0; 1303577fe8bSPiotr Wilczek 13196094d4cSPrzemyslaw Marczak ret = exynos_pinmux_config(dev_id, flag); 13296094d4cSPrzemyslaw Marczak if (ret) { 13396094d4cSPrzemyslaw Marczak printf("external SD not configured\n"); 13496094d4cSPrzemyslaw Marczak return ret; 13596094d4cSPrzemyslaw Marczak } 13696094d4cSPrzemyslaw Marczak 1370347960bSSimon Glass if (dm_gpio_is_valid(&host->pwr_gpio)) { 1380347960bSSimon Glass dm_gpio_set_value(&host->pwr_gpio, 1); 1392308ea7cSTobias Jakobi ret = exynos_pinmux_config(dev_id, flag); 1402308ea7cSTobias Jakobi if (ret) { 1413577fe8bSPiotr Wilczek debug("MMC not configured\n"); 1422308ea7cSTobias Jakobi return ret; 1433577fe8bSPiotr Wilczek } 1443577fe8bSPiotr Wilczek } 1453577fe8bSPiotr Wilczek 1460347960bSSimon Glass if (dm_gpio_is_valid(&host->cd_gpio)) { 1472308ea7cSTobias Jakobi ret = dm_gpio_get_value(&host->cd_gpio); 1482308ea7cSTobias Jakobi if (ret) { 1492308ea7cSTobias Jakobi debug("no SD card detected (%d)\n", ret); 1503577fe8bSPiotr Wilczek return -ENODEV; 1512308ea7cSTobias Jakobi } 1523577fe8bSPiotr Wilczek } 1533577fe8bSPiotr Wilczek 1549b8c9a3cSJaehoon Chung return s5p_sdhci_core_init(host); 1553577fe8bSPiotr Wilczek } 1563577fe8bSPiotr Wilczek 1573577fe8bSPiotr Wilczek static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host) 1583577fe8bSPiotr Wilczek { 1593577fe8bSPiotr Wilczek int bus_width, dev_id; 1603577fe8bSPiotr Wilczek unsigned int base; 1613577fe8bSPiotr Wilczek 1623577fe8bSPiotr Wilczek /* Get device id */ 1633577fe8bSPiotr Wilczek dev_id = pinmux_decode_periph_id(blob, node); 164f0ecfc5eSSeung-Woo Kim if (dev_id < PERIPH_ID_SDMMC0 || dev_id > PERIPH_ID_SDMMC3) { 1653577fe8bSPiotr Wilczek debug("MMC: Can't get device id\n"); 1662cb5d67cSJaehoon Chung return -EINVAL; 1673577fe8bSPiotr Wilczek } 1683577fe8bSPiotr Wilczek host->index = dev_id - PERIPH_ID_SDMMC0; 1693577fe8bSPiotr Wilczek 1703577fe8bSPiotr Wilczek /* Get bus width */ 1713577fe8bSPiotr Wilczek bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0); 1723577fe8bSPiotr Wilczek if (bus_width <= 0) { 1733577fe8bSPiotr Wilczek debug("MMC: Can't get bus-width\n"); 1742cb5d67cSJaehoon Chung return -EINVAL; 1753577fe8bSPiotr Wilczek } 1763577fe8bSPiotr Wilczek host->bus_width = bus_width; 1773577fe8bSPiotr Wilczek 1783577fe8bSPiotr Wilczek /* Get the base address from the device node */ 1793577fe8bSPiotr Wilczek base = fdtdec_get_addr(blob, node, "reg"); 1803577fe8bSPiotr Wilczek if (!base) { 1813577fe8bSPiotr Wilczek debug("MMC: Can't get base address\n"); 1822cb5d67cSJaehoon Chung return -EINVAL; 1833577fe8bSPiotr Wilczek } 1843577fe8bSPiotr Wilczek host->ioaddr = (void *)base; 1853577fe8bSPiotr Wilczek 1860347960bSSimon Glass gpio_request_by_name_nodev(blob, node, "pwr-gpios", 0, &host->pwr_gpio, 1870347960bSSimon Glass GPIOD_IS_OUT); 1880347960bSSimon Glass gpio_request_by_name_nodev(blob, node, "cd-gpios", 0, &host->cd_gpio, 1890347960bSSimon Glass GPIOD_IS_IN); 1903577fe8bSPiotr Wilczek 1913577fe8bSPiotr Wilczek return 0; 1923577fe8bSPiotr Wilczek } 1933577fe8bSPiotr Wilczek 1943577fe8bSPiotr Wilczek static int process_nodes(const void *blob, int node_list[], int count) 1953577fe8bSPiotr Wilczek { 1963577fe8bSPiotr Wilczek struct sdhci_host *host; 197995a54ccSTobias Jakobi int i, node, ret; 1986a9fbb6eSTobias Jakobi int failed = 0; 1993577fe8bSPiotr Wilczek 2003577fe8bSPiotr Wilczek debug("%s: count = %d\n", __func__, count); 2013577fe8bSPiotr Wilczek 2023577fe8bSPiotr Wilczek /* build sdhci_host[] for each controller */ 2033577fe8bSPiotr Wilczek for (i = 0; i < count; i++) { 2043577fe8bSPiotr Wilczek node = node_list[i]; 2053577fe8bSPiotr Wilczek if (node <= 0) 2063577fe8bSPiotr Wilczek continue; 2073577fe8bSPiotr Wilczek 2083577fe8bSPiotr Wilczek host = &sdhci_host[i]; 2093577fe8bSPiotr Wilczek 210995a54ccSTobias Jakobi ret = sdhci_get_config(blob, node, host); 211995a54ccSTobias Jakobi if (ret) { 212995a54ccSTobias Jakobi printf("%s: failed to decode dev %d (%d)\n", __func__, i, ret); 2136a9fbb6eSTobias Jakobi failed++; 2146a9fbb6eSTobias Jakobi continue; 2153577fe8bSPiotr Wilczek } 2166a9fbb6eSTobias Jakobi 217995a54ccSTobias Jakobi ret = do_sdhci_init(host); 21896094d4cSPrzemyslaw Marczak if (ret && ret != -ENODEV) { 219995a54ccSTobias Jakobi printf("%s: failed to initialize dev %d (%d)\n", __func__, i, ret); 2206a9fbb6eSTobias Jakobi failed++; 2213577fe8bSPiotr Wilczek } 2226a9fbb6eSTobias Jakobi } 2236a9fbb6eSTobias Jakobi 2246a9fbb6eSTobias Jakobi /* we only consider it an error when all nodes fail */ 2256a9fbb6eSTobias Jakobi return (failed == count ? -1 : 0); 2263577fe8bSPiotr Wilczek } 2273577fe8bSPiotr Wilczek 2283577fe8bSPiotr Wilczek int exynos_mmc_init(const void *blob) 2293577fe8bSPiotr Wilczek { 2303577fe8bSPiotr Wilczek int count; 2313577fe8bSPiotr Wilczek int node_list[SDHCI_MAX_HOSTS]; 2323577fe8bSPiotr Wilczek 2333577fe8bSPiotr Wilczek count = fdtdec_find_aliases_for_id(blob, "mmc", 2343577fe8bSPiotr Wilczek COMPAT_SAMSUNG_EXYNOS_MMC, node_list, 2353577fe8bSPiotr Wilczek SDHCI_MAX_HOSTS); 2363577fe8bSPiotr Wilczek 2376a9fbb6eSTobias Jakobi return process_nodes(blob, node_list, count); 2383577fe8bSPiotr Wilczek } 2393577fe8bSPiotr Wilczek #endif 2407aedafd6SJaehoon Chung 2417aedafd6SJaehoon Chung #ifdef CONFIG_DM_MMC 2427aedafd6SJaehoon Chung static int s5p_sdhci_probe(struct udevice *dev) 2437aedafd6SJaehoon Chung { 2447aedafd6SJaehoon Chung struct s5p_sdhci_plat *plat = dev_get_platdata(dev); 2457aedafd6SJaehoon Chung struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); 2467aedafd6SJaehoon Chung struct sdhci_host *host = dev_get_priv(dev); 2477aedafd6SJaehoon Chung int ret; 2487aedafd6SJaehoon Chung 2497aedafd6SJaehoon Chung ret = sdhci_get_config(gd->fdt_blob, dev->of_offset, host); 2507aedafd6SJaehoon Chung if (ret) 2517aedafd6SJaehoon Chung return ret; 2527aedafd6SJaehoon Chung 2537aedafd6SJaehoon Chung ret = do_sdhci_init(host); 2547aedafd6SJaehoon Chung if (ret) 2557aedafd6SJaehoon Chung return ret; 2567aedafd6SJaehoon Chung 2577aedafd6SJaehoon Chung ret = sdhci_setup_cfg(&plat->cfg, host, 52000000, 400000); 2587aedafd6SJaehoon Chung if (ret) 2597aedafd6SJaehoon Chung return ret; 2607aedafd6SJaehoon Chung 2617aedafd6SJaehoon Chung host->mmc = &plat->mmc; 2627aedafd6SJaehoon Chung host->mmc->priv = host; 2637aedafd6SJaehoon Chung host->mmc->dev = dev; 2647aedafd6SJaehoon Chung upriv->mmc = host->mmc; 2657aedafd6SJaehoon Chung 2667aedafd6SJaehoon Chung return sdhci_probe(dev); 2677aedafd6SJaehoon Chung } 2687aedafd6SJaehoon Chung 2697aedafd6SJaehoon Chung static int s5p_sdhci_bind(struct udevice *dev) 2707aedafd6SJaehoon Chung { 2717aedafd6SJaehoon Chung struct s5p_sdhci_plat *plat = dev_get_platdata(dev); 2727aedafd6SJaehoon Chung int ret; 2737aedafd6SJaehoon Chung 2747aedafd6SJaehoon Chung ret = sdhci_bind(dev, &plat->mmc, &plat->cfg); 2757aedafd6SJaehoon Chung if (ret) 2767aedafd6SJaehoon Chung return ret; 2777aedafd6SJaehoon Chung 2787aedafd6SJaehoon Chung return 0; 2797aedafd6SJaehoon Chung } 2807aedafd6SJaehoon Chung 2817aedafd6SJaehoon Chung static const struct udevice_id s5p_sdhci_ids[] = { 2827aedafd6SJaehoon Chung { .compatible = "samsung,exynos4412-sdhci"}, 2837aedafd6SJaehoon Chung { } 2847aedafd6SJaehoon Chung }; 2857aedafd6SJaehoon Chung 2867aedafd6SJaehoon Chung U_BOOT_DRIVER(s5p_sdhci_drv) = { 2877aedafd6SJaehoon Chung .name = "s5p_sdhci", 2887aedafd6SJaehoon Chung .id = UCLASS_MMC, 2897aedafd6SJaehoon Chung .of_match = s5p_sdhci_ids, 2907aedafd6SJaehoon Chung .bind = s5p_sdhci_bind, 2917aedafd6SJaehoon Chung .ops = &sdhci_ops, 2927aedafd6SJaehoon Chung .probe = s5p_sdhci_probe, 2937aedafd6SJaehoon Chung .priv_auto_alloc_size = sizeof(struct sdhci_host), 2947aedafd6SJaehoon Chung .platdata_auto_alloc_size = sizeof(struct s5p_sdhci_plat), 2957aedafd6SJaehoon Chung }; 2967aedafd6SJaehoon Chung #endif /* CONFIG_DM_MMC */ 297