]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
79c83065 KY |
2 | /* |
3 | * (C) Copyright 2016 Fuzhou Rockchip Electronics Co., Ltd | |
4 | * | |
5 | * Rockchip SD Host Controller Interface | |
79c83065 KY |
6 | */ |
7 | ||
ac804143 | 8 | #include <clk.h> |
79c83065 | 9 | #include <dm.h> |
ac804143 | 10 | #include <dm/ofnode.h> |
c2868212 | 11 | #include <dt-structs.h> |
ac804143 | 12 | #include <linux/delay.h> |
61b29b82 | 13 | #include <linux/err.h> |
b08c8c48 | 14 | #include <linux/libfdt.h> |
ac804143 | 15 | #include <linux/iopoll.h> |
79c83065 | 16 | #include <malloc.h> |
c2868212 | 17 | #include <mapmem.h> |
ac804143 | 18 | #include "mmc_private.h" |
79c83065 | 19 | #include <sdhci.h> |
ac804143 YZ |
20 | #include <syscon.h> |
21 | #include <asm/arch-rockchip/clock.h> | |
22 | #include <asm/arch-rockchip/hardware.h> | |
79c83065 | 23 | |
c48021d1 ANY |
24 | /* DWCMSHC specific Mode Select value */ |
25 | #define DWCMSHC_CTRL_HS400 0x7 | |
79c83065 KY |
26 | /* 400KHz is max freq for card ID etc. Use that as min */ |
27 | #define EMMC_MIN_FREQ 400000 | |
ac804143 YZ |
28 | #define KHz (1000) |
29 | #define MHz (1000 * KHz) | |
30 | #define SDHCI_TUNING_LOOP_COUNT 40 | |
31 | ||
32 | #define PHYCTRL_CALDONE_MASK 0x1 | |
33 | #define PHYCTRL_CALDONE_SHIFT 0x6 | |
34 | #define PHYCTRL_CALDONE_DONE 0x1 | |
35 | #define PHYCTRL_DLLRDY_MASK 0x1 | |
36 | #define PHYCTRL_DLLRDY_SHIFT 0x5 | |
37 | #define PHYCTRL_DLLRDY_DONE 0x1 | |
38 | #define PHYCTRL_FREQSEL_200M 0x0 | |
39 | #define PHYCTRL_FREQSEL_50M 0x1 | |
40 | #define PHYCTRL_FREQSEL_100M 0x2 | |
41 | #define PHYCTRL_FREQSEL_150M 0x3 | |
42 | #define PHYCTRL_DLL_LOCK_WO_TMOUT(x) \ | |
43 | ((((x) >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK) ==\ | |
44 | PHYCTRL_DLLRDY_DONE) | |
79c83065 | 45 | |
c35af783 ANY |
46 | #define ARASAN_VENDOR_REGISTER 0x78 |
47 | #define ARASAN_VENDOR_ENHANCED_STROBE BIT(0) | |
48 | ||
8874c417 JK |
49 | /* Rockchip specific Registers */ |
50 | #define DWCMSHC_EMMC_EMMC_CTRL 0x52c | |
c48021d1 ANY |
51 | #define DWCMSHC_CARD_IS_EMMC BIT(0) |
52 | #define DWCMSHC_ENHANCED_STROBE BIT(8) | |
a63a57e5 YZ |
53 | #define DWCMSHC_EMMC_DLL_CTRL 0x800 |
54 | #define DWCMSHC_EMMC_DLL_CTRL_RESET BIT(1) | |
55 | #define DWCMSHC_EMMC_DLL_RXCLK 0x804 | |
56 | #define DWCMSHC_EMMC_DLL_TXCLK 0x808 | |
57 | #define DWCMSHC_EMMC_DLL_STRBIN 0x80c | |
a3cab289 | 58 | #define DWCMSHC_EMMC_DLL_CMDOUT 0x810 |
a63a57e5 YZ |
59 | #define DWCMSHC_EMMC_DLL_STATUS0 0x840 |
60 | #define DWCMSHC_EMMC_DLL_STATUS1 0x844 | |
61 | #define DWCMSHC_EMMC_DLL_START BIT(0) | |
8874c417 JK |
62 | #define DWCMSHC_EMMC_DLL_LOCKED BIT(8) |
63 | #define DWCMSHC_EMMC_DLL_TIMEOUT BIT(9) | |
a63a57e5 YZ |
64 | #define DWCMSHC_EMMC_DLL_START_POINT 16 |
65 | #define DWCMSHC_EMMC_DLL_START_DEFAULT 5 | |
66 | #define DWCMSHC_EMMC_DLL_INC_VALUE 2 | |
67 | #define DWCMSHC_EMMC_DLL_INC 8 | |
2321a991 | 68 | #define DWCMSHC_EMMC_DLL_BYPASS BIT(24) |
a63a57e5 | 69 | #define DWCMSHC_EMMC_DLL_DLYENA BIT(27) |
8874c417 JK |
70 | #define DLL_RXCLK_NO_INVERTER BIT(29) |
71 | #define DLL_RXCLK_ORI_GATE BIT(31) | |
d2cece03 | 72 | #define DLL_TXCLK_TAPNUM_DEFAULT 0x10 |
8874c417 | 73 | #define DLL_TXCLK_TAPNUM_FROM_SW BIT(24) |
a3cab289 | 74 | #define DLL_TXCLK_NO_INVERTER BIT(29) |
d2cece03 | 75 | #define DLL_STRBIN_TAPNUM_DEFAULT 0x4 |
c48021d1 ANY |
76 | #define DLL_STRBIN_TAPNUM_FROM_SW BIT(24) |
77 | #define DLL_STRBIN_DELAY_NUM_SEL BIT(26) | |
78 | #define DLL_STRBIN_DELAY_NUM_OFFSET 16 | |
d2cece03 | 79 | #define DLL_STRBIN_DELAY_NUM_DEFAULT 0x10 |
a3cab289 JK |
80 | #define DLL_CMDOUT_TAPNUM_90_DEGREES 0x8 |
81 | #define DLL_CMDOUT_TAPNUM_FROM_SW BIT(24) | |
82 | #define DLL_CMDOUT_SRC_CLK_NEG BIT(28) | |
83 | #define DLL_CMDOUT_EN_SRC_CLK_NEG BIT(29) | |
84 | #define DLL_CMDOUT_BOTH_CLK_EDGE BIT(30) | |
c48021d1 | 85 | |
a63a57e5 YZ |
86 | #define DLL_LOCK_WO_TMOUT(x) \ |
87 | ((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \ | |
88 | (((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0)) | |
89 | #define ROCKCHIP_MAX_CLKS 3 | |
90 | ||
a3cab289 JK |
91 | #define FLAG_INVERTER_FLAG_IN_RXCLK BIT(0) |
92 | ||
79c83065 KY |
93 | struct rockchip_sdhc_plat { |
94 | struct mmc_config cfg; | |
95 | struct mmc mmc; | |
96 | }; | |
97 | ||
ac804143 YZ |
98 | struct rockchip_emmc_phy { |
99 | u32 emmcphy_con[7]; | |
100 | u32 reserved; | |
101 | u32 emmcphy_status; | |
102 | }; | |
103 | ||
79c83065 KY |
104 | struct rockchip_sdhc { |
105 | struct sdhci_host host; | |
ac804143 | 106 | struct udevice *dev; |
79c83065 | 107 | void *base; |
ac804143 YZ |
108 | struct rockchip_emmc_phy *phy; |
109 | struct clk emmc_clk; | |
110 | }; | |
111 | ||
112 | struct sdhci_data { | |
ac804143 | 113 | int (*get_phy)(struct udevice *dev); |
ee5a284b ANY |
114 | |
115 | /** | |
116 | * set_control_reg() - Set SDHCI control registers | |
117 | * | |
118 | * This is the set_control_reg() SDHCI operation that should be | |
119 | * used for the hardware this driver data is associated with. | |
120 | * Normally, this is used to set up control registers for | |
121 | * voltage level and UHS speed mode. | |
122 | * | |
123 | * @host: SDHCI host structure | |
124 | */ | |
125 | void (*set_control_reg)(struct sdhci_host *host); | |
126 | ||
127 | /** | |
128 | * set_ios_post() - Host specific hook after set_ios() calls | |
129 | * | |
130 | * This is the set_ios_post() SDHCI operation that should be | |
131 | * used for the hardware this driver data is associated with. | |
132 | * Normally, this is a hook that is called after sdhci_set_ios() | |
133 | * that does any necessary host-specific configuration. | |
134 | * | |
135 | * @host: SDHCI host structure | |
136 | * Return: 0 if successful, -ve on error | |
137 | */ | |
138 | int (*set_ios_post)(struct sdhci_host *host); | |
c35af783 | 139 | |
7e74522d JK |
140 | void (*set_clock)(struct sdhci_host *host, u32 div); |
141 | int (*config_dll)(struct sdhci_host *host, u32 clock, bool enable); | |
142 | ||
c35af783 ANY |
143 | /** |
144 | * set_enhanced_strobe() - Set HS400 Enhanced Strobe config | |
145 | * | |
146 | * This is the set_enhanced_strobe() SDHCI operation that should | |
147 | * be used for the hardware this driver data is associated with. | |
148 | * Normally, this is used to set any host-specific configuration | |
149 | * necessary for HS400 ES. | |
150 | * | |
151 | * @host: SDHCI host structure | |
152 | * Return: 0 if successful, -ve on error | |
153 | */ | |
154 | int (*set_enhanced_strobe)(struct sdhci_host *host); | |
a3cab289 JK |
155 | |
156 | u32 flags; | |
157 | u8 hs200_txclk_tapnum; | |
158 | u8 hs400_txclk_tapnum; | |
ac804143 YZ |
159 | }; |
160 | ||
ac804143 YZ |
161 | static void rk3399_emmc_phy_power_on(struct rockchip_emmc_phy *phy, u32 clock) |
162 | { | |
163 | u32 caldone, dllrdy, freqsel; | |
164 | ||
165 | writel(RK_CLRSETBITS(7 << 4, 0), &phy->emmcphy_con[6]); | |
166 | writel(RK_CLRSETBITS(1 << 11, 1 << 11), &phy->emmcphy_con[0]); | |
167 | writel(RK_CLRSETBITS(0xf << 7, 6 << 7), &phy->emmcphy_con[0]); | |
168 | ||
169 | /* | |
170 | * According to the user manual, calpad calibration | |
171 | * cycle takes more than 2us without the minimal recommended | |
172 | * value, so we may need a little margin here | |
173 | */ | |
174 | udelay(3); | |
175 | writel(RK_CLRSETBITS(1, 1), &phy->emmcphy_con[6]); | |
176 | ||
177 | /* | |
178 | * According to the user manual, it asks driver to | |
179 | * wait 5us for calpad busy trimming. But it seems that | |
180 | * 5us of caldone isn't enough for all cases. | |
181 | */ | |
182 | udelay(500); | |
183 | caldone = readl(&phy->emmcphy_status); | |
184 | caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK; | |
185 | if (caldone != PHYCTRL_CALDONE_DONE) { | |
186 | printf("%s: caldone timeout.\n", __func__); | |
187 | return; | |
188 | } | |
189 | ||
190 | /* Set the frequency of the DLL operation */ | |
191 | if (clock < 75 * MHz) | |
192 | freqsel = PHYCTRL_FREQSEL_50M; | |
193 | else if (clock < 125 * MHz) | |
194 | freqsel = PHYCTRL_FREQSEL_100M; | |
195 | else if (clock < 175 * MHz) | |
196 | freqsel = PHYCTRL_FREQSEL_150M; | |
197 | else | |
198 | freqsel = PHYCTRL_FREQSEL_200M; | |
199 | ||
200 | /* Set the frequency of the DLL operation */ | |
201 | writel(RK_CLRSETBITS(3 << 12, freqsel << 12), &phy->emmcphy_con[0]); | |
202 | writel(RK_CLRSETBITS(1 << 1, 1 << 1), &phy->emmcphy_con[6]); | |
203 | ||
022f5527 YZ |
204 | /* REN Enable on STRB Line for HS400 */ |
205 | writel(RK_CLRSETBITS(0, 1 << 9), &phy->emmcphy_con[2]); | |
206 | ||
d7db0e6d AA |
207 | read_poll_timeout(readl, dllrdy, PHYCTRL_DLL_LOCK_WO_TMOUT(dllrdy), 1, |
208 | 5000, &phy->emmcphy_status); | |
ac804143 YZ |
209 | } |
210 | ||
211 | static void rk3399_emmc_phy_power_off(struct rockchip_emmc_phy *phy) | |
212 | { | |
213 | writel(RK_CLRSETBITS(1, 0), &phy->emmcphy_con[6]); | |
214 | writel(RK_CLRSETBITS(1 << 1, 0), &phy->emmcphy_con[6]); | |
215 | } | |
216 | ||
217 | static int rk3399_emmc_get_phy(struct udevice *dev) | |
218 | { | |
219 | struct rockchip_sdhc *priv = dev_get_priv(dev); | |
220 | ofnode phy_node; | |
221 | void *grf_base; | |
222 | u32 grf_phy_offset, phandle; | |
223 | ||
224 | phandle = dev_read_u32_default(dev, "phys", 0); | |
225 | phy_node = ofnode_get_by_phandle(phandle); | |
226 | if (!ofnode_valid(phy_node)) { | |
227 | debug("Not found emmc phy device\n"); | |
228 | return -ENODEV; | |
229 | } | |
230 | ||
231 | grf_base = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); | |
fdda7901 | 232 | if (IS_ERR_OR_NULL(grf_base)) { |
877bae24 | 233 | printf("%s: Get syscon grf failed\n", __func__); |
ac804143 YZ |
234 | return -ENODEV; |
235 | } | |
236 | grf_phy_offset = ofnode_read_u32_default(phy_node, "reg", 0); | |
237 | ||
238 | priv->phy = (struct rockchip_emmc_phy *)(grf_base + grf_phy_offset); | |
239 | ||
240 | return 0; | |
241 | } | |
242 | ||
c35af783 ANY |
243 | static int rk3399_sdhci_set_enhanced_strobe(struct sdhci_host *host) |
244 | { | |
245 | struct mmc *mmc = host->mmc; | |
246 | u32 vendor; | |
247 | ||
248 | vendor = sdhci_readl(host, ARASAN_VENDOR_REGISTER); | |
249 | if (mmc->selected_mode == MMC_HS_400_ES) | |
250 | vendor |= ARASAN_VENDOR_ENHANCED_STROBE; | |
251 | else | |
252 | vendor &= ~ARASAN_VENDOR_ENHANCED_STROBE; | |
253 | sdhci_writel(host, vendor, ARASAN_VENDOR_REGISTER); | |
254 | ||
255 | return 0; | |
256 | } | |
257 | ||
ee5a284b | 258 | static void rk3399_sdhci_set_control_reg(struct sdhci_host *host) |
ac804143 YZ |
259 | { |
260 | struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); | |
ee5a284b ANY |
261 | struct mmc *mmc = host->mmc; |
262 | uint clock = mmc->tran_speed; | |
ac804143 YZ |
263 | int cycle_phy = host->clock != clock && clock > EMMC_MIN_FREQ; |
264 | ||
265 | if (cycle_phy) | |
266 | rk3399_emmc_phy_power_off(priv->phy); | |
267 | ||
ee5a284b | 268 | sdhci_set_control_reg(host); |
c35af783 ANY |
269 | |
270 | /* | |
271 | * Reinitializing the device tries to set it to lower-speed modes | |
272 | * first, which fails if the Enhanced Strobe bit is set, making | |
273 | * the device impossible to use. Set the correct value here to | |
274 | * let reinitialization attempts succeed. | |
275 | */ | |
276 | if (CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)) | |
277 | rk3399_sdhci_set_enhanced_strobe(host); | |
ee5a284b ANY |
278 | }; |
279 | ||
280 | static int rk3399_sdhci_set_ios_post(struct sdhci_host *host) | |
281 | { | |
282 | struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); | |
283 | struct mmc *mmc = host->mmc; | |
284 | uint clock = mmc->tran_speed; | |
285 | int cycle_phy = host->clock != clock && clock > EMMC_MIN_FREQ; | |
286 | ||
287 | if (!clock) | |
288 | clock = mmc->clock; | |
ac804143 YZ |
289 | |
290 | if (cycle_phy) | |
291 | rk3399_emmc_phy_power_on(priv->phy, clock); | |
292 | ||
293 | return 0; | |
294 | } | |
295 | ||
b8a63c86 | 296 | static void rk3568_sdhci_set_clock(struct sdhci_host *host, u32 div) |
a63a57e5 YZ |
297 | { |
298 | struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); | |
b8a63c86 JK |
299 | struct mmc *mmc = host->mmc; |
300 | ulong rate; | |
301 | ||
302 | rate = clk_set_rate(&priv->emmc_clk, mmc->clock); | |
303 | if (IS_ERR_VALUE(rate)) | |
304 | printf("%s: Set clock rate failed: %ld\n", __func__, (long)rate); | |
305 | } | |
306 | ||
307 | static int rk3568_sdhci_config_dll(struct sdhci_host *host, u32 clock, bool enable) | |
308 | { | |
a3cab289 JK |
309 | struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); |
310 | struct sdhci_data *data = (struct sdhci_data *)dev_get_driver_data(priv->dev); | |
311 | struct mmc *mmc = host->mmc; | |
a63a57e5 | 312 | int val, ret; |
a3cab289 | 313 | u32 extra, txclk_tapnum; |
a63a57e5 | 314 | |
5c053f3a JK |
315 | if (!enable) { |
316 | sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL); | |
b8a63c86 | 317 | return 0; |
5c053f3a | 318 | } |
a63a57e5 YZ |
319 | |
320 | if (clock >= 100 * MHz) { | |
321 | /* reset DLL */ | |
322 | sdhci_writel(host, DWCMSHC_EMMC_DLL_CTRL_RESET, DWCMSHC_EMMC_DLL_CTRL); | |
323 | udelay(1); | |
324 | sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL); | |
325 | ||
326 | /* Init DLL settings */ | |
327 | extra = DWCMSHC_EMMC_DLL_START_DEFAULT << DWCMSHC_EMMC_DLL_START_POINT | | |
328 | DWCMSHC_EMMC_DLL_INC_VALUE << DWCMSHC_EMMC_DLL_INC | | |
329 | DWCMSHC_EMMC_DLL_START; | |
330 | sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_CTRL); | |
331 | ||
d7db0e6d AA |
332 | ret = read_poll_timeout(readl, val, DLL_LOCK_WO_TMOUT(val), 1, |
333 | 500, | |
334 | host->ioaddr + DWCMSHC_EMMC_DLL_STATUS0); | |
a63a57e5 YZ |
335 | if (ret) |
336 | return ret; | |
337 | ||
a3cab289 JK |
338 | extra = DWCMSHC_EMMC_DLL_DLYENA | DLL_RXCLK_ORI_GATE; |
339 | if (data->flags & FLAG_INVERTER_FLAG_IN_RXCLK) | |
340 | extra |= DLL_RXCLK_NO_INVERTER; | |
a63a57e5 YZ |
341 | sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK); |
342 | ||
a3cab289 JK |
343 | txclk_tapnum = data->hs200_txclk_tapnum; |
344 | if (mmc->selected_mode == MMC_HS_400 || | |
345 | mmc->selected_mode == MMC_HS_400_ES) { | |
346 | txclk_tapnum = data->hs400_txclk_tapnum; | |
347 | ||
348 | extra = DLL_CMDOUT_SRC_CLK_NEG | | |
349 | DLL_CMDOUT_BOTH_CLK_EDGE | | |
350 | DWCMSHC_EMMC_DLL_DLYENA | | |
351 | DLL_CMDOUT_TAPNUM_90_DEGREES | | |
352 | DLL_CMDOUT_TAPNUM_FROM_SW; | |
353 | sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_CMDOUT); | |
354 | } | |
355 | ||
a63a57e5 | 356 | extra = DWCMSHC_EMMC_DLL_DLYENA | |
a3cab289 JK |
357 | DLL_TXCLK_TAPNUM_FROM_SW | |
358 | DLL_TXCLK_NO_INVERTER | | |
359 | txclk_tapnum; | |
a63a57e5 YZ |
360 | sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_TXCLK); |
361 | ||
362 | extra = DWCMSHC_EMMC_DLL_DLYENA | | |
c48021d1 ANY |
363 | DLL_STRBIN_TAPNUM_DEFAULT | |
364 | DLL_STRBIN_TAPNUM_FROM_SW; | |
a63a57e5 YZ |
365 | sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); |
366 | } else { | |
2321a991 VK |
367 | /* |
368 | * Disable DLL and reset both of sample and drive clock. | |
369 | * The bypass bit and start bit need to be set if DLL is not locked. | |
370 | */ | |
8874c417 JK |
371 | extra = DWCMSHC_EMMC_DLL_BYPASS | DWCMSHC_EMMC_DLL_START; |
372 | sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_CTRL); | |
2321a991 | 373 | sdhci_writel(host, DLL_RXCLK_ORI_GATE, DWCMSHC_EMMC_DLL_RXCLK); |
a63a57e5 | 374 | sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK); |
a3cab289 | 375 | sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CMDOUT); |
c48021d1 ANY |
376 | /* |
377 | * Before switching to hs400es mode, the driver will enable | |
378 | * enhanced strobe first. PHY needs to configure the parameters | |
379 | * of enhanced strobe first. | |
380 | */ | |
381 | extra = DWCMSHC_EMMC_DLL_DLYENA | | |
382 | DLL_STRBIN_DELAY_NUM_SEL | | |
383 | DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET; | |
384 | sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); | |
a63a57e5 YZ |
385 | } |
386 | ||
387 | return 0; | |
388 | } | |
389 | ||
ee5a284b | 390 | static int rk3568_sdhci_set_ios_post(struct sdhci_host *host) |
ac804143 | 391 | { |
ac804143 | 392 | struct mmc *mmc = host->mmc; |
e82d4edf JK |
393 | struct rockchip_sdhc_plat *plat = dev_get_plat(mmc->dev); |
394 | struct mmc_config *cfg = &plat->cfg; | |
8874c417 | 395 | u32 reg; |
ac804143 | 396 | |
6de44385 JK |
397 | reg = sdhci_readw(host, SDHCI_HOST_CONTROL2); |
398 | reg &= ~SDHCI_CTRL_UHS_MASK; | |
399 | ||
400 | switch (mmc->selected_mode) { | |
401 | case UHS_SDR25: | |
402 | case MMC_HS: | |
403 | case MMC_HS_52: | |
404 | reg |= SDHCI_CTRL_UHS_SDR25; | |
405 | break; | |
406 | case UHS_SDR50: | |
407 | reg |= SDHCI_CTRL_UHS_SDR50; | |
408 | break; | |
409 | case UHS_DDR50: | |
410 | case MMC_DDR_52: | |
411 | reg |= SDHCI_CTRL_UHS_DDR50; | |
412 | break; | |
413 | case UHS_SDR104: | |
414 | case MMC_HS_200: | |
415 | reg |= SDHCI_CTRL_UHS_SDR104; | |
416 | break; | |
417 | case MMC_HS_400: | |
418 | case MMC_HS_400_ES: | |
c48021d1 | 419 | reg |= DWCMSHC_CTRL_HS400; |
6de44385 JK |
420 | break; |
421 | default: | |
422 | reg |= SDHCI_CTRL_UHS_SDR12; | |
423 | } | |
424 | ||
425 | sdhci_writew(host, reg, SDHCI_HOST_CONTROL2); | |
c48021d1 | 426 | |
8874c417 | 427 | reg = sdhci_readw(host, DWCMSHC_EMMC_EMMC_CTRL); |
6de44385 JK |
428 | |
429 | if (IS_MMC(mmc)) | |
c48021d1 | 430 | reg |= DWCMSHC_CARD_IS_EMMC; |
6de44385 JK |
431 | else |
432 | reg &= ~DWCMSHC_CARD_IS_EMMC; | |
433 | ||
434 | if (mmc->selected_mode == MMC_HS_400_ES) | |
435 | reg |= DWCMSHC_ENHANCED_STROBE; | |
436 | else | |
437 | reg &= ~DWCMSHC_ENHANCED_STROBE; | |
438 | ||
8874c417 | 439 | sdhci_writew(host, reg, DWCMSHC_EMMC_EMMC_CTRL); |
ac804143 | 440 | |
e82d4edf JK |
441 | /* |
442 | * Reading more than 4 blocks with a single CMD18 command in PIO mode | |
443 | * triggers Data End Bit Error using a slower mode than HS200. Limit to | |
444 | * reading max 4 blocks in one command when using PIO mode. | |
445 | */ | |
446 | if (!(host->flags & USE_DMA)) { | |
447 | if (mmc->selected_mode == MMC_HS_200 || | |
448 | mmc->selected_mode == MMC_HS_400 || | |
449 | mmc->selected_mode == MMC_HS_400_ES) | |
450 | cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; | |
451 | else | |
452 | cfg->b_max = 4; | |
453 | } | |
454 | ||
ac804143 YZ |
455 | return 0; |
456 | } | |
457 | ||
ee5a284b ANY |
458 | static void rockchip_sdhci_set_control_reg(struct sdhci_host *host) |
459 | { | |
460 | struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); | |
461 | struct sdhci_data *data = (struct sdhci_data *)dev_get_driver_data(priv->dev); | |
462 | ||
463 | if (data->set_control_reg) | |
464 | data->set_control_reg(host); | |
465 | } | |
466 | ||
467 | static int rockchip_sdhci_set_ios_post(struct sdhci_host *host) | |
468 | { | |
469 | struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); | |
470 | struct sdhci_data *data = (struct sdhci_data *)dev_get_driver_data(priv->dev); | |
471 | ||
472 | if (data->set_ios_post) | |
473 | return data->set_ios_post(host); | |
474 | ||
475 | return 0; | |
476 | } | |
477 | ||
7e74522d JK |
478 | static void rockchip_sdhci_set_clock(struct sdhci_host *host, u32 div) |
479 | { | |
480 | struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); | |
481 | struct sdhci_data *data = (struct sdhci_data *)dev_get_driver_data(priv->dev); | |
482 | ||
483 | if (data->set_clock) | |
484 | data->set_clock(host, div); | |
485 | } | |
486 | ||
ac804143 YZ |
487 | static int rockchip_sdhci_execute_tuning(struct mmc *mmc, u8 opcode) |
488 | { | |
b8c394b7 JK |
489 | struct rockchip_sdhc *priv = dev_get_priv(mmc->dev); |
490 | struct sdhci_host *host = &priv->host; | |
ac804143 YZ |
491 | char tuning_loop_counter = SDHCI_TUNING_LOOP_COUNT; |
492 | struct mmc_cmd cmd; | |
493 | u32 ctrl, blk_size; | |
ba9f5e54 | 494 | int ret; |
ac804143 YZ |
495 | |
496 | ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); | |
497 | ctrl |= SDHCI_CTRL_EXEC_TUNING; | |
498 | sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); | |
499 | ||
500 | sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE); | |
ac804143 YZ |
501 | |
502 | blk_size = SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, 64); | |
ba9f5e54 | 503 | if (opcode == MMC_CMD_SEND_TUNING_BLOCK_HS200 && mmc->bus_width == 8) |
ac804143 YZ |
504 | blk_size = SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, 128); |
505 | sdhci_writew(host, blk_size, SDHCI_BLOCK_SIZE); | |
506 | sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE); | |
507 | ||
508 | cmd.cmdidx = opcode; | |
509 | cmd.resp_type = MMC_RSP_R1; | |
510 | cmd.cmdarg = 0; | |
511 | ||
512 | do { | |
ba9f5e54 | 513 | ret = mmc_send_cmd(mmc, &cmd, NULL); |
ac804143 | 514 | ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); |
ba9f5e54 JK |
515 | if (ret || tuning_loop_counter-- == 0) |
516 | break; | |
ac804143 YZ |
517 | } while (ctrl & SDHCI_CTRL_EXEC_TUNING); |
518 | ||
ba9f5e54 JK |
519 | if (ret || tuning_loop_counter < 0 || !(ctrl & SDHCI_CTRL_TUNED_CLK)) { |
520 | if (!ret) | |
521 | ret = -EIO; | |
522 | printf("%s: Tuning failed: %d\n", __func__, ret); | |
ac804143 | 523 | |
ac804143 | 524 | ctrl &= ~SDHCI_CTRL_TUNED_CLK; |
ba9f5e54 JK |
525 | ctrl &= ~SDHCI_CTRL_EXEC_TUNING; |
526 | sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); | |
ac804143 YZ |
527 | } |
528 | ||
529 | /* Enable only interrupts served by the SD controller */ | |
530 | sdhci_writel(host, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK, SDHCI_INT_ENABLE); | |
ac804143 YZ |
531 | |
532 | return ret; | |
533 | } | |
534 | ||
7e74522d JK |
535 | static int rockchip_sdhci_config_dll(struct sdhci_host *host, u32 clock, bool enable) |
536 | { | |
537 | struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); | |
538 | struct sdhci_data *data = (struct sdhci_data *)dev_get_driver_data(priv->dev); | |
539 | ||
540 | if (data->config_dll) | |
541 | return data->config_dll(host, clock, enable); | |
542 | ||
543 | return 0; | |
544 | } | |
545 | ||
c35af783 ANY |
546 | static int rockchip_sdhci_set_enhanced_strobe(struct sdhci_host *host) |
547 | { | |
548 | struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); | |
549 | struct sdhci_data *data = (struct sdhci_data *)dev_get_driver_data(priv->dev); | |
550 | ||
551 | if (data->set_enhanced_strobe) | |
552 | return data->set_enhanced_strobe(host); | |
553 | ||
667576c5 | 554 | return 0; |
c35af783 ANY |
555 | } |
556 | ||
ac804143 | 557 | static struct sdhci_ops rockchip_sdhci_ops = { |
ee5a284b | 558 | .set_control_reg = rockchip_sdhci_set_control_reg, |
7e74522d JK |
559 | .set_ios_post = rockchip_sdhci_set_ios_post, |
560 | .set_clock = rockchip_sdhci_set_clock, | |
561 | .platform_execute_tuning = rockchip_sdhci_execute_tuning, | |
562 | .config_dll = rockchip_sdhci_config_dll, | |
c35af783 | 563 | .set_enhanced_strobe = rockchip_sdhci_set_enhanced_strobe, |
79c83065 KY |
564 | }; |
565 | ||
ac804143 | 566 | static int rockchip_sdhci_probe(struct udevice *dev) |
79c83065 | 567 | { |
ac804143 | 568 | struct sdhci_data *data = (struct sdhci_data *)dev_get_driver_data(dev); |
79c83065 | 569 | struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); |
c69cda25 | 570 | struct rockchip_sdhc_plat *plat = dev_get_plat(dev); |
b8c394b7 | 571 | struct rockchip_sdhc *priv = dev_get_priv(dev); |
ac804143 | 572 | struct mmc_config *cfg = &plat->cfg; |
b8c394b7 | 573 | struct sdhci_host *host = &priv->host; |
b2f571fe | 574 | struct clk *clk = &priv->emmc_clk; |
ac804143 | 575 | int ret; |
39fbb56f | 576 | |
ac804143 | 577 | host->max_clk = cfg->f_max; |
b2f571fe | 578 | ret = clk_get_by_index(dev, 0, clk); |
39fbb56f | 579 | if (!ret) { |
b2f571fe | 580 | ret = clk_set_rate(clk, host->max_clk); |
39fbb56f KY |
581 | if (IS_ERR_VALUE(ret)) |
582 | printf("%s clk set rate fail!\n", __func__); | |
b2f571fe | 583 | } else if (ret != -ENOSYS) { |
39fbb56f KY |
584 | printf("%s fail to get clk\n", __func__); |
585 | } | |
79c83065 | 586 | |
b8c394b7 | 587 | priv->dev = dev; |
ac804143 YZ |
588 | |
589 | if (data->get_phy) { | |
590 | ret = data->get_phy(dev); | |
591 | if (ret) | |
592 | return ret; | |
593 | } | |
594 | ||
ac804143 | 595 | host->ops = &rockchip_sdhci_ops; |
79c83065 KY |
596 | host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD; |
597 | ||
79c83065 | 598 | host->mmc = &plat->mmc; |
b8c394b7 | 599 | host->mmc->priv = &priv->host; |
79c83065 KY |
600 | host->mmc->dev = dev; |
601 | upriv->mmc = host->mmc; | |
602 | ||
ac804143 | 603 | ret = sdhci_setup_cfg(cfg, host, cfg->f_max, EMMC_MIN_FREQ); |
4dcdc5c1 KY |
604 | if (ret) |
605 | return ret; | |
606 | ||
3b804b37 JK |
607 | /* |
608 | * Disable use of DMA and force use of PIO mode in SPL to fix an issue | |
609 | * where loading part of TF-A into SRAM using DMA silently fails. | |
610 | */ | |
371dc068 | 611 | if (IS_ENABLED(CONFIG_XPL_BUILD) && |
3b804b37 JK |
612 | dev_read_bool(dev, "u-boot,spl-fifo-mode")) |
613 | host->flags &= ~USE_DMA; | |
614 | ||
79c83065 KY |
615 | return sdhci_probe(dev); |
616 | } | |
617 | ||
ac804143 | 618 | static int rockchip_sdhci_of_to_plat(struct udevice *dev) |
79c83065 | 619 | { |
ac804143 | 620 | struct rockchip_sdhc_plat *plat = dev_get_plat(dev); |
b8c394b7 | 621 | struct rockchip_sdhc *priv = dev_get_priv(dev); |
ac804143 | 622 | struct mmc_config *cfg = &plat->cfg; |
b8c394b7 | 623 | struct sdhci_host *host = &priv->host; |
ac804143 | 624 | int ret; |
79c83065 KY |
625 | |
626 | host->name = dev->name; | |
327b2b35 | 627 | host->ioaddr = dev_read_addr_ptr(dev); |
ac804143 YZ |
628 | |
629 | ret = mmc_of_parse(dev, cfg); | |
630 | if (ret) | |
631 | return ret; | |
79c83065 KY |
632 | |
633 | return 0; | |
634 | } | |
635 | ||
636 | static int rockchip_sdhci_bind(struct udevice *dev) | |
637 | { | |
c69cda25 | 638 | struct rockchip_sdhc_plat *plat = dev_get_plat(dev); |
79c83065 | 639 | |
24f5aec3 | 640 | return sdhci_bind(dev, &plat->mmc, &plat->cfg); |
79c83065 KY |
641 | } |
642 | ||
ac804143 | 643 | static const struct sdhci_data rk3399_data = { |
ac804143 | 644 | .get_phy = rk3399_emmc_get_phy, |
ee5a284b ANY |
645 | .set_control_reg = rk3399_sdhci_set_control_reg, |
646 | .set_ios_post = rk3399_sdhci_set_ios_post, | |
c35af783 | 647 | .set_enhanced_strobe = rk3399_sdhci_set_enhanced_strobe, |
ac804143 YZ |
648 | }; |
649 | ||
a63a57e5 | 650 | static const struct sdhci_data rk3568_data = { |
ee5a284b | 651 | .set_ios_post = rk3568_sdhci_set_ios_post, |
b8a63c86 JK |
652 | .set_clock = rk3568_sdhci_set_clock, |
653 | .config_dll = rk3568_sdhci_config_dll, | |
a3cab289 JK |
654 | .flags = FLAG_INVERTER_FLAG_IN_RXCLK, |
655 | .hs200_txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT, | |
5c053f3a | 656 | .hs400_txclk_tapnum = 0x8, |
a3cab289 JK |
657 | }; |
658 | ||
659 | static const struct sdhci_data rk3588_data = { | |
660 | .set_ios_post = rk3568_sdhci_set_ios_post, | |
661 | .set_clock = rk3568_sdhci_set_clock, | |
662 | .config_dll = rk3568_sdhci_config_dll, | |
663 | .hs200_txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT, | |
5c053f3a | 664 | .hs400_txclk_tapnum = 0x9, |
a63a57e5 YZ |
665 | }; |
666 | ||
ac804143 YZ |
667 | static const struct udevice_id sdhci_ids[] = { |
668 | { | |
669 | .compatible = "arasan,sdhci-5.1", | |
670 | .data = (ulong)&rk3399_data, | |
671 | }, | |
a63a57e5 YZ |
672 | { |
673 | .compatible = "rockchip,rk3568-dwcmshc", | |
674 | .data = (ulong)&rk3568_data, | |
675 | }, | |
a3cab289 JK |
676 | { |
677 | .compatible = "rockchip,rk3588-dwcmshc", | |
678 | .data = (ulong)&rk3588_data, | |
679 | }, | |
79c83065 KY |
680 | { } |
681 | }; | |
682 | ||
683 | U_BOOT_DRIVER(arasan_sdhci_drv) = { | |
ac804143 | 684 | .name = "rockchip_sdhci_5_1", |
79c83065 | 685 | .id = UCLASS_MMC, |
ac804143 YZ |
686 | .of_match = sdhci_ids, |
687 | .of_to_plat = rockchip_sdhci_of_to_plat, | |
79c83065 KY |
688 | .ops = &sdhci_ops, |
689 | .bind = rockchip_sdhci_bind, | |
ac804143 | 690 | .probe = rockchip_sdhci_probe, |
41575d8e | 691 | .priv_auto = sizeof(struct rockchip_sdhc), |
caa4daa2 | 692 | .plat_auto = sizeof(struct rockchip_sdhc_plat), |
79c83065 | 693 | }; |