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 <linux/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(dev), 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 *)devfdt_get_addr(dev); 82 host->buswidth = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), 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(dev), 96 "drvsel", 3); 97 priv->smplsel = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), 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, host, host->bus_hz, 400000); 115 host->mmc = &plat->mmc; 116 #else 117 int ret; 118 119 ret = add_dwmci(host, host->bus_hz, 400000); 120 if (ret) 121 return ret; 122 #endif 123 host->mmc->priv = &priv->host; 124 upriv->mmc = host->mmc; 125 host->mmc->dev = dev; 126 127 return 0; 128 } 129 130 static int socfpga_dwmmc_bind(struct udevice *dev) 131 { 132 #ifdef CONFIG_BLK 133 struct socfpga_dwmci_plat *plat = dev_get_platdata(dev); 134 int ret; 135 136 ret = dwmci_bind(dev, &plat->mmc, &plat->cfg); 137 if (ret) 138 return ret; 139 #endif 140 141 return 0; 142 } 143 144 static const struct udevice_id socfpga_dwmmc_ids[] = { 145 { .compatible = "altr,socfpga-dw-mshc" }, 146 { } 147 }; 148 149 U_BOOT_DRIVER(socfpga_dwmmc_drv) = { 150 .name = "socfpga_dwmmc", 151 .id = UCLASS_MMC, 152 .of_match = socfpga_dwmmc_ids, 153 .ofdata_to_platdata = socfpga_dwmmc_ofdata_to_platdata, 154 .ops = &dm_dwmci_ops, 155 .bind = socfpga_dwmmc_bind, 156 .probe = socfpga_dwmmc_probe, 157 .priv_auto_alloc_size = sizeof(struct dwmci_socfpga_priv_data), 158 .platdata_auto_alloc_size = sizeof(struct socfpga_dwmci_plat), 159 }; 160