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 upriv->mmc = host->mmc; 108 109 return 0; 110 } 111 112 static const struct udevice_id rockchip_dwmmc_ids[] = { 113 { .compatible = "rockchip,rk3288-dw-mshc" }, 114 { } 115 }; 116 117 U_BOOT_DRIVER(rockchip_dwmmc_drv) = { 118 .name = "rockchip_dwmmc", 119 .id = UCLASS_MMC, 120 .of_match = rockchip_dwmmc_ids, 121 .ofdata_to_platdata = rockchip_dwmmc_ofdata_to_platdata, 122 .probe = rockchip_dwmmc_probe, 123 .priv_auto_alloc_size = sizeof(struct rockchip_dwmmc_priv), 124 }; 125 126 #ifdef CONFIG_PWRSEQ 127 static int rockchip_dwmmc_pwrseq_set_power(struct udevice *dev, bool enable) 128 { 129 struct gpio_desc reset; 130 int ret; 131 132 ret = gpio_request_by_name(dev, "reset-gpios", 0, &reset, GPIOD_IS_OUT); 133 if (ret) 134 return ret; 135 dm_gpio_set_value(&reset, 1); 136 udelay(1); 137 dm_gpio_set_value(&reset, 0); 138 udelay(200); 139 140 return 0; 141 } 142 143 static const struct pwrseq_ops rockchip_dwmmc_pwrseq_ops = { 144 .set_power = rockchip_dwmmc_pwrseq_set_power, 145 }; 146 147 static const struct udevice_id rockchip_dwmmc_pwrseq_ids[] = { 148 { .compatible = "mmc-pwrseq-emmc" }, 149 { } 150 }; 151 152 U_BOOT_DRIVER(rockchip_dwmmc_pwrseq_drv) = { 153 .name = "mmc_pwrseq_emmc", 154 .id = UCLASS_PWRSEQ, 155 .of_match = rockchip_dwmmc_pwrseq_ids, 156 .ops = &rockchip_dwmmc_pwrseq_ops, 157 }; 158 #endif 159