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 /* socfpga implmentation specific driver private data */ 26 struct dwmci_socfpga_priv_data { 27 struct dwmci_host host; 28 unsigned int drvsel; 29 unsigned int smplsel; 30 }; 31 32 static void socfpga_dwmci_clksel(struct dwmci_host *host) 33 { 34 struct dwmci_socfpga_priv_data *priv = host->priv; 35 u32 sdmmc_mask = ((priv->smplsel & 0x7) << SYSMGR_SDMMC_SMPLSEL_SHIFT) | 36 ((priv->drvsel & 0x7) << SYSMGR_SDMMC_DRVSEL_SHIFT); 37 38 /* Disable SDMMC clock. */ 39 clrbits_le32(&clock_manager_base->per_pll.en, 40 CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK); 41 42 debug("%s: drvsel %d smplsel %d\n", __func__, 43 priv->drvsel, priv->smplsel); 44 writel(sdmmc_mask, &system_manager_base->sdmmcgrp_ctrl); 45 46 debug("%s: SYSMGR_SDMMCGRP_CTRL_REG = 0x%x\n", __func__, 47 readl(&system_manager_base->sdmmcgrp_ctrl)); 48 49 /* Enable SDMMC clock */ 50 setbits_le32(&clock_manager_base->per_pll.en, 51 CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK); 52 } 53 54 static int socfpga_dwmmc_ofdata_to_platdata(struct udevice *dev) 55 { 56 /* FIXME: probe from DT eventually too/ */ 57 const unsigned long clk = cm_get_mmc_controller_clk_hz(); 58 59 struct dwmci_socfpga_priv_data *priv = dev_get_priv(dev); 60 struct dwmci_host *host = &priv->host; 61 int fifo_depth; 62 63 if (clk == 0) { 64 printf("DWMMC: MMC clock is zero!"); 65 return -EINVAL; 66 } 67 68 fifo_depth = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 69 "fifo-depth", 0); 70 if (fifo_depth < 0) { 71 printf("DWMMC: Can't get FIFO depth\n"); 72 return -EINVAL; 73 } 74 75 host->name = dev->name; 76 host->ioaddr = (void *)dev_get_addr(dev); 77 host->buswidth = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 78 "bus-width", 4); 79 host->clksel = socfpga_dwmci_clksel; 80 81 /* 82 * TODO(sjg@chromium.org): Remove the need for this hack. 83 * We only have one dwmmc block on gen5 SoCFPGA. 84 */ 85 host->dev_index = 0; 86 /* Fixed clock divide by 4 which due to the SDMMC wrapper */ 87 host->bus_hz = clk; 88 host->fifoth_val = MSIZE(0x2) | 89 RX_WMARK(fifo_depth / 2 - 1) | TX_WMARK(fifo_depth / 2); 90 priv->drvsel = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, 91 "drvsel", 3); 92 priv->smplsel = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, 93 "smplsel", 0); 94 host->priv = priv; 95 96 return 0; 97 } 98 99 static int socfpga_dwmmc_probe(struct udevice *dev) 100 { 101 struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); 102 struct dwmci_socfpga_priv_data *priv = dev_get_priv(dev); 103 struct dwmci_host *host = &priv->host; 104 int ret; 105 106 ret = add_dwmci(host, host->bus_hz, 400000); 107 if (ret) 108 return ret; 109 110 upriv->mmc = host->mmc; 111 host->mmc->dev = dev; 112 113 return 0; 114 } 115 116 static const struct udevice_id socfpga_dwmmc_ids[] = { 117 { .compatible = "altr,socfpga-dw-mshc" }, 118 { } 119 }; 120 121 U_BOOT_DRIVER(socfpga_dwmmc_drv) = { 122 .name = "socfpga_dwmmc", 123 .id = UCLASS_MMC, 124 .of_match = socfpga_dwmmc_ids, 125 .ofdata_to_platdata = socfpga_dwmmc_ofdata_to_platdata, 126 .probe = socfpga_dwmmc_probe, 127 .priv_auto_alloc_size = sizeof(struct dwmci_socfpga_priv_data), 128 }; 129