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> 9442d5568SJaehoon Chung #include <malloc.h> 10442d5568SJaehoon Chung #include <sdhci.h> 113577fe8bSPiotr Wilczek #include <fdtdec.h> 123577fe8bSPiotr Wilczek #include <libfdt.h> 133577fe8bSPiotr Wilczek #include <asm/gpio.h> 14442d5568SJaehoon Chung #include <asm/arch/mmc.h> 15b09ed6e4SJaehoon Chung #include <asm/arch/clk.h> 163577fe8bSPiotr Wilczek #include <errno.h> 173577fe8bSPiotr Wilczek #include <asm/arch/pinmux.h> 18442d5568SJaehoon Chung 19442d5568SJaehoon Chung static char *S5P_NAME = "SAMSUNG SDHCI"; 20442d5568SJaehoon Chung static void s5p_sdhci_set_control_reg(struct sdhci_host *host) 21442d5568SJaehoon Chung { 22442d5568SJaehoon Chung unsigned long val, ctrl; 23442d5568SJaehoon Chung /* 24442d5568SJaehoon Chung * SELCLKPADDS[17:16] 25442d5568SJaehoon Chung * 00 = 2mA 26442d5568SJaehoon Chung * 01 = 4mA 27442d5568SJaehoon Chung * 10 = 7mA 28442d5568SJaehoon Chung * 11 = 9mA 29442d5568SJaehoon Chung */ 30442d5568SJaehoon Chung sdhci_writel(host, SDHCI_CTRL4_DRIVE_MASK(0x3), SDHCI_CONTROL4); 31442d5568SJaehoon Chung 32442d5568SJaehoon Chung val = sdhci_readl(host, SDHCI_CONTROL2); 338ebde4f0SMatt Reimer val &= SDHCI_CTRL2_SELBASECLK_MASK(3); 34442d5568SJaehoon Chung 35442d5568SJaehoon Chung val |= SDHCI_CTRL2_ENSTAASYNCCLR | 36442d5568SJaehoon Chung SDHCI_CTRL2_ENCMDCNFMSK | 37442d5568SJaehoon Chung SDHCI_CTRL2_ENFBCLKRX | 38442d5568SJaehoon Chung SDHCI_CTRL2_ENCLKOUTHOLD; 39442d5568SJaehoon Chung 40442d5568SJaehoon Chung sdhci_writel(host, val, SDHCI_CONTROL2); 41442d5568SJaehoon Chung 42442d5568SJaehoon Chung /* 43442d5568SJaehoon Chung * FCSEL3[31] FCSEL2[23] FCSEL1[15] FCSEL0[7] 44442d5568SJaehoon Chung * FCSel[1:0] : Rx Feedback Clock Delay Control 45442d5568SJaehoon Chung * Inverter delay means10ns delay if SDCLK 50MHz setting 46442d5568SJaehoon Chung * 01 = Delay1 (basic delay) 47442d5568SJaehoon Chung * 11 = Delay2 (basic delay + 2ns) 48442d5568SJaehoon Chung * 00 = Delay3 (inverter delay) 49442d5568SJaehoon Chung * 10 = Delay4 (inverter delay + 2ns) 50442d5568SJaehoon Chung */ 51b268660cSJaehoon Chung val = SDHCI_CTRL3_FCSEL0 | SDHCI_CTRL3_FCSEL1; 52442d5568SJaehoon Chung sdhci_writel(host, val, SDHCI_CONTROL3); 53442d5568SJaehoon Chung 54442d5568SJaehoon Chung /* 55442d5568SJaehoon Chung * SELBASECLK[5:4] 56442d5568SJaehoon Chung * 00/01 = HCLK 57442d5568SJaehoon Chung * 10 = EPLL 58442d5568SJaehoon Chung * 11 = XTI or XEXTCLK 59442d5568SJaehoon Chung */ 60442d5568SJaehoon Chung ctrl = sdhci_readl(host, SDHCI_CONTROL2); 61442d5568SJaehoon Chung ctrl &= ~SDHCI_CTRL2_SELBASECLK_MASK(0x3); 62442d5568SJaehoon Chung ctrl |= SDHCI_CTRL2_SELBASECLK_MASK(0x2); 63442d5568SJaehoon Chung sdhci_writel(host, ctrl, SDHCI_CONTROL2); 64442d5568SJaehoon Chung } 65442d5568SJaehoon Chung 669b8c9a3cSJaehoon Chung static int s5p_sdhci_core_init(struct sdhci_host *host) 67442d5568SJaehoon Chung { 68442d5568SJaehoon Chung host->name = S5P_NAME; 69442d5568SJaehoon Chung 70b268660cSJaehoon Chung host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE | 7113243f2eSTushar Behera SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR | 72113e5dfcSJaehoon Chung SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_USE_WIDE8; 73442d5568SJaehoon Chung host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; 74442d5568SJaehoon Chung host->version = sdhci_readw(host, SDHCI_HOST_VERSION); 75442d5568SJaehoon Chung 76442d5568SJaehoon Chung host->set_control_reg = &s5p_sdhci_set_control_reg; 77b09ed6e4SJaehoon Chung host->set_clock = set_mmc_clk; 78442d5568SJaehoon Chung 799b8c9a3cSJaehoon Chung if (host->bus_width == 8) 80113e5dfcSJaehoon Chung host->host_caps |= MMC_MODE_8BIT; 81442d5568SJaehoon Chung 82a68aac49SJaehoon Chung return add_sdhci(host, 52000000, 400000); 83442d5568SJaehoon Chung } 843577fe8bSPiotr Wilczek 859b8c9a3cSJaehoon Chung int s5p_sdhci_init(u32 regbase, int index, int bus_width) 869b8c9a3cSJaehoon Chung { 871a9d1731STobias Jakobi struct sdhci_host *host = calloc(1, sizeof(struct sdhci_host)); 889b8c9a3cSJaehoon Chung if (!host) { 891a9d1731STobias Jakobi printf("sdhci__host allocation fail!\n"); 909b8c9a3cSJaehoon Chung return 1; 919b8c9a3cSJaehoon Chung } 929b8c9a3cSJaehoon Chung host->ioaddr = (void *)regbase; 939b8c9a3cSJaehoon Chung host->index = index; 949b8c9a3cSJaehoon Chung host->bus_width = bus_width; 959b8c9a3cSJaehoon Chung 969b8c9a3cSJaehoon Chung return s5p_sdhci_core_init(host); 979b8c9a3cSJaehoon Chung } 989b8c9a3cSJaehoon Chung 990f925822SMasahiro Yamada #if CONFIG_IS_ENABLED(OF_CONTROL) 1003577fe8bSPiotr Wilczek struct sdhci_host sdhci_host[SDHCI_MAX_HOSTS]; 1013577fe8bSPiotr Wilczek 1023577fe8bSPiotr Wilczek static int do_sdhci_init(struct sdhci_host *host) 1033577fe8bSPiotr Wilczek { 104*2308ea7cSTobias Jakobi int dev_id, flag, ret; 1053577fe8bSPiotr Wilczek 1063577fe8bSPiotr Wilczek flag = host->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE; 1073577fe8bSPiotr Wilczek dev_id = host->index + PERIPH_ID_SDMMC0; 1083577fe8bSPiotr Wilczek 1090347960bSSimon Glass if (dm_gpio_is_valid(&host->pwr_gpio)) { 1100347960bSSimon Glass dm_gpio_set_value(&host->pwr_gpio, 1); 111*2308ea7cSTobias Jakobi ret = exynos_pinmux_config(dev_id, flag); 112*2308ea7cSTobias Jakobi if (ret) { 1133577fe8bSPiotr Wilczek debug("MMC not configured\n"); 114*2308ea7cSTobias Jakobi return ret; 1153577fe8bSPiotr Wilczek } 1163577fe8bSPiotr Wilczek } 1173577fe8bSPiotr Wilczek 1180347960bSSimon Glass if (dm_gpio_is_valid(&host->cd_gpio)) { 119*2308ea7cSTobias Jakobi ret = dm_gpio_get_value(&host->cd_gpio); 120*2308ea7cSTobias Jakobi if (ret) { 121*2308ea7cSTobias Jakobi debug("no SD card detected (%d)\n", ret); 1223577fe8bSPiotr Wilczek return -ENODEV; 123*2308ea7cSTobias Jakobi } 1243577fe8bSPiotr Wilczek 125*2308ea7cSTobias Jakobi ret = exynos_pinmux_config(dev_id, flag); 126*2308ea7cSTobias Jakobi if (ret) { 1273577fe8bSPiotr Wilczek printf("external SD not configured\n"); 128*2308ea7cSTobias Jakobi return ret; 1293577fe8bSPiotr Wilczek } 1303577fe8bSPiotr Wilczek } 1313577fe8bSPiotr Wilczek 1329b8c9a3cSJaehoon Chung return s5p_sdhci_core_init(host); 1333577fe8bSPiotr Wilczek } 1343577fe8bSPiotr Wilczek 1353577fe8bSPiotr Wilczek static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host) 1363577fe8bSPiotr Wilczek { 1373577fe8bSPiotr Wilczek int bus_width, dev_id; 1383577fe8bSPiotr Wilczek unsigned int base; 1393577fe8bSPiotr Wilczek 1403577fe8bSPiotr Wilczek /* Get device id */ 1413577fe8bSPiotr Wilczek dev_id = pinmux_decode_periph_id(blob, node); 1423577fe8bSPiotr Wilczek if (dev_id < PERIPH_ID_SDMMC0 && dev_id > PERIPH_ID_SDMMC3) { 1433577fe8bSPiotr Wilczek debug("MMC: Can't get device id\n"); 1443577fe8bSPiotr Wilczek return -1; 1453577fe8bSPiotr Wilczek } 1463577fe8bSPiotr Wilczek host->index = dev_id - PERIPH_ID_SDMMC0; 1473577fe8bSPiotr Wilczek 1483577fe8bSPiotr Wilczek /* Get bus width */ 1493577fe8bSPiotr Wilczek bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0); 1503577fe8bSPiotr Wilczek if (bus_width <= 0) { 1513577fe8bSPiotr Wilczek debug("MMC: Can't get bus-width\n"); 1523577fe8bSPiotr Wilczek return -1; 1533577fe8bSPiotr Wilczek } 1543577fe8bSPiotr Wilczek host->bus_width = bus_width; 1553577fe8bSPiotr Wilczek 1563577fe8bSPiotr Wilczek /* Get the base address from the device node */ 1573577fe8bSPiotr Wilczek base = fdtdec_get_addr(blob, node, "reg"); 1583577fe8bSPiotr Wilczek if (!base) { 1593577fe8bSPiotr Wilczek debug("MMC: Can't get base address\n"); 1603577fe8bSPiotr Wilczek return -1; 1613577fe8bSPiotr Wilczek } 1623577fe8bSPiotr Wilczek host->ioaddr = (void *)base; 1633577fe8bSPiotr Wilczek 1640347960bSSimon Glass gpio_request_by_name_nodev(blob, node, "pwr-gpios", 0, &host->pwr_gpio, 1650347960bSSimon Glass GPIOD_IS_OUT); 1660347960bSSimon Glass gpio_request_by_name_nodev(blob, node, "cd-gpios", 0, &host->cd_gpio, 1670347960bSSimon Glass GPIOD_IS_IN); 1683577fe8bSPiotr Wilczek 1693577fe8bSPiotr Wilczek return 0; 1703577fe8bSPiotr Wilczek } 1713577fe8bSPiotr Wilczek 1723577fe8bSPiotr Wilczek static int process_nodes(const void *blob, int node_list[], int count) 1733577fe8bSPiotr Wilczek { 1743577fe8bSPiotr Wilczek struct sdhci_host *host; 175995a54ccSTobias Jakobi int i, node, ret; 1766a9fbb6eSTobias Jakobi int failed = 0; 1773577fe8bSPiotr Wilczek 1783577fe8bSPiotr Wilczek debug("%s: count = %d\n", __func__, count); 1793577fe8bSPiotr Wilczek 1803577fe8bSPiotr Wilczek /* build sdhci_host[] for each controller */ 1813577fe8bSPiotr Wilczek for (i = 0; i < count; i++) { 1823577fe8bSPiotr Wilczek node = node_list[i]; 1833577fe8bSPiotr Wilczek if (node <= 0) 1843577fe8bSPiotr Wilczek continue; 1853577fe8bSPiotr Wilczek 1863577fe8bSPiotr Wilczek host = &sdhci_host[i]; 1873577fe8bSPiotr Wilczek 188995a54ccSTobias Jakobi ret = sdhci_get_config(blob, node, host); 189995a54ccSTobias Jakobi if (ret) { 190995a54ccSTobias Jakobi printf("%s: failed to decode dev %d (%d)\n", __func__, i, ret); 1916a9fbb6eSTobias Jakobi failed++; 1926a9fbb6eSTobias Jakobi continue; 1933577fe8bSPiotr Wilczek } 1946a9fbb6eSTobias Jakobi 195995a54ccSTobias Jakobi ret = do_sdhci_init(host); 196995a54ccSTobias Jakobi if (ret) { 197995a54ccSTobias Jakobi printf("%s: failed to initialize dev %d (%d)\n", __func__, i, ret); 1986a9fbb6eSTobias Jakobi failed++; 1993577fe8bSPiotr Wilczek } 2006a9fbb6eSTobias Jakobi } 2016a9fbb6eSTobias Jakobi 2026a9fbb6eSTobias Jakobi /* we only consider it an error when all nodes fail */ 2036a9fbb6eSTobias Jakobi return (failed == count ? -1 : 0); 2043577fe8bSPiotr Wilczek } 2053577fe8bSPiotr Wilczek 2063577fe8bSPiotr Wilczek int exynos_mmc_init(const void *blob) 2073577fe8bSPiotr Wilczek { 2083577fe8bSPiotr Wilczek int count; 2093577fe8bSPiotr Wilczek int node_list[SDHCI_MAX_HOSTS]; 2103577fe8bSPiotr Wilczek 2113577fe8bSPiotr Wilczek count = fdtdec_find_aliases_for_id(blob, "mmc", 2123577fe8bSPiotr Wilczek COMPAT_SAMSUNG_EXYNOS_MMC, node_list, 2133577fe8bSPiotr Wilczek SDHCI_MAX_HOSTS); 2143577fe8bSPiotr Wilczek 2156a9fbb6eSTobias Jakobi return process_nodes(blob, node_list, count); 2163577fe8bSPiotr Wilczek } 2173577fe8bSPiotr Wilczek #endif 218