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