1e5e7a7c2SMasahiro Yamada /* 2e5e7a7c2SMasahiro Yamada * Copyright (C) 2016 Socionext Inc. 3e5e7a7c2SMasahiro Yamada * Author: Masahiro Yamada <yamada.masahiro@socionext.com> 4e5e7a7c2SMasahiro Yamada * 5e5e7a7c2SMasahiro Yamada * SPDX-License-Identifier: GPL-2.0+ 6e5e7a7c2SMasahiro Yamada */ 7e5e7a7c2SMasahiro Yamada 8e5e7a7c2SMasahiro Yamada #include <common.h> 99d922450SSimon Glass #include <dm.h> 10e5e7a7c2SMasahiro Yamada #include <linux/io.h> 110cacd6b7SMasahiro Yamada #include <linux/iopoll.h> 12e5e7a7c2SMasahiro Yamada #include <linux/sizes.h> 130cacd6b7SMasahiro Yamada #include <libfdt.h> 14e5e7a7c2SMasahiro Yamada #include <mmc.h> 15e5e7a7c2SMasahiro Yamada #include <sdhci.h> 16e5e7a7c2SMasahiro Yamada 17e5e7a7c2SMasahiro Yamada /* HRS - Host Register Set (specific to Cadence) */ 18e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_HRS04 0x10 /* PHY access port */ 19e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_HRS04_ACK BIT(26) 20e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_HRS04_RD BIT(25) 21e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_HRS04_WR BIT(24) 220cacd6b7SMasahiro Yamada #define SDHCI_CDNS_HRS04_RDATA_SHIFT 16 23e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_HRS04_WDATA_SHIFT 8 24e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_HRS04_ADDR_SHIFT 0 25e5e7a7c2SMasahiro Yamada 26e5e7a7c2SMasahiro Yamada /* SRS - Slot Register Set (SDHCI-compatible) */ 27e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_SRS_BASE 0x200 28e5e7a7c2SMasahiro Yamada 29e5e7a7c2SMasahiro Yamada /* PHY */ 30e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_SD_HS 0x00 31e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01 32e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02 33e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03 34e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04 35e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05 36e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06 37e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07 38e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08 390cacd6b7SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_SDCLK 0x0b 400cacd6b7SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c 410cacd6b7SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_STROBE 0x0d 42e5e7a7c2SMasahiro Yamada 43e5e7a7c2SMasahiro Yamada struct sdhci_cdns_plat { 44e5e7a7c2SMasahiro Yamada struct mmc_config cfg; 45e5e7a7c2SMasahiro Yamada struct mmc mmc; 46e5e7a7c2SMasahiro Yamada void __iomem *hrs_addr; 47e5e7a7c2SMasahiro Yamada }; 48e5e7a7c2SMasahiro Yamada 490cacd6b7SMasahiro Yamada struct sdhci_cdns_phy_cfg { 500cacd6b7SMasahiro Yamada const char *property; 510cacd6b7SMasahiro Yamada u8 addr; 520cacd6b7SMasahiro Yamada }; 530cacd6b7SMasahiro Yamada 540cacd6b7SMasahiro Yamada static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = { 550cacd6b7SMasahiro Yamada { "cdns,phy-input-delay-sd-highspeed", SDHCI_CDNS_PHY_DLY_SD_HS, }, 560cacd6b7SMasahiro Yamada { "cdns,phy-input-delay-legacy", SDHCI_CDNS_PHY_DLY_SD_DEFAULT, }, 570cacd6b7SMasahiro Yamada { "cdns,phy-input-delay-sd-uhs-sdr12", SDHCI_CDNS_PHY_DLY_UHS_SDR12, }, 580cacd6b7SMasahiro Yamada { "cdns,phy-input-delay-sd-uhs-sdr25", SDHCI_CDNS_PHY_DLY_UHS_SDR25, }, 590cacd6b7SMasahiro Yamada { "cdns,phy-input-delay-sd-uhs-sdr50", SDHCI_CDNS_PHY_DLY_UHS_SDR50, }, 600cacd6b7SMasahiro Yamada { "cdns,phy-input-delay-sd-uhs-ddr50", SDHCI_CDNS_PHY_DLY_UHS_DDR50, }, 610cacd6b7SMasahiro Yamada { "cdns,phy-input-delay-mmc-highspeed", SDHCI_CDNS_PHY_DLY_EMMC_SDR, }, 620cacd6b7SMasahiro Yamada { "cdns,phy-input-delay-mmc-ddr", SDHCI_CDNS_PHY_DLY_EMMC_DDR, }, 630cacd6b7SMasahiro Yamada { "cdns,phy-dll-delay-sdclk", SDHCI_CDNS_PHY_DLY_SDCLK, }, 640cacd6b7SMasahiro Yamada { "cdns,phy-dll-delay-sdclk-hsmmc", SDHCI_CDNS_PHY_DLY_HSMMC, }, 650cacd6b7SMasahiro Yamada { "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE, }, 660cacd6b7SMasahiro Yamada }; 670cacd6b7SMasahiro Yamada 680cacd6b7SMasahiro Yamada static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat *plat, 69e5e7a7c2SMasahiro Yamada u8 addr, u8 data) 70e5e7a7c2SMasahiro Yamada { 71e5e7a7c2SMasahiro Yamada void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS04; 72e5e7a7c2SMasahiro Yamada u32 tmp; 730cacd6b7SMasahiro Yamada int ret; 74e5e7a7c2SMasahiro Yamada 75e5e7a7c2SMasahiro Yamada tmp = (data << SDHCI_CDNS_HRS04_WDATA_SHIFT) | 76e5e7a7c2SMasahiro Yamada (addr << SDHCI_CDNS_HRS04_ADDR_SHIFT); 77e5e7a7c2SMasahiro Yamada writel(tmp, reg); 78e5e7a7c2SMasahiro Yamada 79e5e7a7c2SMasahiro Yamada tmp |= SDHCI_CDNS_HRS04_WR; 80e5e7a7c2SMasahiro Yamada writel(tmp, reg); 81e5e7a7c2SMasahiro Yamada 820cacd6b7SMasahiro Yamada ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_HRS04_ACK, 10); 830cacd6b7SMasahiro Yamada if (ret) 840cacd6b7SMasahiro Yamada return ret; 850cacd6b7SMasahiro Yamada 86e5e7a7c2SMasahiro Yamada tmp &= ~SDHCI_CDNS_HRS04_WR; 87e5e7a7c2SMasahiro Yamada writel(tmp, reg); 880cacd6b7SMasahiro Yamada 890cacd6b7SMasahiro Yamada return 0; 90e5e7a7c2SMasahiro Yamada } 91e5e7a7c2SMasahiro Yamada 920cacd6b7SMasahiro Yamada static int sdhci_cdns_phy_init(struct sdhci_cdns_plat *plat, 930cacd6b7SMasahiro Yamada const void *fdt, int nodeoffset) 94e5e7a7c2SMasahiro Yamada { 950cacd6b7SMasahiro Yamada const u32 *prop; 960cacd6b7SMasahiro Yamada int ret, i; 970cacd6b7SMasahiro Yamada 980cacd6b7SMasahiro Yamada for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) { 990cacd6b7SMasahiro Yamada prop = fdt_getprop(fdt, nodeoffset, 1000cacd6b7SMasahiro Yamada sdhci_cdns_phy_cfgs[i].property, NULL); 1010cacd6b7SMasahiro Yamada if (!prop) 1020cacd6b7SMasahiro Yamada continue; 1030cacd6b7SMasahiro Yamada 1040cacd6b7SMasahiro Yamada ret = sdhci_cdns_write_phy_reg(plat, 1050cacd6b7SMasahiro Yamada sdhci_cdns_phy_cfgs[i].addr, 1060cacd6b7SMasahiro Yamada fdt32_to_cpu(*prop)); 1070cacd6b7SMasahiro Yamada if (ret) 1080cacd6b7SMasahiro Yamada return ret; 1090cacd6b7SMasahiro Yamada } 1100cacd6b7SMasahiro Yamada 1110cacd6b7SMasahiro Yamada return 0; 112e5e7a7c2SMasahiro Yamada } 113e5e7a7c2SMasahiro Yamada 114e5e7a7c2SMasahiro Yamada static int sdhci_cdns_bind(struct udevice *dev) 115e5e7a7c2SMasahiro Yamada { 116e5e7a7c2SMasahiro Yamada struct sdhci_cdns_plat *plat = dev_get_platdata(dev); 117e5e7a7c2SMasahiro Yamada 118e5e7a7c2SMasahiro Yamada return sdhci_bind(dev, &plat->mmc, &plat->cfg); 119e5e7a7c2SMasahiro Yamada } 120e5e7a7c2SMasahiro Yamada 121e5e7a7c2SMasahiro Yamada static int sdhci_cdns_probe(struct udevice *dev) 122e5e7a7c2SMasahiro Yamada { 1230cacd6b7SMasahiro Yamada DECLARE_GLOBAL_DATA_PTR; 124e5e7a7c2SMasahiro Yamada struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); 125e5e7a7c2SMasahiro Yamada struct sdhci_cdns_plat *plat = dev_get_platdata(dev); 126e5e7a7c2SMasahiro Yamada struct sdhci_host *host = dev_get_priv(dev); 127e5e7a7c2SMasahiro Yamada fdt_addr_t base; 128e5e7a7c2SMasahiro Yamada int ret; 129e5e7a7c2SMasahiro Yamada 130a821c4afSSimon Glass base = devfdt_get_addr(dev); 131e5e7a7c2SMasahiro Yamada if (base == FDT_ADDR_T_NONE) 132e5e7a7c2SMasahiro Yamada return -EINVAL; 133e5e7a7c2SMasahiro Yamada 134e5e7a7c2SMasahiro Yamada plat->hrs_addr = devm_ioremap(dev, base, SZ_1K); 135e5e7a7c2SMasahiro Yamada if (!plat->hrs_addr) 136e5e7a7c2SMasahiro Yamada return -ENOMEM; 137e5e7a7c2SMasahiro Yamada 138e5e7a7c2SMasahiro Yamada host->name = dev->name; 139e5e7a7c2SMasahiro Yamada host->ioaddr = plat->hrs_addr + SDHCI_CDNS_SRS_BASE; 140e5e7a7c2SMasahiro Yamada host->quirks |= SDHCI_QUIRK_WAIT_SEND_CMD; 141e5e7a7c2SMasahiro Yamada 142*da409cccSSimon Glass ret = sdhci_cdns_phy_init(plat, gd->fdt_blob, dev_of_offset(dev)); 1430cacd6b7SMasahiro Yamada if (ret) 1440cacd6b7SMasahiro Yamada return ret; 145e5e7a7c2SMasahiro Yamada 146e5e7a7c2SMasahiro Yamada ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0); 147e5e7a7c2SMasahiro Yamada if (ret) 148e5e7a7c2SMasahiro Yamada return ret; 149e5e7a7c2SMasahiro Yamada 150e5e7a7c2SMasahiro Yamada upriv->mmc = &plat->mmc; 151e5e7a7c2SMasahiro Yamada host->mmc = &plat->mmc; 152e5e7a7c2SMasahiro Yamada host->mmc->priv = host; 153e5e7a7c2SMasahiro Yamada 154e5e7a7c2SMasahiro Yamada return sdhci_probe(dev); 155e5e7a7c2SMasahiro Yamada } 156e5e7a7c2SMasahiro Yamada 157e5e7a7c2SMasahiro Yamada static const struct udevice_id sdhci_cdns_match[] = { 158e5e7a7c2SMasahiro Yamada { .compatible = "socionext,uniphier-sd4hc" }, 159e5e7a7c2SMasahiro Yamada { .compatible = "cdns,sd4hc" }, 160e5e7a7c2SMasahiro Yamada { /* sentinel */ } 161e5e7a7c2SMasahiro Yamada }; 162e5e7a7c2SMasahiro Yamada 163e5e7a7c2SMasahiro Yamada U_BOOT_DRIVER(sdhci_cdns) = { 164e5e7a7c2SMasahiro Yamada .name = "sdhci-cdns", 165e5e7a7c2SMasahiro Yamada .id = UCLASS_MMC, 166e5e7a7c2SMasahiro Yamada .of_match = sdhci_cdns_match, 167e5e7a7c2SMasahiro Yamada .bind = sdhci_cdns_bind, 168e5e7a7c2SMasahiro Yamada .probe = sdhci_cdns_probe, 169e5e7a7c2SMasahiro Yamada .priv_auto_alloc_size = sizeof(struct sdhci_host), 170e5e7a7c2SMasahiro Yamada .platdata_auto_alloc_size = sizeof(struct sdhci_cdns_plat), 171e5e7a7c2SMasahiro Yamada .ops = &sdhci_ops, 172e5e7a7c2SMasahiro Yamada }; 173