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 40a8cb4fb5SSimon Glass static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq) 41a8cb4fb5SSimon Glass { 42a8cb4fb5SSimon Glass struct udevice *dev = host->priv; 43a8cb4fb5SSimon Glass struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); 44a8cb4fb5SSimon Glass int ret; 45a8cb4fb5SSimon Glass 4624527ef9SZiyuan Xu /* 4724527ef9SZiyuan Xu * If DDR52 8bit mode(only emmc work in 8bit mode), 4824527ef9SZiyuan Xu * divider must be set 1 4924527ef9SZiyuan Xu */ 5024527ef9SZiyuan Xu if (mmc_card_ddr52(host->mmc) && host->mmc->bus_width == 8) 5124527ef9SZiyuan Xu freq *= 2; 5224527ef9SZiyuan Xu 53135aa950SStephen Warren ret = clk_set_rate(&priv->clk, freq); 54a8cb4fb5SSimon Glass if (ret < 0) { 55419b0801SKever Yang debug("%s: err=%d\n", __func__, ret); 56a8cb4fb5SSimon Glass return ret; 57a8cb4fb5SSimon Glass } 58a8cb4fb5SSimon Glass 59a8cb4fb5SSimon Glass return freq; 60a8cb4fb5SSimon Glass } 61a8cb4fb5SSimon Glass 62a8cb4fb5SSimon Glass static int rockchip_dwmmc_ofdata_to_platdata(struct udevice *dev) 63a8cb4fb5SSimon Glass { 64bfeb443eSSimon Glass #if !CONFIG_IS_ENABLED(OF_PLATDATA) 65a8cb4fb5SSimon Glass struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); 66a8cb4fb5SSimon Glass struct dwmci_host *host = &priv->host; 67a8cb4fb5SSimon Glass 68a8cb4fb5SSimon Glass host->name = dev->name; 69a821c4afSSimon Glass host->ioaddr = (void *)devfdt_get_addr(dev); 70fd1bf8dfSPhilipp Tomsich host->buswidth = dev_read_u32_default(dev, "bus-width", 4); 71a8cb4fb5SSimon Glass host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk; 72a8cb4fb5SSimon Glass host->priv = dev; 73a8cb4fb5SSimon Glass 74ace2198bShuang lin /* use non-removeable as sdcard and emmc as judgement */ 75fd1bf8dfSPhilipp Tomsich if (dev_read_bool(dev, "non-removable")) 766579385bShuang lin host->dev_index = 0; 776579385bShuang lin else 78ace2198bShuang lin host->dev_index = 1; 79a8cb4fb5SSimon Glass 80fd1bf8dfSPhilipp Tomsich priv->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0); 81fd1bf8dfSPhilipp Tomsich 826809b04fSSimon Glass if (priv->fifo_depth < 0) 836809b04fSSimon Glass return -EINVAL; 84fd1bf8dfSPhilipp Tomsich priv->fifo_mode = dev_read_bool(dev, "fifo-mode"); 85ff71f9acSPhilipp Tomsich 86ff71f9acSPhilipp Tomsich /* 87ff71f9acSPhilipp Tomsich * 'clock-freq-min-max' is deprecated 88ff71f9acSPhilipp Tomsich * (see https://github.com/torvalds/linux/commit/b023030f10573de738bbe8df63d43acab64c9f7b) 89ff71f9acSPhilipp Tomsich */ 90fd1bf8dfSPhilipp Tomsich if (dev_read_u32_array(dev, "clock-freq-min-max", priv->minmax, 2)) { 91fd1bf8dfSPhilipp Tomsich int val = dev_read_u32_default(dev, "max-frequency", -EINVAL); 92ff71f9acSPhilipp Tomsich 93ff71f9acSPhilipp Tomsich if (val < 0) 94ff71f9acSPhilipp Tomsich return val; 95ff71f9acSPhilipp Tomsich 96ff71f9acSPhilipp Tomsich priv->minmax[0] = 400000; /* 400 kHz */ 97ff71f9acSPhilipp Tomsich priv->minmax[1] = val; 98ff71f9acSPhilipp Tomsich } else { 99ff71f9acSPhilipp Tomsich debug("%s: 'clock-freq-min-max' property was deprecated.\n", 100ff71f9acSPhilipp Tomsich __func__); 101ff71f9acSPhilipp Tomsich } 102bfeb443eSSimon Glass #endif 103a8cb4fb5SSimon Glass return 0; 104a8cb4fb5SSimon Glass } 105a8cb4fb5SSimon Glass 1064455fdd9SZiyuan Xu #define NUM_PHASES 270 1074455fdd9SZiyuan Xu #define TUNING_ITERATION_TO_PHASE(i) (DIV_ROUND_UP((i) * 270, NUM_PHASES)) 1084455fdd9SZiyuan Xu 1094455fdd9SZiyuan Xu static int rockchip_dwmmc_execute_tuning(struct dwmci_host *host, u32 opcode) 1104455fdd9SZiyuan Xu { 1114455fdd9SZiyuan Xu int ret = 0; 1124455fdd9SZiyuan Xu int i; 1134455fdd9SZiyuan Xu bool v, prev_v = 0, first_v; 1144455fdd9SZiyuan Xu struct range_t { 1154455fdd9SZiyuan Xu int start; 1164455fdd9SZiyuan Xu int end; /* inclusive */ 1174455fdd9SZiyuan Xu }; 1184455fdd9SZiyuan Xu struct range_t *ranges; 1194455fdd9SZiyuan Xu unsigned int range_count = 0; 1204455fdd9SZiyuan Xu int longest_range_len = -1; 1214455fdd9SZiyuan Xu int longest_range = -1; 1224455fdd9SZiyuan Xu int middle_phase; 1234455fdd9SZiyuan Xu struct udevice *dev = host->priv; 1244455fdd9SZiyuan Xu struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); 1254455fdd9SZiyuan Xu struct mmc *mmc = host->mmc; 1264455fdd9SZiyuan Xu 1274455fdd9SZiyuan Xu if (IS_ERR(&priv->sample_clk)) 1284455fdd9SZiyuan Xu return -EIO; 1294455fdd9SZiyuan Xu 1304455fdd9SZiyuan Xu ranges = calloc(sizeof(*ranges), NUM_PHASES / 2 + 1); 1314455fdd9SZiyuan Xu if (!ranges) 1324455fdd9SZiyuan Xu return -ENOMEM; 1334455fdd9SZiyuan Xu 1344455fdd9SZiyuan Xu /* Try each phase and extract good ranges */ 1354455fdd9SZiyuan Xu for (i = 0; i < NUM_PHASES; ) { 1364455fdd9SZiyuan Xu clk_set_phase(&priv->sample_clk, TUNING_ITERATION_TO_PHASE(i)); 1374455fdd9SZiyuan Xu 1384455fdd9SZiyuan Xu v = !mmc_send_tuning(mmc, opcode); 1394455fdd9SZiyuan Xu 1404455fdd9SZiyuan Xu if (i == 0) 1414455fdd9SZiyuan Xu first_v = v; 1424455fdd9SZiyuan Xu 1434455fdd9SZiyuan Xu if ((!prev_v) && v) { 1444455fdd9SZiyuan Xu range_count++; 1454455fdd9SZiyuan Xu ranges[range_count - 1].start = i; 1464455fdd9SZiyuan Xu } 1474455fdd9SZiyuan Xu if (v) { 1484455fdd9SZiyuan Xu ranges[range_count - 1].end = i; 1494455fdd9SZiyuan Xu i++; 1504455fdd9SZiyuan Xu } else if (i == NUM_PHASES - 1) { 1514455fdd9SZiyuan Xu /* No extra skipping rules if we're at the end */ 1524455fdd9SZiyuan Xu i++; 1534455fdd9SZiyuan Xu } else { 1544455fdd9SZiyuan Xu /* 1554455fdd9SZiyuan Xu * No need to check too close to an invalid 1564455fdd9SZiyuan Xu * one since testing bad phases is slow. Skip 1574455fdd9SZiyuan Xu * 20 degrees. 1584455fdd9SZiyuan Xu */ 1594455fdd9SZiyuan Xu i += DIV_ROUND_UP(20 * NUM_PHASES, NUM_PHASES); 1604455fdd9SZiyuan Xu 1614455fdd9SZiyuan Xu /* Always test the last one */ 1624455fdd9SZiyuan Xu if (i >= NUM_PHASES) 1634455fdd9SZiyuan Xu i = NUM_PHASES - 1; 1644455fdd9SZiyuan Xu } 1654455fdd9SZiyuan Xu 1664455fdd9SZiyuan Xu prev_v = v; 1674455fdd9SZiyuan Xu } 1684455fdd9SZiyuan Xu 1694455fdd9SZiyuan Xu if (range_count == 0) { 1704455fdd9SZiyuan Xu debug("All phases bad!"); 1714455fdd9SZiyuan Xu ret = -EIO; 1724455fdd9SZiyuan Xu goto free; 1734455fdd9SZiyuan Xu } 1744455fdd9SZiyuan Xu 1754455fdd9SZiyuan Xu /* wrap around case, merge the end points */ 1764455fdd9SZiyuan Xu if ((range_count > 1) && first_v && v) { 1774455fdd9SZiyuan Xu ranges[0].start = ranges[range_count - 1].start; 1784455fdd9SZiyuan Xu range_count--; 1794455fdd9SZiyuan Xu } 1804455fdd9SZiyuan Xu 1814455fdd9SZiyuan Xu if (ranges[0].start == 0 && ranges[0].end == NUM_PHASES - 1) { 1824455fdd9SZiyuan Xu clk_set_phase(&priv->sample_clk, 1834455fdd9SZiyuan Xu TUNING_ITERATION_TO_PHASE(NUM_PHASES / 2)); 1844455fdd9SZiyuan Xu debug("All phases work, using middle phase.\n"); 1854455fdd9SZiyuan Xu goto free; 1864455fdd9SZiyuan Xu } 1874455fdd9SZiyuan Xu 1884455fdd9SZiyuan Xu /* Find the longest range */ 1894455fdd9SZiyuan Xu for (i = 0; i < range_count; i++) { 1904455fdd9SZiyuan Xu int len = (ranges[i].end - ranges[i].start + 1); 1914455fdd9SZiyuan Xu 1924455fdd9SZiyuan Xu if (len < 0) 1934455fdd9SZiyuan Xu len += NUM_PHASES; 1944455fdd9SZiyuan Xu 1954455fdd9SZiyuan Xu if (longest_range_len < len) { 1964455fdd9SZiyuan Xu longest_range_len = len; 1974455fdd9SZiyuan Xu longest_range = i; 1984455fdd9SZiyuan Xu } 1994455fdd9SZiyuan Xu 2004455fdd9SZiyuan Xu debug("Good phase range %d-%d (%d len)\n", 2014455fdd9SZiyuan Xu TUNING_ITERATION_TO_PHASE(ranges[i].start), 2024455fdd9SZiyuan Xu TUNING_ITERATION_TO_PHASE(ranges[i].end), len); 2034455fdd9SZiyuan Xu } 2044455fdd9SZiyuan Xu 2054455fdd9SZiyuan Xu printf("Best phase range %d-%d (%d len)\n", 2064455fdd9SZiyuan Xu TUNING_ITERATION_TO_PHASE(ranges[longest_range].start), 2074455fdd9SZiyuan Xu TUNING_ITERATION_TO_PHASE(ranges[longest_range].end), 2084455fdd9SZiyuan Xu longest_range_len); 2094455fdd9SZiyuan Xu 2104455fdd9SZiyuan Xu middle_phase = ranges[longest_range].start + longest_range_len / 2; 2114455fdd9SZiyuan Xu middle_phase %= NUM_PHASES; 2124455fdd9SZiyuan Xu debug("Successfully tuned phase to %d\n", 2134455fdd9SZiyuan Xu TUNING_ITERATION_TO_PHASE(middle_phase)); 2144455fdd9SZiyuan Xu 2154455fdd9SZiyuan Xu clk_set_phase(&priv->sample_clk, 2164455fdd9SZiyuan Xu TUNING_ITERATION_TO_PHASE(middle_phase)); 2174455fdd9SZiyuan Xu 2184455fdd9SZiyuan Xu free: 2194455fdd9SZiyuan Xu free(ranges); 2204455fdd9SZiyuan Xu return ret; 2214455fdd9SZiyuan Xu } 2224455fdd9SZiyuan Xu 223a8cb4fb5SSimon Glass static int rockchip_dwmmc_probe(struct udevice *dev) 224a8cb4fb5SSimon Glass { 225f6e41d17SSimon Glass struct rockchip_mmc_plat *plat = dev_get_platdata(dev); 226a8cb4fb5SSimon Glass struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); 227a8cb4fb5SSimon Glass struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); 228a8cb4fb5SSimon Glass struct dwmci_host *host = &priv->host; 229e1efec4eSSimon Glass struct udevice *pwr_dev __maybe_unused; 230a8cb4fb5SSimon Glass int ret; 231a8cb4fb5SSimon Glass 232bfeb443eSSimon Glass #if CONFIG_IS_ENABLED(OF_PLATDATA) 233bfeb443eSSimon Glass struct dtd_rockchip_rk3288_dw_mshc *dtplat = &plat->dtplat; 234bfeb443eSSimon Glass 235bfeb443eSSimon Glass host->name = dev->name; 236bfeb443eSSimon Glass host->ioaddr = map_sysmem(dtplat->reg[0], dtplat->reg[1]); 237bfeb443eSSimon Glass host->buswidth = dtplat->bus_width; 238bfeb443eSSimon Glass host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk; 2394455fdd9SZiyuan Xu host->execute_tuning = rockchip_dwmmc_execute_tuning; 240bfeb443eSSimon Glass host->priv = dev; 241bfeb443eSSimon Glass host->dev_index = 0; 242bfeb443eSSimon Glass priv->fifo_depth = dtplat->fifo_depth; 243bfeb443eSSimon Glass priv->fifo_mode = 0; 24480935298SKever Yang priv->minmax[0] = 400000; /* 400 kHz */ 24580935298SKever Yang priv->minmax[1] = dtplat->max_frequency; 246bfeb443eSSimon Glass 247bfeb443eSSimon Glass ret = clk_get_by_index_platdata(dev, 0, dtplat->clocks, &priv->clk); 248bfeb443eSSimon Glass if (ret < 0) 249bfeb443eSSimon Glass return ret; 250bfeb443eSSimon Glass #else 251419b0801SKever Yang ret = clk_get_by_index(dev, 0, &priv->clk); 252898d6439SSimon Glass if (ret < 0) 253a8cb4fb5SSimon Glass return ret; 2544455fdd9SZiyuan Xu clk_get_by_name(dev, "ciu_sample", &priv->sample_clk); 2554455fdd9SZiyuan Xu host->execute_tuning = rockchip_dwmmc_execute_tuning; 256bfeb443eSSimon Glass #endif 25728637248Shuang lin host->fifoth_val = MSIZE(0x2) | 2586809b04fSSimon Glass RX_WMARK(priv->fifo_depth / 2 - 1) | 2596809b04fSSimon Glass TX_WMARK(priv->fifo_depth / 2); 26028637248Shuang lin 2616809b04fSSimon Glass host->fifo_mode = priv->fifo_mode; 26228637248Shuang lin 263bda599f7SShawn Lin #ifdef CONFIG_ROCKCHIP_RK3128 264bda599f7SShawn Lin host->stride_pio = true; 265bda599f7SShawn Lin #else 266bda599f7SShawn Lin host->stride_pio = false; 267bda599f7SShawn Lin #endif 268bda599f7SShawn Lin 269e1efec4eSSimon Glass #ifdef CONFIG_PWRSEQ 270e1efec4eSSimon Glass /* Enable power if needed */ 271e1efec4eSSimon Glass ret = uclass_get_device_by_phandle(UCLASS_PWRSEQ, dev, "mmc-pwrseq", 272e1efec4eSSimon Glass &pwr_dev); 273e1efec4eSSimon Glass if (!ret) { 274e1efec4eSSimon Glass ret = pwrseq_set_power(pwr_dev, true); 275e1efec4eSSimon Glass if (ret) 276e1efec4eSSimon Glass return ret; 277e1efec4eSSimon Glass } 278e1efec4eSSimon Glass #endif 279e5113c33SJaehoon Chung dwmci_setup_cfg(&plat->cfg, host, priv->minmax[1], priv->minmax[0]); 280f6e41d17SSimon Glass host->mmc = &plat->mmc; 281f6e41d17SSimon Glass host->mmc->priv = &priv->host; 282cffe5d86SSimon Glass host->mmc->dev = dev; 283a8cb4fb5SSimon Glass upriv->mmc = host->mmc; 284a8cb4fb5SSimon Glass 28542b37d8dSSimon Glass return dwmci_probe(dev); 286a8cb4fb5SSimon Glass } 287a8cb4fb5SSimon Glass 288f6e41d17SSimon Glass static int rockchip_dwmmc_bind(struct udevice *dev) 289f6e41d17SSimon Glass { 290f6e41d17SSimon Glass struct rockchip_mmc_plat *plat = dev_get_platdata(dev); 291f6e41d17SSimon Glass 29224f5aec3SMasahiro Yamada return dwmci_bind(dev, &plat->mmc, &plat->cfg); 293f6e41d17SSimon Glass } 294f6e41d17SSimon Glass 295a8cb4fb5SSimon Glass static const struct udevice_id rockchip_dwmmc_ids[] = { 296a8cb4fb5SSimon Glass { .compatible = "rockchip,rk3288-dw-mshc" }, 297*337e8c3eSPaweł Jarosz { .compatible = "rockchip,rk2928-dw-mshc" }, 298a8cb4fb5SSimon Glass { } 299a8cb4fb5SSimon Glass }; 300a8cb4fb5SSimon Glass 301a8cb4fb5SSimon Glass U_BOOT_DRIVER(rockchip_dwmmc_drv) = { 302bfeb443eSSimon Glass .name = "rockchip_rk3288_dw_mshc", 303a8cb4fb5SSimon Glass .id = UCLASS_MMC, 304a8cb4fb5SSimon Glass .of_match = rockchip_dwmmc_ids, 305a8cb4fb5SSimon Glass .ofdata_to_platdata = rockchip_dwmmc_ofdata_to_platdata, 30642b37d8dSSimon Glass .ops = &dm_dwmci_ops, 307f6e41d17SSimon Glass .bind = rockchip_dwmmc_bind, 308a8cb4fb5SSimon Glass .probe = rockchip_dwmmc_probe, 309a8cb4fb5SSimon Glass .priv_auto_alloc_size = sizeof(struct rockchip_dwmmc_priv), 310f6e41d17SSimon Glass .platdata_auto_alloc_size = sizeof(struct rockchip_mmc_plat), 311a8cb4fb5SSimon Glass }; 312e1efec4eSSimon Glass 313e1efec4eSSimon Glass #ifdef CONFIG_PWRSEQ 314e1efec4eSSimon Glass static int rockchip_dwmmc_pwrseq_set_power(struct udevice *dev, bool enable) 315e1efec4eSSimon Glass { 316e1efec4eSSimon Glass struct gpio_desc reset; 317e1efec4eSSimon Glass int ret; 318e1efec4eSSimon Glass 319e1efec4eSSimon Glass ret = gpio_request_by_name(dev, "reset-gpios", 0, &reset, GPIOD_IS_OUT); 320e1efec4eSSimon Glass if (ret) 321e1efec4eSSimon Glass return ret; 322e1efec4eSSimon Glass dm_gpio_set_value(&reset, 1); 323e1efec4eSSimon Glass udelay(1); 324e1efec4eSSimon Glass dm_gpio_set_value(&reset, 0); 325e1efec4eSSimon Glass udelay(200); 326e1efec4eSSimon Glass 327e1efec4eSSimon Glass return 0; 328e1efec4eSSimon Glass } 329e1efec4eSSimon Glass 330e1efec4eSSimon Glass static const struct pwrseq_ops rockchip_dwmmc_pwrseq_ops = { 331e1efec4eSSimon Glass .set_power = rockchip_dwmmc_pwrseq_set_power, 332e1efec4eSSimon Glass }; 333e1efec4eSSimon Glass 334e1efec4eSSimon Glass static const struct udevice_id rockchip_dwmmc_pwrseq_ids[] = { 335e1efec4eSSimon Glass { .compatible = "mmc-pwrseq-emmc" }, 336e1efec4eSSimon Glass { } 337e1efec4eSSimon Glass }; 338e1efec4eSSimon Glass 339e1efec4eSSimon Glass U_BOOT_DRIVER(rockchip_dwmmc_pwrseq_drv) = { 340e1efec4eSSimon Glass .name = "mmc_pwrseq_emmc", 341e1efec4eSSimon Glass .id = UCLASS_PWRSEQ, 342e1efec4eSSimon Glass .of_match = rockchip_dwmmc_pwrseq_ids, 343e1efec4eSSimon Glass .ops = &rockchip_dwmmc_pwrseq_ops, 344e1efec4eSSimon Glass }; 345e1efec4eSSimon Glass #endif 346