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