1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (c) 2013 Google, Inc 4 */ 5 6#include <clk.h> 7#include <dm.h> 8#include <dt-structs.h> 9#include <dwmmc.h> 10#include <errno.h> 11#include <log.h> 12#include <mapmem.h> 13#include <pwrseq.h> 14#include <syscon.h> 15#include <asm/gpio.h> 16#include <asm/arch-rockchip/clock.h> 17#include <asm/arch-rockchip/periph.h> 18#include <linux/delay.h> 19#include <linux/err.h> 20 21struct rockchip_mmc_plat { 22#if CONFIG_IS_ENABLED(OF_PLATDATA) 23 struct dtd_rockchip_rk3288_dw_mshc dtplat; 24#endif 25 struct mmc_config cfg; 26 struct mmc mmc; 27}; 28 29struct rockchip_dwmmc_priv { 30 struct clk clk; 31 struct dwmci_host host; 32 int fifo_depth; 33 bool fifo_mode; 34 u32 minmax[2]; 35}; 36 37static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq) 38{ 39 struct udevice *dev = host->priv; 40 struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); 41 int ret; 42 43 /* 44 * The clock frequency chosen here affects CLKDIV in the dw_mmc core. 45 * That can be either 0 or 1, but it must be set to 1 for eMMC DDR52 46 * 8-bit mode. It will be set to 0 for all other modes. 47 */ 48 if (host->mmc->selected_mode == MMC_DDR_52 && host->mmc->bus_width == 8) 49 freq *= 2; 50 51 ret = clk_set_rate(&priv->clk, freq); 52 if (ret < 0) { 53 debug("%s: err=%d\n", __func__, ret); 54 return 0; 55 } 56 57 return freq; 58} 59 60static int rockchip_dwmmc_of_to_plat(struct udevice *dev) 61{ 62 struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); 63 struct dwmci_host *host = &priv->host; 64 65 if (!CONFIG_IS_ENABLED(OF_REAL)) 66 return 0; 67 68 host->name = dev->name; 69 host->ioaddr = dev_read_addr_ptr(dev); 70 host->buswidth = dev_read_u32_default(dev, "bus-width", 4); 71 host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk; 72 host->priv = dev; 73 74 /* use non-removeable as sdcard and emmc as judgement */ 75 if (dev_read_bool(dev, "non-removable")) 76 host->dev_index = 0; 77 else 78 host->dev_index = 1; 79 80 priv->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0); 81 82 if (priv->fifo_depth < 0) 83 return -EINVAL; 84 priv->fifo_mode = dev_read_bool(dev, "fifo-mode"); 85 86#ifdef CONFIG_SPL_BUILD 87 if (!priv->fifo_mode) 88 priv->fifo_mode = dev_read_bool(dev, "u-boot,spl-fifo-mode"); 89#endif 90 91 /* 92 * 'clock-freq-min-max' is deprecated 93 * (see https://github.com/torvalds/linux/commit/b023030f10573de738bbe8df63d43acab64c9f7b) 94 */ 95 if (dev_read_u32_array(dev, "clock-freq-min-max", priv->minmax, 2)) { 96 int val = dev_read_u32_default(dev, "max-frequency", -EINVAL); 97 98 if (val < 0) 99 return val; 100 101 priv->minmax[0] = 400000; /* 400 kHz */ 102 priv->minmax[1] = val; 103 } else { 104 debug("%s: 'clock-freq-min-max' property was deprecated.\n", 105 __func__); 106 } 107 108 return 0; 109} 110 111static int rockchip_dwmmc_probe(struct udevice *dev) 112{ 113 struct rockchip_mmc_plat *plat = dev_get_plat(dev); 114 struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); 115 struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); 116 struct dwmci_host *host = &priv->host; 117 int ret; 118 119#if CONFIG_IS_ENABLED(OF_PLATDATA) 120 struct dtd_rockchip_rk3288_dw_mshc *dtplat = &plat->dtplat; 121 122 host->name = dev->name; 123 host->ioaddr = map_sysmem(dtplat->reg[0], dtplat->reg[1]); 124 host->buswidth = dtplat->bus_width; 125 host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk; 126 host->priv = dev; 127 host->dev_index = 0; 128 priv->fifo_depth = dtplat->fifo_depth; 129 priv->fifo_mode = dtplat->u_boot_spl_fifo_mode; 130 priv->minmax[0] = 400000; /* 400 kHz */ 131 priv->minmax[1] = dtplat->max_frequency; 132 133 ret = clk_get_by_phandle(dev, &dtplat->clocks[1], &priv->clk); 134 if (ret < 0) 135 return ret; 136#else 137 ret = clk_get_by_index(dev, 1, &priv->clk); 138 if (ret < 0) 139 return ret; 140#endif 141 host->fifoth_val = MSIZE(0x2) | 142 RX_WMARK(priv->fifo_depth / 2 - 1) | 143 TX_WMARK(priv->fifo_depth / 2); 144 145 host->fifo_mode = priv->fifo_mode; 146 147#if CONFIG_IS_ENABLED(MMC_PWRSEQ) 148 /* Enable power if needed */ 149 ret = mmc_pwrseq_get_power(dev, &plat->cfg); 150 if (!ret) { 151 ret = pwrseq_set_power(plat->cfg.pwr_dev, true); 152 if (ret) 153 return ret; 154 } 155#endif 156 dwmci_setup_cfg(&plat->cfg, host, priv->minmax[1], priv->minmax[0]); 157 host->mmc = &plat->mmc; 158 host->mmc->priv = &priv->host; 159 host->mmc->dev = dev; 160 upriv->mmc = host->mmc; 161 162 return dwmci_probe(dev); 163} 164 165static int rockchip_dwmmc_bind(struct udevice *dev) 166{ 167 struct rockchip_mmc_plat *plat = dev_get_plat(dev); 168 169 return dwmci_bind(dev, &plat->mmc, &plat->cfg); 170} 171 172static const struct udevice_id rockchip_dwmmc_ids[] = { 173 { .compatible = "rockchip,rk2928-dw-mshc" }, 174 { .compatible = "rockchip,rk3288-dw-mshc" }, 175 { } 176}; 177 178U_BOOT_DRIVER(rockchip_rk3288_dw_mshc) = { 179 .name = "rockchip_rk3288_dw_mshc", 180 .id = UCLASS_MMC, 181 .of_match = rockchip_dwmmc_ids, 182 .of_to_plat = rockchip_dwmmc_of_to_plat, 183 .ops = &dm_dwmci_ops, 184 .bind = rockchip_dwmmc_bind, 185 .probe = rockchip_dwmmc_probe, 186 .priv_auto = sizeof(struct rockchip_dwmmc_priv), 187 .plat_auto = sizeof(struct rockchip_mmc_plat), 188}; 189 190DM_DRIVER_ALIAS(rockchip_rk3288_dw_mshc, rockchip_rk2928_dw_mshc) 191DM_DRIVER_ALIAS(rockchip_rk3288_dw_mshc, rockchip_rk3328_dw_mshc) 192DM_DRIVER_ALIAS(rockchip_rk3288_dw_mshc, rockchip_rk3368_dw_mshc) 193