]>
Commit | Line | Data |
---|---|---|
c5c1af21 CLS |
1 | /* |
2 | * (C) Copyright 2013 Altera Corporation <www.altera.com> | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0+ | |
5 | */ | |
6 | ||
7 | #include <common.h> | |
c5c1af21 CLS |
8 | #include <asm/arch/clock_manager.h> |
9 | #include <asm/arch/system_manager.h> | |
c35ed77a MV |
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; | |
c5c1af21 CLS |
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 | ||
c35ed77a | 25 | /* socfpga implmentation specific driver private data */ |
9a41404d | 26 | struct dwmci_socfpga_priv_data { |
c35ed77a MV |
27 | struct dwmci_host host; |
28 | unsigned int drvsel; | |
29 | unsigned int smplsel; | |
9a41404d CLS |
30 | }; |
31 | ||
32 | static void socfpga_dwmci_clksel(struct dwmci_host *host) | |
33 | { | |
34 | struct dwmci_socfpga_priv_data *priv = host->priv; | |
a1684b61 DN |
35 | u32 sdmmc_mask = ((priv->smplsel & 0x7) << SYSMGR_SDMMC_SMPLSEL_SHIFT) | |
36 | ((priv->drvsel & 0x7) << SYSMGR_SDMMC_DRVSEL_SHIFT); | |
c5c1af21 CLS |
37 | |
38 | /* Disable SDMMC clock. */ | |
51fb455f | 39 | clrbits_le32(&clock_manager_base->per_pll.en, |
c5c1af21 CLS |
40 | CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK); |
41 | ||
9a41404d CLS |
42 | debug("%s: drvsel %d smplsel %d\n", __func__, |
43 | priv->drvsel, priv->smplsel); | |
a1684b61 | 44 | writel(sdmmc_mask, &system_manager_base->sdmmcgrp_ctrl); |
c5c1af21 CLS |
45 | |
46 | debug("%s: SYSMGR_SDMMCGRP_CTRL_REG = 0x%x\n", __func__, | |
47 | readl(&system_manager_base->sdmmcgrp_ctrl)); | |
48 | ||
49 | /* Enable SDMMC clock */ | |
51fb455f | 50 | setbits_le32(&clock_manager_base->per_pll.en, |
c5c1af21 CLS |
51 | CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK); |
52 | } | |
53 | ||
c35ed77a | 54 | static int socfpga_dwmmc_ofdata_to_platdata(struct udevice *dev) |
c5c1af21 | 55 | { |
129adf5b MV |
56 | /* FIXME: probe from DT eventually too/ */ |
57 | const unsigned long clk = cm_get_mmc_controller_clk_hz(); | |
58 | ||
c35ed77a MV |
59 | struct dwmci_socfpga_priv_data *priv = dev_get_priv(dev); |
60 | struct dwmci_host *host = &priv->host; | |
61 | int fifo_depth; | |
498d1a62 PM |
62 | |
63 | if (clk == 0) { | |
c35ed77a | 64 | printf("DWMMC: MMC clock is zero!"); |
129adf5b MV |
65 | return -EINVAL; |
66 | } | |
67 | ||
c35ed77a MV |
68 | fifo_depth = fdtdec_get_int(gd->fdt_blob, dev->of_offset, |
69 | "fifo-depth", 0); | |
129adf5b | 70 | if (fifo_depth < 0) { |
c35ed77a | 71 | printf("DWMMC: Can't get FIFO depth\n"); |
129adf5b MV |
72 | return -EINVAL; |
73 | } | |
74 | ||
c35ed77a MV |
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); | |
c5c1af21 | 79 | host->clksel = socfpga_dwmci_clksel; |
c35ed77a MV |
80 | |
81 | /* | |
82 | * TODO([email protected]): Remove the need for this hack. | |
83 | * We only have one dwmmc block on gen5 SoCFPGA. | |
84 | */ | |
85 | host->dev_index = 0; | |
129adf5b | 86 | /* Fixed clock divide by 4 which due to the SDMMC wrapper */ |
498d1a62 | 87 | host->bus_hz = clk; |
c5c1af21 | 88 | host->fifoth_val = MSIZE(0x2) | |
129adf5b | 89 | RX_WMARK(fifo_depth / 2 - 1) | TX_WMARK(fifo_depth / 2); |
c35ed77a MV |
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); | |
9a41404d | 94 | host->priv = priv; |
c5c1af21 | 95 | |
129adf5b MV |
96 | return 0; |
97 | } | |
98 | ||
c35ed77a | 99 | static int socfpga_dwmmc_probe(struct udevice *dev) |
129adf5b | 100 | { |
c35ed77a MV |
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; | |
129adf5b | 105 | |
c35ed77a MV |
106 | ret = add_dwmci(host, host->bus_hz, 400000); |
107 | if (ret) | |
108 | return ret; | |
129adf5b | 109 | |
c35ed77a | 110 | upriv->mmc = host->mmc; |
cffe5d86 | 111 | host->mmc->dev = dev; |
129adf5b | 112 | |
c35ed77a | 113 | return 0; |
129adf5b | 114 | } |
c35ed77a MV |
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 | }; |