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); 33442d5568SJaehoon Chung val &= SDHCI_CTRL2_SELBASECLK_SHIFT; 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 79442d5568SJaehoon Chung host->host_caps = MMC_MODE_HC; 809b8c9a3cSJaehoon Chung if (host->bus_width == 8) 81113e5dfcSJaehoon Chung host->host_caps |= MMC_MODE_8BIT; 82442d5568SJaehoon Chung 83a68aac49SJaehoon Chung return add_sdhci(host, 52000000, 400000); 84442d5568SJaehoon Chung } 853577fe8bSPiotr Wilczek 869b8c9a3cSJaehoon Chung int s5p_sdhci_init(u32 regbase, int index, int bus_width) 879b8c9a3cSJaehoon Chung { 889b8c9a3cSJaehoon Chung struct sdhci_host *host = malloc(sizeof(struct sdhci_host)); 899b8c9a3cSJaehoon Chung if (!host) { 909b8c9a3cSJaehoon Chung printf("sdhci__host malloc fail!\n"); 919b8c9a3cSJaehoon Chung return 1; 929b8c9a3cSJaehoon Chung } 939b8c9a3cSJaehoon Chung host->ioaddr = (void *)regbase; 949b8c9a3cSJaehoon Chung host->index = index; 959b8c9a3cSJaehoon Chung host->bus_width = bus_width; 969b8c9a3cSJaehoon Chung 979b8c9a3cSJaehoon Chung return s5p_sdhci_core_init(host); 989b8c9a3cSJaehoon Chung } 999b8c9a3cSJaehoon Chung 1003577fe8bSPiotr Wilczek #ifdef CONFIG_OF_CONTROL 1013577fe8bSPiotr Wilczek struct sdhci_host sdhci_host[SDHCI_MAX_HOSTS]; 1023577fe8bSPiotr Wilczek 1033577fe8bSPiotr Wilczek static int do_sdhci_init(struct sdhci_host *host) 1043577fe8bSPiotr Wilczek { 1053577fe8bSPiotr Wilczek int dev_id, flag; 1063577fe8bSPiotr Wilczek int err = 0; 1073577fe8bSPiotr Wilczek 1083577fe8bSPiotr Wilczek flag = host->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE; 1093577fe8bSPiotr Wilczek dev_id = host->index + PERIPH_ID_SDMMC0; 1103577fe8bSPiotr Wilczek 111*0347960bSSimon Glass if (dm_gpio_is_valid(&host->pwr_gpio)) { 112*0347960bSSimon Glass dm_gpio_set_value(&host->pwr_gpio, 1); 1133577fe8bSPiotr Wilczek err = exynos_pinmux_config(dev_id, flag); 1143577fe8bSPiotr Wilczek if (err) { 1153577fe8bSPiotr Wilczek debug("MMC not configured\n"); 1163577fe8bSPiotr Wilczek return err; 1173577fe8bSPiotr Wilczek } 1183577fe8bSPiotr Wilczek } 1193577fe8bSPiotr Wilczek 120*0347960bSSimon Glass if (dm_gpio_is_valid(&host->cd_gpio)) { 121*0347960bSSimon Glass if (dm_gpio_get_value(&host->cd_gpio)) 1223577fe8bSPiotr Wilczek return -ENODEV; 1233577fe8bSPiotr Wilczek 1243577fe8bSPiotr Wilczek err = exynos_pinmux_config(dev_id, flag); 1253577fe8bSPiotr Wilczek if (err) { 1263577fe8bSPiotr Wilczek printf("external SD not configured\n"); 1273577fe8bSPiotr Wilczek return err; 1283577fe8bSPiotr Wilczek } 1293577fe8bSPiotr Wilczek } 1303577fe8bSPiotr Wilczek 1319b8c9a3cSJaehoon Chung return s5p_sdhci_core_init(host); 1323577fe8bSPiotr Wilczek } 1333577fe8bSPiotr Wilczek 1343577fe8bSPiotr Wilczek static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host) 1353577fe8bSPiotr Wilczek { 1363577fe8bSPiotr Wilczek int bus_width, dev_id; 1373577fe8bSPiotr Wilczek unsigned int base; 1383577fe8bSPiotr Wilczek 1393577fe8bSPiotr Wilczek /* Get device id */ 1403577fe8bSPiotr Wilczek dev_id = pinmux_decode_periph_id(blob, node); 1413577fe8bSPiotr Wilczek if (dev_id < PERIPH_ID_SDMMC0 && dev_id > PERIPH_ID_SDMMC3) { 1423577fe8bSPiotr Wilczek debug("MMC: Can't get device id\n"); 1433577fe8bSPiotr Wilczek return -1; 1443577fe8bSPiotr Wilczek } 1453577fe8bSPiotr Wilczek host->index = dev_id - PERIPH_ID_SDMMC0; 1463577fe8bSPiotr Wilczek 1473577fe8bSPiotr Wilczek /* Get bus width */ 1483577fe8bSPiotr Wilczek bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0); 1493577fe8bSPiotr Wilczek if (bus_width <= 0) { 1503577fe8bSPiotr Wilczek debug("MMC: Can't get bus-width\n"); 1513577fe8bSPiotr Wilczek return -1; 1523577fe8bSPiotr Wilczek } 1533577fe8bSPiotr Wilczek host->bus_width = bus_width; 1543577fe8bSPiotr Wilczek 1553577fe8bSPiotr Wilczek /* Get the base address from the device node */ 1563577fe8bSPiotr Wilczek base = fdtdec_get_addr(blob, node, "reg"); 1573577fe8bSPiotr Wilczek if (!base) { 1583577fe8bSPiotr Wilczek debug("MMC: Can't get base address\n"); 1593577fe8bSPiotr Wilczek return -1; 1603577fe8bSPiotr Wilczek } 1613577fe8bSPiotr Wilczek host->ioaddr = (void *)base; 1623577fe8bSPiotr Wilczek 163*0347960bSSimon Glass gpio_request_by_name_nodev(blob, node, "pwr-gpios", 0, &host->pwr_gpio, 164*0347960bSSimon Glass GPIOD_IS_OUT); 165*0347960bSSimon Glass gpio_request_by_name_nodev(blob, node, "cd-gpios", 0, &host->cd_gpio, 166*0347960bSSimon Glass GPIOD_IS_IN); 1673577fe8bSPiotr Wilczek 1683577fe8bSPiotr Wilczek return 0; 1693577fe8bSPiotr Wilczek } 1703577fe8bSPiotr Wilczek 1713577fe8bSPiotr Wilczek static int process_nodes(const void *blob, int node_list[], int count) 1723577fe8bSPiotr Wilczek { 1733577fe8bSPiotr Wilczek struct sdhci_host *host; 1743577fe8bSPiotr Wilczek int i, node; 1753577fe8bSPiotr Wilczek 1763577fe8bSPiotr Wilczek debug("%s: count = %d\n", __func__, count); 1773577fe8bSPiotr Wilczek 1783577fe8bSPiotr Wilczek /* build sdhci_host[] for each controller */ 1793577fe8bSPiotr Wilczek for (i = 0; i < count; i++) { 1803577fe8bSPiotr Wilczek node = node_list[i]; 1813577fe8bSPiotr Wilczek if (node <= 0) 1823577fe8bSPiotr Wilczek continue; 1833577fe8bSPiotr Wilczek 1843577fe8bSPiotr Wilczek host = &sdhci_host[i]; 1853577fe8bSPiotr Wilczek 1863577fe8bSPiotr Wilczek if (sdhci_get_config(blob, node, host)) { 1873577fe8bSPiotr Wilczek printf("%s: failed to decode dev %d\n", __func__, i); 1883577fe8bSPiotr Wilczek return -1; 1893577fe8bSPiotr Wilczek } 1903577fe8bSPiotr Wilczek do_sdhci_init(host); 1913577fe8bSPiotr Wilczek } 1923577fe8bSPiotr Wilczek return 0; 1933577fe8bSPiotr Wilczek } 1943577fe8bSPiotr Wilczek 1953577fe8bSPiotr Wilczek int exynos_mmc_init(const void *blob) 1963577fe8bSPiotr Wilczek { 1973577fe8bSPiotr Wilczek int count; 1983577fe8bSPiotr Wilczek int node_list[SDHCI_MAX_HOSTS]; 1993577fe8bSPiotr Wilczek 2003577fe8bSPiotr Wilczek count = fdtdec_find_aliases_for_id(blob, "mmc", 2013577fe8bSPiotr Wilczek COMPAT_SAMSUNG_EXYNOS_MMC, node_list, 2023577fe8bSPiotr Wilczek SDHCI_MAX_HOSTS); 2033577fe8bSPiotr Wilczek 2043577fe8bSPiotr Wilczek process_nodes(blob, node_list, count); 2053577fe8bSPiotr Wilczek 2063577fe8bSPiotr Wilczek return 1; 2073577fe8bSPiotr Wilczek } 2083577fe8bSPiotr Wilczek #endif 209