1 /* 2 * Copyright (C) 2016 Socionext Inc. 3 * Author: Masahiro Yamada <yamada.masahiro@socionext.com> 4 * 5 * SPDX-License-Identifier: GPL-2.0+ 6 */ 7 8 #include <common.h> 9 #include <linux/io.h> 10 #include <linux/sizes.h> 11 #include <dm/device.h> 12 #include <mmc.h> 13 #include <sdhci.h> 14 15 /* HRS - Host Register Set (specific to Cadence) */ 16 #define SDHCI_CDNS_HRS04 0x10 /* PHY access port */ 17 #define SDHCI_CDNS_HRS04_ACK BIT(26) 18 #define SDHCI_CDNS_HRS04_RD BIT(25) 19 #define SDHCI_CDNS_HRS04_WR BIT(24) 20 #define SDHCI_CDNS_HRS04_RDATA_SHIFT 12 21 #define SDHCI_CDNS_HRS04_WDATA_SHIFT 8 22 #define SDHCI_CDNS_HRS04_ADDR_SHIFT 0 23 24 /* SRS - Slot Register Set (SDHCI-compatible) */ 25 #define SDHCI_CDNS_SRS_BASE 0x200 26 27 /* PHY */ 28 #define SDHCI_CDNS_PHY_DLY_SD_HS 0x00 29 #define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01 30 #define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02 31 #define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03 32 #define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04 33 #define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05 34 #define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06 35 #define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07 36 #define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08 37 38 struct sdhci_cdns_plat { 39 struct mmc_config cfg; 40 struct mmc mmc; 41 void __iomem *hrs_addr; 42 }; 43 44 static void sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat *plat, 45 u8 addr, u8 data) 46 { 47 void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS04; 48 u32 tmp; 49 50 tmp = (data << SDHCI_CDNS_HRS04_WDATA_SHIFT) | 51 (addr << SDHCI_CDNS_HRS04_ADDR_SHIFT); 52 writel(tmp, reg); 53 54 tmp |= SDHCI_CDNS_HRS04_WR; 55 writel(tmp, reg); 56 57 tmp &= ~SDHCI_CDNS_HRS04_WR; 58 writel(tmp, reg); 59 } 60 61 static void sdhci_cdns_phy_init(struct sdhci_cdns_plat *plat) 62 { 63 sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_SD_HS, 4); 64 sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_SD_DEFAULT, 4); 65 sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_EMMC_LEGACY, 9); 66 sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_EMMC_SDR, 2); 67 sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_EMMC_DDR, 3); 68 } 69 70 static int sdhci_cdns_bind(struct udevice *dev) 71 { 72 struct sdhci_cdns_plat *plat = dev_get_platdata(dev); 73 74 return sdhci_bind(dev, &plat->mmc, &plat->cfg); 75 } 76 77 static int sdhci_cdns_probe(struct udevice *dev) 78 { 79 struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); 80 struct sdhci_cdns_plat *plat = dev_get_platdata(dev); 81 struct sdhci_host *host = dev_get_priv(dev); 82 fdt_addr_t base; 83 int ret; 84 85 base = dev_get_addr(dev); 86 if (base == FDT_ADDR_T_NONE) 87 return -EINVAL; 88 89 plat->hrs_addr = devm_ioremap(dev, base, SZ_1K); 90 if (!plat->hrs_addr) 91 return -ENOMEM; 92 93 host->name = dev->name; 94 host->ioaddr = plat->hrs_addr + SDHCI_CDNS_SRS_BASE; 95 host->quirks |= SDHCI_QUIRK_WAIT_SEND_CMD; 96 97 sdhci_cdns_phy_init(plat); 98 99 ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0); 100 if (ret) 101 return ret; 102 103 upriv->mmc = &plat->mmc; 104 host->mmc = &plat->mmc; 105 host->mmc->priv = host; 106 107 return sdhci_probe(dev); 108 } 109 110 static const struct udevice_id sdhci_cdns_match[] = { 111 { .compatible = "socionext,uniphier-sd4hc" }, 112 { .compatible = "cdns,sd4hc" }, 113 { /* sentinel */ } 114 }; 115 116 U_BOOT_DRIVER(sdhci_cdns) = { 117 .name = "sdhci-cdns", 118 .id = UCLASS_MMC, 119 .of_match = sdhci_cdns_match, 120 .bind = sdhci_cdns_bind, 121 .probe = sdhci_cdns_probe, 122 .priv_auto_alloc_size = sizeof(struct sdhci_host), 123 .platdata_auto_alloc_size = sizeof(struct sdhci_cdns_plat), 124 .ops = &sdhci_ops, 125 }; 126