1a8cb4fb5SSimon Glass /* 2a8cb4fb5SSimon Glass * Copyright (c) 2013 Google, Inc 3a8cb4fb5SSimon Glass * 4a8cb4fb5SSimon Glass * SPDX-License-Identifier: GPL-2.0+ 5a8cb4fb5SSimon Glass */ 6a8cb4fb5SSimon Glass 7a8cb4fb5SSimon Glass #include <common.h> 8a8cb4fb5SSimon Glass #include <clk.h> 9a8cb4fb5SSimon Glass #include <dm.h> 10bfeb443eSSimon Glass #include <dt-structs.h> 11a8cb4fb5SSimon Glass #include <dwmmc.h> 12a8cb4fb5SSimon Glass #include <errno.h> 13bfeb443eSSimon Glass #include <mapmem.h> 14e1efec4eSSimon Glass #include <pwrseq.h> 15a8cb4fb5SSimon Glass #include <syscon.h> 16e1efec4eSSimon Glass #include <asm/gpio.h> 17a8cb4fb5SSimon Glass #include <asm/arch/clock.h> 18a8cb4fb5SSimon Glass #include <asm/arch/periph.h> 19a8cb4fb5SSimon Glass #include <linux/err.h> 20a8cb4fb5SSimon Glass 21a8cb4fb5SSimon Glass DECLARE_GLOBAL_DATA_PTR; 22a8cb4fb5SSimon Glass 23f6e41d17SSimon Glass struct rockchip_mmc_plat { 24bfeb443eSSimon Glass #if CONFIG_IS_ENABLED(OF_PLATDATA) 25bfeb443eSSimon Glass struct dtd_rockchip_rk3288_dw_mshc dtplat; 26bfeb443eSSimon Glass #endif 27f6e41d17SSimon Glass struct mmc_config cfg; 28f6e41d17SSimon Glass struct mmc mmc; 29f6e41d17SSimon Glass }; 30f6e41d17SSimon Glass 31a8cb4fb5SSimon Glass struct rockchip_dwmmc_priv { 32135aa950SStephen Warren struct clk clk; 334455fdd9SZiyuan Xu struct clk sample_clk; 34a8cb4fb5SSimon Glass struct dwmci_host host; 356809b04fSSimon Glass int fifo_depth; 366809b04fSSimon Glass bool fifo_mode; 376809b04fSSimon Glass u32 minmax[2]; 38a8cb4fb5SSimon Glass }; 39a8cb4fb5SSimon Glass 40*1a0c3c4dSJason Zhu #ifdef CONFIG_USING_KERNEL_DTB 41*1a0c3c4dSJason Zhu int board_mmc_dm_reinit(struct udevice *dev) 42*1a0c3c4dSJason Zhu { 43*1a0c3c4dSJason Zhu struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); 44*1a0c3c4dSJason Zhu 45*1a0c3c4dSJason Zhu if (!priv || !&priv->clk) 46*1a0c3c4dSJason Zhu return 0; 47*1a0c3c4dSJason Zhu 48*1a0c3c4dSJason Zhu if (!memcmp(dev->name, "dwmmc", strlen("dwmmc"))) 49*1a0c3c4dSJason Zhu return clk_get_by_index(dev, 0, &priv->clk); 50*1a0c3c4dSJason Zhu else 51*1a0c3c4dSJason Zhu return 0; 52*1a0c3c4dSJason Zhu } 53*1a0c3c4dSJason Zhu #endif 54*1a0c3c4dSJason Zhu 55ace0ade6SJason Zhu #ifdef CONFIG_SPL_BUILD 56ace0ade6SJason Zhu __weak void mmc_gpio_init_direct(void) {} 57ace0ade6SJason Zhu #endif 58ace0ade6SJason Zhu 59a8cb4fb5SSimon Glass static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq) 60a8cb4fb5SSimon Glass { 61a8cb4fb5SSimon Glass struct udevice *dev = host->priv; 62a8cb4fb5SSimon Glass struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); 63a8cb4fb5SSimon Glass int ret; 64a8cb4fb5SSimon Glass 6524527ef9SZiyuan Xu /* 6624527ef9SZiyuan Xu * If DDR52 8bit mode(only emmc work in 8bit mode), 6724527ef9SZiyuan Xu * divider must be set 1 6824527ef9SZiyuan Xu */ 6924527ef9SZiyuan Xu if (mmc_card_ddr52(host->mmc) && host->mmc->bus_width == 8) 7024527ef9SZiyuan Xu freq *= 2; 7124527ef9SZiyuan Xu 72135aa950SStephen Warren ret = clk_set_rate(&priv->clk, freq); 73a8cb4fb5SSimon Glass if (ret < 0) { 74419b0801SKever Yang debug("%s: err=%d\n", __func__, ret); 75a8cb4fb5SSimon Glass return ret; 76a8cb4fb5SSimon Glass } 77a8cb4fb5SSimon Glass 78a8cb4fb5SSimon Glass return freq; 79a8cb4fb5SSimon Glass } 80a8cb4fb5SSimon Glass 81a8cb4fb5SSimon Glass static int rockchip_dwmmc_ofdata_to_platdata(struct udevice *dev) 82a8cb4fb5SSimon Glass { 83bfeb443eSSimon Glass #if !CONFIG_IS_ENABLED(OF_PLATDATA) 84a8cb4fb5SSimon Glass struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); 85a8cb4fb5SSimon Glass struct dwmci_host *host = &priv->host; 86a8cb4fb5SSimon Glass 87a8cb4fb5SSimon Glass host->name = dev->name; 881c23e4e1SPhilipp Tomsich host->ioaddr = dev_read_addr_ptr(dev); 89fd1bf8dfSPhilipp Tomsich host->buswidth = dev_read_u32_default(dev, "bus-width", 4); 90a8cb4fb5SSimon Glass host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk; 91a8cb4fb5SSimon Glass host->priv = dev; 92a8cb4fb5SSimon Glass 93ace2198bShuang lin /* use non-removeable as sdcard and emmc as judgement */ 94fd1bf8dfSPhilipp Tomsich if (dev_read_bool(dev, "non-removable")) 956579385bShuang lin host->dev_index = 0; 966579385bShuang lin else 97ace2198bShuang lin host->dev_index = 1; 98a8cb4fb5SSimon Glass 99fd1bf8dfSPhilipp Tomsich priv->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0); 100fd1bf8dfSPhilipp Tomsich 1016809b04fSSimon Glass if (priv->fifo_depth < 0) 1026809b04fSSimon Glass return -EINVAL; 103fd1bf8dfSPhilipp Tomsich priv->fifo_mode = dev_read_bool(dev, "fifo-mode"); 104ff71f9acSPhilipp Tomsich 105ff71f9acSPhilipp Tomsich /* 106ff71f9acSPhilipp Tomsich * 'clock-freq-min-max' is deprecated 107ff71f9acSPhilipp Tomsich * (see https://github.com/torvalds/linux/commit/b023030f10573de738bbe8df63d43acab64c9f7b) 108ff71f9acSPhilipp Tomsich */ 109fd1bf8dfSPhilipp Tomsich if (dev_read_u32_array(dev, "clock-freq-min-max", priv->minmax, 2)) { 110fd1bf8dfSPhilipp Tomsich int val = dev_read_u32_default(dev, "max-frequency", -EINVAL); 111ff71f9acSPhilipp Tomsich 112ff71f9acSPhilipp Tomsich if (val < 0) 113ff71f9acSPhilipp Tomsich return val; 114ff71f9acSPhilipp Tomsich 115ff71f9acSPhilipp Tomsich priv->minmax[0] = 400000; /* 400 kHz */ 116ff71f9acSPhilipp Tomsich priv->minmax[1] = val; 117ff71f9acSPhilipp Tomsich } else { 118ff71f9acSPhilipp Tomsich debug("%s: 'clock-freq-min-max' property was deprecated.\n", 119ff71f9acSPhilipp Tomsich __func__); 120ff71f9acSPhilipp Tomsich } 121bfeb443eSSimon Glass #endif 122a8cb4fb5SSimon Glass return 0; 123a8cb4fb5SSimon Glass } 124a8cb4fb5SSimon Glass 1254455fdd9SZiyuan Xu static int rockchip_dwmmc_execute_tuning(struct dwmci_host *host, u32 opcode) 1264455fdd9SZiyuan Xu { 1274a6b8656SJason Zhu int i = 0; 1284a6b8656SJason Zhu int ret = -1; 1294a6b8656SJason Zhu struct mmc *mmc = host->mmc; 1304455fdd9SZiyuan Xu struct udevice *dev = host->priv; 1314455fdd9SZiyuan Xu struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); 1324455fdd9SZiyuan Xu 1334455fdd9SZiyuan Xu if (IS_ERR(&priv->sample_clk)) 1344455fdd9SZiyuan Xu return -EIO; 1354455fdd9SZiyuan Xu 1364a6b8656SJason Zhu if (mmc->default_phase > 0 && mmc->default_phase < 360) { 1374a6b8656SJason Zhu ret = clk_set_phase(&priv->sample_clk, mmc->default_phase); 1384a6b8656SJason Zhu if (ret) 1394a6b8656SJason Zhu printf("set clk phase fail\n"); 1404a6b8656SJason Zhu else 1414a6b8656SJason Zhu ret = mmc_send_tuning(mmc, opcode); 1424a6b8656SJason Zhu mmc->default_phase = 0; 1434455fdd9SZiyuan Xu } 1444455fdd9SZiyuan Xu /* 1454a6b8656SJason Zhu * If use default_phase to tune successfully, return. 1464a6b8656SJason Zhu * Otherwise, use the othe phase to tune. 1474455fdd9SZiyuan Xu */ 1484a6b8656SJason Zhu if (!ret) 1494a6b8656SJason Zhu return ret; 1504455fdd9SZiyuan Xu 1514a6b8656SJason Zhu for (i = 0; i < 5; i++) { 1524a6b8656SJason Zhu /* mmc->init_retry must be 0, 1, 2, 3 */ 1534a6b8656SJason Zhu if (mmc->init_retry == 4) 1544a6b8656SJason Zhu mmc->init_retry = 0; 1554a6b8656SJason Zhu 1564a6b8656SJason Zhu ret = clk_set_phase(&priv->sample_clk, 90 * mmc->init_retry); 1574a6b8656SJason Zhu if (ret) { 1584a6b8656SJason Zhu printf("set clk phase fail\n"); 1594a6b8656SJason Zhu break; 1604a6b8656SJason Zhu } 1614a6b8656SJason Zhu ret = mmc_send_tuning(mmc, opcode); 1624a6b8656SJason Zhu debug("Tuning phase is %d, ret is %d\n", mmc->init_retry * 90, ret); 1634a6b8656SJason Zhu mmc->init_retry++; 1644a6b8656SJason Zhu if (!ret) 1654a6b8656SJason Zhu break; 1664455fdd9SZiyuan Xu } 1674455fdd9SZiyuan Xu 1684455fdd9SZiyuan Xu return ret; 1694455fdd9SZiyuan Xu } 1704455fdd9SZiyuan Xu 171a8cb4fb5SSimon Glass static int rockchip_dwmmc_probe(struct udevice *dev) 172a8cb4fb5SSimon Glass { 173f6e41d17SSimon Glass struct rockchip_mmc_plat *plat = dev_get_platdata(dev); 174a8cb4fb5SSimon Glass struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); 175a8cb4fb5SSimon Glass struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); 176a8cb4fb5SSimon Glass struct dwmci_host *host = &priv->host; 177e1efec4eSSimon Glass struct udevice *pwr_dev __maybe_unused; 178a8cb4fb5SSimon Glass int ret; 179a8cb4fb5SSimon Glass 180ace0ade6SJason Zhu #ifdef CONFIG_SPL_BUILD 181ace0ade6SJason Zhu mmc_gpio_init_direct(); 182ace0ade6SJason Zhu #endif 183bfeb443eSSimon Glass #if CONFIG_IS_ENABLED(OF_PLATDATA) 184bfeb443eSSimon Glass struct dtd_rockchip_rk3288_dw_mshc *dtplat = &plat->dtplat; 185bfeb443eSSimon Glass 186bfeb443eSSimon Glass host->name = dev->name; 187bfeb443eSSimon Glass host->ioaddr = map_sysmem(dtplat->reg[0], dtplat->reg[1]); 188bfeb443eSSimon Glass host->buswidth = dtplat->bus_width; 189bfeb443eSSimon Glass host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk; 1904455fdd9SZiyuan Xu host->execute_tuning = rockchip_dwmmc_execute_tuning; 191bfeb443eSSimon Glass host->priv = dev; 192bfeb443eSSimon Glass host->dev_index = 0; 193bfeb443eSSimon Glass priv->fifo_depth = dtplat->fifo_depth; 194bfeb443eSSimon Glass priv->fifo_mode = 0; 19580935298SKever Yang priv->minmax[0] = 400000; /* 400 kHz */ 19680935298SKever Yang priv->minmax[1] = dtplat->max_frequency; 197bfeb443eSSimon Glass 198bfeb443eSSimon Glass ret = clk_get_by_index_platdata(dev, 0, dtplat->clocks, &priv->clk); 199bfeb443eSSimon Glass if (ret < 0) 200bfeb443eSSimon Glass return ret; 201bfeb443eSSimon Glass #else 202419b0801SKever Yang ret = clk_get_by_index(dev, 0, &priv->clk); 203898d6439SSimon Glass if (ret < 0) 204a8cb4fb5SSimon Glass return ret; 20508c9dc10SJason Zhu 20608c9dc10SJason Zhu ret = clk_get_by_name(dev, "ciu-sample", &priv->sample_clk); 20708c9dc10SJason Zhu if (ret < 0) 2081e72694fSKever Yang debug("MMC: sample clock not found, not support hs200!\n"); 2094455fdd9SZiyuan Xu host->execute_tuning = rockchip_dwmmc_execute_tuning; 210bfeb443eSSimon Glass #endif 2115ef89808SJason Zhu host->fifoth_val = MSIZE(DWMCI_MSIZE) | 2126809b04fSSimon Glass RX_WMARK(priv->fifo_depth / 2 - 1) | 2136809b04fSSimon Glass TX_WMARK(priv->fifo_depth / 2); 21428637248Shuang lin 2156809b04fSSimon Glass host->fifo_mode = priv->fifo_mode; 21628637248Shuang lin 217bda599f7SShawn Lin #ifdef CONFIG_ROCKCHIP_RK3128 218bda599f7SShawn Lin host->stride_pio = true; 219bda599f7SShawn Lin #else 220bda599f7SShawn Lin host->stride_pio = false; 221bda599f7SShawn Lin #endif 222bda599f7SShawn Lin 223e1efec4eSSimon Glass #ifdef CONFIG_PWRSEQ 224e1efec4eSSimon Glass /* Enable power if needed */ 225e1efec4eSSimon Glass ret = uclass_get_device_by_phandle(UCLASS_PWRSEQ, dev, "mmc-pwrseq", 226e1efec4eSSimon Glass &pwr_dev); 227e1efec4eSSimon Glass if (!ret) { 228e1efec4eSSimon Glass ret = pwrseq_set_power(pwr_dev, true); 229e1efec4eSSimon Glass if (ret) 230e1efec4eSSimon Glass return ret; 231e1efec4eSSimon Glass } 232e1efec4eSSimon Glass #endif 233e5113c33SJaehoon Chung dwmci_setup_cfg(&plat->cfg, host, priv->minmax[1], priv->minmax[0]); 23416cc62c8SJason Zhu if (dev_read_bool(dev, "mmc-hs200-1_8v")) 23516cc62c8SJason Zhu plat->cfg.host_caps |= MMC_MODE_HS200; 23609494d3aSJason Zhu plat->mmc.default_phase = 23709494d3aSJason Zhu dev_read_u32_default(dev, "default-sample-phase", 0); 238e860ec32SJason Zhu plat->mmc.init_retry = 0; 239f6e41d17SSimon Glass host->mmc = &plat->mmc; 240f6e41d17SSimon Glass host->mmc->priv = &priv->host; 241cffe5d86SSimon Glass host->mmc->dev = dev; 242a8cb4fb5SSimon Glass upriv->mmc = host->mmc; 243a8cb4fb5SSimon Glass 24442b37d8dSSimon Glass return dwmci_probe(dev); 245a8cb4fb5SSimon Glass } 246a8cb4fb5SSimon Glass 247f6e41d17SSimon Glass static int rockchip_dwmmc_bind(struct udevice *dev) 248f6e41d17SSimon Glass { 249f6e41d17SSimon Glass struct rockchip_mmc_plat *plat = dev_get_platdata(dev); 250f6e41d17SSimon Glass 25124f5aec3SMasahiro Yamada return dwmci_bind(dev, &plat->mmc, &plat->cfg); 252f6e41d17SSimon Glass } 253f6e41d17SSimon Glass 254a8cb4fb5SSimon Glass static const struct udevice_id rockchip_dwmmc_ids[] = { 255a8cb4fb5SSimon Glass { .compatible = "rockchip,rk3288-dw-mshc" }, 256337e8c3eSPaweł Jarosz { .compatible = "rockchip,rk2928-dw-mshc" }, 257a8cb4fb5SSimon Glass { } 258a8cb4fb5SSimon Glass }; 259a8cb4fb5SSimon Glass 260a8cb4fb5SSimon Glass U_BOOT_DRIVER(rockchip_dwmmc_drv) = { 261bfeb443eSSimon Glass .name = "rockchip_rk3288_dw_mshc", 262a8cb4fb5SSimon Glass .id = UCLASS_MMC, 263a8cb4fb5SSimon Glass .of_match = rockchip_dwmmc_ids, 264a8cb4fb5SSimon Glass .ofdata_to_platdata = rockchip_dwmmc_ofdata_to_platdata, 26542b37d8dSSimon Glass .ops = &dm_dwmci_ops, 266f6e41d17SSimon Glass .bind = rockchip_dwmmc_bind, 267a8cb4fb5SSimon Glass .probe = rockchip_dwmmc_probe, 268a8cb4fb5SSimon Glass .priv_auto_alloc_size = sizeof(struct rockchip_dwmmc_priv), 269f6e41d17SSimon Glass .platdata_auto_alloc_size = sizeof(struct rockchip_mmc_plat), 270a8cb4fb5SSimon Glass }; 271e1efec4eSSimon Glass 272e1efec4eSSimon Glass #ifdef CONFIG_PWRSEQ 273e1efec4eSSimon Glass static int rockchip_dwmmc_pwrseq_set_power(struct udevice *dev, bool enable) 274e1efec4eSSimon Glass { 275e1efec4eSSimon Glass struct gpio_desc reset; 276e1efec4eSSimon Glass int ret; 277e1efec4eSSimon Glass 278e1efec4eSSimon Glass ret = gpio_request_by_name(dev, "reset-gpios", 0, &reset, GPIOD_IS_OUT); 279e1efec4eSSimon Glass if (ret) 280e1efec4eSSimon Glass return ret; 281e1efec4eSSimon Glass dm_gpio_set_value(&reset, 1); 282e1efec4eSSimon Glass udelay(1); 283e1efec4eSSimon Glass dm_gpio_set_value(&reset, 0); 284e1efec4eSSimon Glass udelay(200); 285e1efec4eSSimon Glass 286e1efec4eSSimon Glass return 0; 287e1efec4eSSimon Glass } 288e1efec4eSSimon Glass 289e1efec4eSSimon Glass static const struct pwrseq_ops rockchip_dwmmc_pwrseq_ops = { 290e1efec4eSSimon Glass .set_power = rockchip_dwmmc_pwrseq_set_power, 291e1efec4eSSimon Glass }; 292e1efec4eSSimon Glass 293e1efec4eSSimon Glass static const struct udevice_id rockchip_dwmmc_pwrseq_ids[] = { 294e1efec4eSSimon Glass { .compatible = "mmc-pwrseq-emmc" }, 295e1efec4eSSimon Glass { } 296e1efec4eSSimon Glass }; 297e1efec4eSSimon Glass 298e1efec4eSSimon Glass U_BOOT_DRIVER(rockchip_dwmmc_pwrseq_drv) = { 299e1efec4eSSimon Glass .name = "mmc_pwrseq_emmc", 300e1efec4eSSimon Glass .id = UCLASS_PWRSEQ, 301e1efec4eSSimon Glass .of_match = rockchip_dwmmc_pwrseq_ids, 302e1efec4eSSimon Glass .ops = &rockchip_dwmmc_pwrseq_ops, 303e1efec4eSSimon Glass }; 304e1efec4eSSimon Glass #endif 305