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