1 /* 2 * (C) Copyright 2013 Altera Corporation <www.altera.com> 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <asm/arch/clock_manager.h> 9 #include <asm/arch/system_manager.h> 10 #include <dm.h> 11 #include <dwmmc.h> 12 #include <errno.h> 13 #include <fdtdec.h> 14 #include <libfdt.h> 15 #include <linux/err.h> 16 #include <malloc.h> 17 18 DECLARE_GLOBAL_DATA_PTR; 19 20 static const struct socfpga_clock_manager *clock_manager_base = 21 (void *)SOCFPGA_CLKMGR_ADDRESS; 22 static const struct socfpga_system_manager *system_manager_base = 23 (void *)SOCFPGA_SYSMGR_ADDRESS; 24 25 struct socfpga_dwmci_plat { 26 struct mmc_config cfg; 27 struct mmc mmc; 28 }; 29 30 /* socfpga implmentation specific driver private data */ 31 struct dwmci_socfpga_priv_data { 32 struct dwmci_host host; 33 unsigned int drvsel; 34 unsigned int smplsel; 35 }; 36 37 static void socfpga_dwmci_clksel(struct dwmci_host *host) 38 { 39 struct dwmci_socfpga_priv_data *priv = host->priv; 40 u32 sdmmc_mask = ((priv->smplsel & 0x7) << SYSMGR_SDMMC_SMPLSEL_SHIFT) | 41 ((priv->drvsel & 0x7) << SYSMGR_SDMMC_DRVSEL_SHIFT); 42 43 /* Disable SDMMC clock. */ 44 clrbits_le32(&clock_manager_base->per_pll.en, 45 CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK); 46 47 debug("%s: drvsel %d smplsel %d\n", __func__, 48 priv->drvsel, priv->smplsel); 49 writel(sdmmc_mask, &system_manager_base->sdmmcgrp_ctrl); 50 51 debug("%s: SYSMGR_SDMMCGRP_CTRL_REG = 0x%x\n", __func__, 52 readl(&system_manager_base->sdmmcgrp_ctrl)); 53 54 /* Enable SDMMC clock */ 55 setbits_le32(&clock_manager_base->per_pll.en, 56 CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK); 57 } 58 59 static int socfpga_dwmmc_ofdata_to_platdata(struct udevice *dev) 60 { 61 /* FIXME: probe from DT eventually too/ */ 62 const unsigned long clk = cm_get_mmc_controller_clk_hz(); 63 64 struct dwmci_socfpga_priv_data *priv = dev_get_priv(dev); 65 struct dwmci_host *host = &priv->host; 66 int fifo_depth; 67 68 if (clk == 0) { 69 printf("DWMMC: MMC clock is zero!"); 70 return -EINVAL; 71 } 72 73 fifo_depth = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 74 "fifo-depth", 0); 75 if (fifo_depth < 0) { 76 printf("DWMMC: Can't get FIFO depth\n"); 77 return -EINVAL; 78 } 79 80 host->name = dev->name; 81 host->ioaddr = (void *)dev_get_addr(dev); 82 host->buswidth = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 83 "bus-width", 4); 84 host->clksel = socfpga_dwmci_clksel; 85 86 /* 87 * TODO(sjg@chromium.org): Remove the need for this hack. 88 * We only have one dwmmc block on gen5 SoCFPGA. 89 */ 90 host->dev_index = 0; 91 /* Fixed clock divide by 4 which due to the SDMMC wrapper */ 92 host->bus_hz = clk; 93 host->fifoth_val = MSIZE(0x2) | 94 RX_WMARK(fifo_depth / 2 - 1) | TX_WMARK(fifo_depth / 2); 95 priv->drvsel = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, 96 "drvsel", 3); 97 priv->smplsel = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, 98 "smplsel", 0); 99 host->priv = priv; 100 101 return 0; 102 } 103 104 static int socfpga_dwmmc_probe(struct udevice *dev) 105 { 106 #ifdef CONFIG_BLK 107 struct socfpga_dwmci_plat *plat = dev_get_platdata(dev); 108 #endif 109 struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); 110 struct dwmci_socfpga_priv_data *priv = dev_get_priv(dev); 111 struct dwmci_host *host = &priv->host; 112 113 #ifdef CONFIG_BLK 114 dwmci_setup_cfg(&plat->cfg, dev->name, host->buswidth, host->caps, 115 host->bus_hz, 400000); 116 host->mmc = &plat->mmc; 117 #else 118 int ret; 119 120 ret = add_dwmci(host, host->bus_hz, 400000); 121 if (ret) 122 return ret; 123 #endif 124 host->mmc->priv = &priv->host; 125 upriv->mmc = host->mmc; 126 host->mmc->dev = dev; 127 128 return 0; 129 } 130 131 static int socfpga_dwmmc_bind(struct udevice *dev) 132 { 133 #ifdef CONFIG_BLK 134 struct socfpga_dwmci_plat *plat = dev_get_platdata(dev); 135 int ret; 136 137 ret = dwmci_bind(dev, &plat->mmc, &plat->cfg); 138 if (ret) 139 return ret; 140 #endif 141 142 return 0; 143 } 144 145 static const struct udevice_id socfpga_dwmmc_ids[] = { 146 { .compatible = "altr,socfpga-dw-mshc" }, 147 { } 148 }; 149 150 U_BOOT_DRIVER(socfpga_dwmmc_drv) = { 151 .name = "socfpga_dwmmc", 152 .id = UCLASS_MMC, 153 .of_match = socfpga_dwmmc_ids, 154 .ofdata_to_platdata = socfpga_dwmmc_ofdata_to_platdata, 155 .bind = socfpga_dwmmc_bind, 156 .probe = socfpga_dwmmc_probe, 157 .priv_auto_alloc_size = sizeof(struct dwmci_socfpga_priv_data), 158 }; 159