]> Git Repo - u-boot.git/blob - drivers/mmc/nexell_dw_mmc.c
Merge branch 'qcom-main' of https://source.denx.de/u-boot/custodians/u-boot-snapdragon
[u-boot.git] / drivers / mmc / nexell_dw_mmc.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2016 Nexell
4  * Youngbok, Park <[email protected]>
5  *
6  * (C) Copyright 2019 Stefan Bosch <[email protected]>
7  */
8
9 #include <dm.h>
10 #include <dt-structs.h>
11 #include <dwmmc.h>
12 #include <log.h>
13 #include <syscon.h>
14 #include <asm/arch/reset.h>
15 #include <asm/arch/clk.h>
16
17 #define DWMCI_CLKSEL                    0x09C
18 #define DWMCI_SHIFT_0                   0x0
19 #define DWMCI_SHIFT_1                   0x1
20 #define DWMCI_SHIFT_2                   0x2
21 #define DWMCI_SHIFT_3                   0x3
22 #define DWMCI_SET_SAMPLE_CLK(x) (x)
23 #define DWMCI_SET_DRV_CLK(x)    ((x) << 16)
24 #define DWMCI_SET_DIV_RATIO(x)  ((x) << 24)
25 #define DWMCI_CLKCTRL                   0x114
26 #define NX_MMC_CLK_DELAY(x, y, a, b)    ((((x) & 0xFF) << 0) |\
27                                         (((y) & 0x03) << 16) |\
28                                         (((a) & 0xFF) << 8)  |\
29                                         (((b) & 0x03) << 24))
30
31 struct nexell_mmc_plat {
32         struct mmc_config cfg;
33         struct mmc mmc;
34 };
35
36 struct nexell_dwmmc_priv {
37         struct clk *clk;
38         struct dwmci_host host;
39         int fifo_size;
40         bool fifo_mode;
41         int frequency;
42         u32 min_freq;
43         u32 max_freq;
44         int d_delay;
45         int d_shift;
46         int s_delay;
47         int s_shift;
48         bool mmcboost;
49 };
50
51 struct clk *clk_get(const char *id);
52
53 static int nx_dw_mmc_clksel(struct dwmci_host *host)
54 {
55         /* host->priv is pointer to "struct udevice" */
56         struct nexell_dwmmc_priv *priv = dev_get_priv(host->priv);
57         u32 val;
58
59         if (priv->mmcboost)
60                 val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) |
61                       DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(1);
62         else
63                 val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) |
64                       DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(3);
65
66         dwmci_writel(host, DWMCI_CLKSEL, val);
67
68         return 0;
69 }
70
71 static void nx_dw_mmc_reset(int ch)
72 {
73         int rst_id = RESET_ID_SDMMC0 + ch;
74
75         nx_rstcon_setrst(rst_id, 0);
76         nx_rstcon_setrst(rst_id, 1);
77 }
78
79 static void nx_dw_mmc_clk_delay(struct udevice *dev)
80 {
81         unsigned int delay;
82         struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
83         struct dwmci_host *host = &priv->host;
84
85         delay = NX_MMC_CLK_DELAY(priv->d_delay,
86                                  priv->d_shift, priv->s_delay, priv->s_shift);
87
88         writel(delay, (host->ioaddr + DWMCI_CLKCTRL));
89         debug("%s: Values set: d_delay==%d, d_shift==%d, s_delay==%d, "
90               "s_shift==%d\n", __func__, priv->d_delay, priv->d_shift,
91               priv->s_delay, priv->s_shift);
92 }
93
94 static unsigned int nx_dw_mmc_get_clk(struct dwmci_host *host, uint freq)
95 {
96         struct clk *clk;
97         struct udevice *dev = host->priv;
98         struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
99
100         int index = host->dev_index;
101         char name[50] = { 0, };
102
103         clk = priv->clk;
104         if (!clk) {
105                 sprintf(name, "%s.%d", DEV_NAME_SDHC, index);
106                 clk = clk_get((const char *)name);
107                 if (!clk)
108                         return 0;
109                 priv->clk = clk;
110         }
111
112         return clk_get_rate(clk) / 2;
113 }
114
115 static unsigned long nx_dw_mmc_set_clk(struct dwmci_host *host,
116                                        unsigned int rate)
117 {
118         struct clk *clk;
119         char name[50] = { 0, };
120         struct udevice *dev = host->priv;
121         struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
122
123         int index = host->dev_index;
124
125         clk = priv->clk;
126         if (!clk) {
127                 sprintf(name, "%s.%d", DEV_NAME_SDHC, index);
128                 clk = clk_get((const char *)name);
129                 if (!clk) {
130                         debug("%s: clk_get(\"%s\") failed!\n", __func__, name);
131                         return 0;
132                 }
133                 priv->clk = clk;
134         }
135
136         clk_disable(clk);
137         rate = clk_set_rate(clk, rate);
138         clk_enable(clk);
139
140         return rate;
141 }
142
143 static int nexell_dwmmc_of_to_plat(struct udevice *dev)
144 {
145         struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
146         struct dwmci_host *host = &priv->host;
147         int val = -1;
148
149         debug("%s\n", __func__);
150
151         host->name = dev->name;
152         host->ioaddr = dev_read_addr_ptr(dev);
153         host->buswidth = dev_read_u32_default(dev, "bus-width", 4);
154         host->get_mmc_clk = nx_dw_mmc_get_clk;
155         host->clksel = nx_dw_mmc_clksel;
156         host->priv = dev;
157
158         val = dev_read_u32_default(dev, "index", -1);
159         if (val < 0 || val > 2) {
160                 debug("  'index' missing/invalid!\n");
161                 return -EINVAL;
162         }
163         host->dev_index = val;
164
165         priv->fifo_size = dev_read_u32_default(dev, "fifo-size", 0x20);
166         priv->fifo_mode = dev_read_bool(dev, "fifo-mode");
167         priv->frequency = dev_read_u32_default(dev, "frequency", 50000000);
168         priv->max_freq = dev_read_u32_default(dev, "max-frequency", 50000000);
169         priv->min_freq = 400000;  /* 400 kHz */
170         priv->d_delay = dev_read_u32_default(dev, "drive_dly", 0);
171         priv->d_shift = dev_read_u32_default(dev, "drive_shift", 3);
172         priv->s_delay = dev_read_u32_default(dev, "sample_dly", 0);
173         priv->s_shift = dev_read_u32_default(dev, "sample_shift", 2);
174         priv->mmcboost = dev_read_u32_default(dev, "mmcboost", 0);
175
176         debug("  index==%d, name==%s, ioaddr==0x%08x\n",
177               host->dev_index, host->name, (u32)host->ioaddr);
178         return 0;
179 }
180
181 static int nexell_dwmmc_probe(struct udevice *dev)
182 {
183         struct nexell_mmc_plat *plat = dev_get_plat(dev);
184         struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
185         struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
186         struct dwmci_host *host = &priv->host;
187         struct udevice *pwr_dev __maybe_unused;
188
189         host->fifoth_val = MSIZE(0x2) |
190                 RX_WMARK(priv->fifo_size / 2 - 1) |
191                 TX_WMARK(priv->fifo_size / 2);
192
193         host->fifo_mode = priv->fifo_mode;
194
195         dwmci_setup_cfg(&plat->cfg, host, priv->max_freq, priv->min_freq);
196         host->mmc = &plat->mmc;
197         host->mmc->priv = &priv->host;
198         host->mmc->dev = dev;
199         upriv->mmc = host->mmc;
200
201         if (nx_dw_mmc_set_clk(host, priv->frequency * 4) !=
202             priv->frequency * 4) {
203                 debug("%s: nx_dw_mmc_set_clk(host, %d) failed!\n",
204                       __func__, priv->frequency * 4);
205                 return -EIO;
206         }
207         debug("%s: nx_dw_mmc_set_clk(host, %d) OK\n",
208               __func__, priv->frequency * 4);
209
210         nx_dw_mmc_reset(host->dev_index);
211         nx_dw_mmc_clk_delay(dev);
212
213         return dwmci_probe(dev);
214 }
215
216 static int nexell_dwmmc_bind(struct udevice *dev)
217 {
218         struct nexell_mmc_plat *plat = dev_get_plat(dev);
219
220         return dwmci_bind(dev, &plat->mmc, &plat->cfg);
221 }
222
223 static const struct udevice_id nexell_dwmmc_ids[] = {
224         { .compatible = "nexell,nexell-dwmmc" },
225         { }
226 };
227
228 U_BOOT_DRIVER(nexell_dwmmc_drv) = {
229         .name           = "nexell_dwmmc",
230         .id             = UCLASS_MMC,
231         .of_match       = nexell_dwmmc_ids,
232         .of_to_plat = nexell_dwmmc_of_to_plat,
233         .ops            = &dm_dwmci_ops,
234         .bind           = nexell_dwmmc_bind,
235         .probe          = nexell_dwmmc_probe,
236         .priv_auto      = sizeof(struct nexell_dwmmc_priv),
237         .plat_auto      = sizeof(struct nexell_mmc_plat),
238 };
This page took 0.039356 seconds and 4 git commands to generate.