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