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/dwmmc.h> 10 #include <asm/arch/system_manager.h> 11 #include <dm.h> 12 #include <dwmmc.h> 13 #include <errno.h> 14 #include <fdtdec.h> 15 #include <libfdt.h> 16 #include <linux/err.h> 17 #include <malloc.h> 18 19 DECLARE_GLOBAL_DATA_PTR; 20 21 static const struct socfpga_clock_manager *clock_manager_base = 22 (void *)SOCFPGA_CLKMGR_ADDRESS; 23 static const struct socfpga_system_manager *system_manager_base = 24 (void *)SOCFPGA_SYSMGR_ADDRESS; 25 26 /* socfpga implmentation specific driver private data */ 27 struct dwmci_socfpga_priv_data { 28 struct dwmci_host host; 29 unsigned int drvsel; 30 unsigned int smplsel; 31 }; 32 33 static void socfpga_dwmci_clksel(struct dwmci_host *host) 34 { 35 struct dwmci_socfpga_priv_data *priv = host->priv; 36 u32 sdmmc_mask = ((priv->smplsel & 0x7) << SYSMGR_SDMMC_SMPLSEL_SHIFT) | 37 ((priv->drvsel & 0x7) << SYSMGR_SDMMC_DRVSEL_SHIFT); 38 39 /* Disable SDMMC clock. */ 40 clrbits_le32(&clock_manager_base->per_pll.en, 41 CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK); 42 43 debug("%s: drvsel %d smplsel %d\n", __func__, 44 priv->drvsel, priv->smplsel); 45 writel(sdmmc_mask, &system_manager_base->sdmmcgrp_ctrl); 46 47 debug("%s: SYSMGR_SDMMCGRP_CTRL_REG = 0x%x\n", __func__, 48 readl(&system_manager_base->sdmmcgrp_ctrl)); 49 50 /* Enable SDMMC clock */ 51 setbits_le32(&clock_manager_base->per_pll.en, 52 CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK); 53 } 54 55 static int socfpga_dwmmc_ofdata_to_platdata(struct udevice *dev) 56 { 57 /* FIXME: probe from DT eventually too/ */ 58 const unsigned long clk = cm_get_mmc_controller_clk_hz(); 59 60 struct dwmci_socfpga_priv_data *priv = dev_get_priv(dev); 61 struct dwmci_host *host = &priv->host; 62 int fifo_depth; 63 64 if (clk == 0) { 65 printf("DWMMC: MMC clock is zero!"); 66 return -EINVAL; 67 } 68 69 fifo_depth = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 70 "fifo-depth", 0); 71 if (fifo_depth < 0) { 72 printf("DWMMC: Can't get FIFO depth\n"); 73 return -EINVAL; 74 } 75 76 host->name = dev->name; 77 host->ioaddr = (void *)dev_get_addr(dev); 78 host->buswidth = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 79 "bus-width", 4); 80 host->clksel = socfpga_dwmci_clksel; 81 82 /* 83 * TODO(sjg@chromium.org): Remove the need for this hack. 84 * We only have one dwmmc block on gen5 SoCFPGA. 85 */ 86 host->dev_index = 0; 87 /* Fixed clock divide by 4 which due to the SDMMC wrapper */ 88 host->bus_hz = clk; 89 host->fifoth_val = MSIZE(0x2) | 90 RX_WMARK(fifo_depth / 2 - 1) | TX_WMARK(fifo_depth / 2); 91 priv->drvsel = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, 92 "drvsel", 3); 93 priv->smplsel = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, 94 "smplsel", 0); 95 host->priv = priv; 96 97 return 0; 98 } 99 100 static int socfpga_dwmmc_probe(struct udevice *dev) 101 { 102 struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); 103 struct dwmci_socfpga_priv_data *priv = dev_get_priv(dev); 104 struct dwmci_host *host = &priv->host; 105 int ret; 106 107 ret = add_dwmci(host, host->bus_hz, 400000); 108 if (ret) 109 return ret; 110 111 upriv->mmc = host->mmc; 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