1 /* 2 * Copyright (c) 2013 Google, Inc 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <clk.h> 9 #include <dm.h> 10 #include <dwmmc.h> 11 #include <errno.h> 12 #include <pwrseq.h> 13 #include <syscon.h> 14 #include <asm/gpio.h> 15 #include <asm/arch/clock.h> 16 #include <asm/arch/periph.h> 17 #include <linux/err.h> 18 19 DECLARE_GLOBAL_DATA_PTR; 20 21 struct rockchip_mmc_plat { 22 struct mmc_config cfg; 23 struct mmc mmc; 24 }; 25 26 struct rockchip_dwmmc_priv { 27 struct clk clk; 28 struct dwmci_host host; 29 }; 30 31 static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq) 32 { 33 struct udevice *dev = host->priv; 34 struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); 35 int ret; 36 37 ret = clk_set_rate(&priv->clk, freq); 38 if (ret < 0) { 39 debug("%s: err=%d\n", __func__, ret); 40 return ret; 41 } 42 43 return freq; 44 } 45 46 static int rockchip_dwmmc_ofdata_to_platdata(struct udevice *dev) 47 { 48 struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); 49 struct dwmci_host *host = &priv->host; 50 51 host->name = dev->name; 52 host->ioaddr = (void *)dev_get_addr(dev); 53 host->buswidth = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 54 "bus-width", 4); 55 host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk; 56 host->priv = dev; 57 58 /* use non-removeable as sdcard and emmc as judgement */ 59 if (fdtdec_get_bool(gd->fdt_blob, dev->of_offset, "non-removable")) 60 host->dev_index = 0; 61 else 62 host->dev_index = 1; 63 64 return 0; 65 } 66 67 static int rockchip_dwmmc_probe(struct udevice *dev) 68 { 69 #ifdef CONFIG_BLK 70 struct rockchip_mmc_plat *plat = dev_get_platdata(dev); 71 #endif 72 struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); 73 struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); 74 struct dwmci_host *host = &priv->host; 75 struct udevice *pwr_dev __maybe_unused; 76 u32 minmax[2]; 77 int ret; 78 int fifo_depth; 79 80 ret = clk_get_by_index(dev, 0, &priv->clk); 81 if (ret < 0) 82 return ret; 83 84 if (fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, 85 "clock-freq-min-max", minmax, 2)) 86 return -EINVAL; 87 88 fifo_depth = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 89 "fifo-depth", 0); 90 if (fifo_depth < 0) 91 return -EINVAL; 92 93 host->fifoth_val = MSIZE(0x2) | 94 RX_WMARK(fifo_depth / 2 - 1) | TX_WMARK(fifo_depth / 2); 95 96 if (fdtdec_get_bool(gd->fdt_blob, dev->of_offset, "fifo-mode")) 97 host->fifo_mode = true; 98 99 #ifdef CONFIG_PWRSEQ 100 /* Enable power if needed */ 101 ret = uclass_get_device_by_phandle(UCLASS_PWRSEQ, dev, "mmc-pwrseq", 102 &pwr_dev); 103 if (!ret) { 104 ret = pwrseq_set_power(pwr_dev, true); 105 if (ret) 106 return ret; 107 } 108 #endif 109 #ifdef CONFIG_BLK 110 dwmci_setup_cfg(&plat->cfg, dev->name, host->buswidth, host->caps, 111 minmax[1], minmax[0]); 112 host->mmc = &plat->mmc; 113 #else 114 ret = add_dwmci(host, minmax[1], minmax[0]); 115 if (ret) 116 return ret; 117 118 #endif 119 host->mmc->priv = &priv->host; 120 host->mmc->dev = dev; 121 upriv->mmc = host->mmc; 122 123 return 0; 124 } 125 126 static int rockchip_dwmmc_bind(struct udevice *dev) 127 { 128 #ifdef CONFIG_BLK 129 struct rockchip_mmc_plat *plat = dev_get_platdata(dev); 130 int ret; 131 132 ret = dwmci_bind(dev, &plat->mmc, &plat->cfg); 133 if (ret) 134 return ret; 135 #endif 136 137 return 0; 138 } 139 140 static const struct udevice_id rockchip_dwmmc_ids[] = { 141 { .compatible = "rockchip,rk3288-dw-mshc" }, 142 { } 143 }; 144 145 U_BOOT_DRIVER(rockchip_dwmmc_drv) = { 146 .name = "rockchip_dwmmc", 147 .id = UCLASS_MMC, 148 .of_match = rockchip_dwmmc_ids, 149 .ofdata_to_platdata = rockchip_dwmmc_ofdata_to_platdata, 150 .bind = rockchip_dwmmc_bind, 151 .probe = rockchip_dwmmc_probe, 152 .priv_auto_alloc_size = sizeof(struct rockchip_dwmmc_priv), 153 .platdata_auto_alloc_size = sizeof(struct rockchip_mmc_plat), 154 }; 155 156 #ifdef CONFIG_PWRSEQ 157 static int rockchip_dwmmc_pwrseq_set_power(struct udevice *dev, bool enable) 158 { 159 struct gpio_desc reset; 160 int ret; 161 162 ret = gpio_request_by_name(dev, "reset-gpios", 0, &reset, GPIOD_IS_OUT); 163 if (ret) 164 return ret; 165 dm_gpio_set_value(&reset, 1); 166 udelay(1); 167 dm_gpio_set_value(&reset, 0); 168 udelay(200); 169 170 return 0; 171 } 172 173 static const struct pwrseq_ops rockchip_dwmmc_pwrseq_ops = { 174 .set_power = rockchip_dwmmc_pwrseq_set_power, 175 }; 176 177 static const struct udevice_id rockchip_dwmmc_pwrseq_ids[] = { 178 { .compatible = "mmc-pwrseq-emmc" }, 179 { } 180 }; 181 182 U_BOOT_DRIVER(rockchip_dwmmc_pwrseq_drv) = { 183 .name = "mmc_pwrseq_emmc", 184 .id = UCLASS_PWRSEQ, 185 .of_match = rockchip_dwmmc_pwrseq_ids, 186 .ops = &rockchip_dwmmc_pwrseq_ops, 187 }; 188 #endif 189