blob: 7168679f90d258bdd0debeab233fde63141810f7 [file] [log] [blame]
Jun Nie8b659132018-06-28 16:38:02 +08001/*
2 * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
Ghennadi Procopciuca59d43f2025-03-17 12:21:13 +02003 * Copyright 2025 NXP
Jun Nie8b659132018-06-28 16:38:02 +08004 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
Antonio Nino Diaz09d40e02018-12-14 00:18:21 +00008#include <assert.h>
9#include <errno.h>
10#include <string.h>
11
Jun Nie8b659132018-06-28 16:38:02 +080012#include <arch.h>
13#include <arch_helpers.h>
Antonio Nino Diaz09d40e02018-12-14 00:18:21 +000014#include <common/debug.h>
15#include <drivers/delay_timer.h>
16#include <drivers/mmc.h>
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +030017#include <lib/mmio_poll.h>
Ghennadi Procopciuccdf002d2025-03-28 08:58:39 +020018#include <lib/xlat_tables/xlat_tables_v2.h>
Antonio Nino Diaz09d40e02018-12-14 00:18:21 +000019
Jun Nie8b659132018-06-28 16:38:02 +080020#include <imx_usdhc.h>
Jun Nie8b659132018-06-28 16:38:02 +080021
Ghennadi Procopciuca59d43f2025-03-17 12:21:13 +020022/* These masks represent the commands which involve a data transfer. */
23#define ADTC_MASK_SD (BIT_32(6U) | BIT_32(17U) | BIT_32(18U) |\
24 BIT_32(24U) | BIT_32(25U))
25#define ADTC_MASK_ACMD (BIT_64(51U))
26
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +030027#define USDHC_TIMEOUT_US (1U * 1000U) /* 1 msec */
28#define USDHC_TRANSFER_TIMEOUT (1U * 1000U * 1000U) /* 1 sec */
29
Ghennadi Procopciucb61379f2025-03-17 22:54:41 +020030struct imx_usdhc_device_data {
31 uint32_t addr;
32 uint32_t blk_size;
33 uint32_t blks;
34 bool valid;
35};
36
Jun Nie8b659132018-06-28 16:38:02 +080037static void imx_usdhc_initialize(void);
38static int imx_usdhc_send_cmd(struct mmc_cmd *cmd);
39static int imx_usdhc_set_ios(unsigned int clk, unsigned int width);
40static int imx_usdhc_prepare(int lba, uintptr_t buf, size_t size);
41static int imx_usdhc_read(int lba, uintptr_t buf, size_t size);
42static int imx_usdhc_write(int lba, uintptr_t buf, size_t size);
43
44static const struct mmc_ops imx_usdhc_ops = {
45 .init = imx_usdhc_initialize,
46 .send_cmd = imx_usdhc_send_cmd,
47 .set_ios = imx_usdhc_set_ios,
48 .prepare = imx_usdhc_prepare,
49 .read = imx_usdhc_read,
50 .write = imx_usdhc_write,
51};
52
53static imx_usdhc_params_t imx_usdhc_params;
Ghennadi Procopciucb61379f2025-03-17 22:54:41 +020054static struct imx_usdhc_device_data imx_usdhc_data;
55
56static bool imx_usdhc_is_buf_valid(void)
57{
58 return imx_usdhc_data.valid;
59}
60
61static bool imx_usdhc_is_buf_multiblk(void)
62{
63 return imx_usdhc_data.blks > 1U;
64}
65
66static void imx_usdhc_inval_buf_data(void)
67{
68 imx_usdhc_data.valid = false;
69}
70
71static int imx_usdhc_save_buf_data(uintptr_t buf, size_t size)
72{
73 uint32_t block_size;
74 uint64_t blks;
75
76 if (size <= MMC_BLOCK_SIZE) {
77 block_size = (uint32_t)size;
78 } else {
79 block_size = MMC_BLOCK_SIZE;
80 }
81
82 if (buf > UINT32_MAX) {
83 return -EOVERFLOW;
84 }
85
86 imx_usdhc_data.addr = (uint32_t)buf;
87 imx_usdhc_data.blk_size = block_size;
88 blks = size / block_size;
89 imx_usdhc_data.blks = (uint32_t)blks;
90
91 imx_usdhc_data.valid = true;
92
93 return 0;
94}
95
96static void imx_usdhc_write_buf_data(void)
97{
98 uintptr_t reg_base = imx_usdhc_params.reg_base;
99 uint32_t addr, blks, blk_size;
100
101 addr = imx_usdhc_data.addr;
102 blks = imx_usdhc_data.blks;
103 blk_size = imx_usdhc_data.blk_size;
104
105 mmio_write_32(reg_base + DSADDR, addr);
106 mmio_write_32(reg_base + BLKATT, BLKATT_BLKCNT(blks) |
107 BLKATT_BLKSIZE(blk_size));
108}
Jun Nie8b659132018-06-28 16:38:02 +0800109
110#define IMX7_MMC_SRC_CLK_RATE (200 * 1000 * 1000)
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300111static int imx_usdhc_set_clk(unsigned int clk)
Jun Nie8b659132018-06-28 16:38:02 +0800112{
Jun Nie8b659132018-06-28 16:38:02 +0800113 unsigned int sdhc_clk = IMX7_MMC_SRC_CLK_RATE;
114 uintptr_t reg_base = imx_usdhc_params.reg_base;
Ghennadi Procopciuc2e90f3e2025-03-28 08:37:33 +0200115 unsigned int pre_div = 1U, div = 1U;
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300116 uint32_t pstate;
117 int ret;
Jun Nie8b659132018-06-28 16:38:02 +0800118
119 assert(clk > 0);
120
121 while (sdhc_clk / (16 * pre_div) > clk && pre_div < 256)
122 pre_div *= 2;
123
Ghennadi Procopciuc2e90f3e2025-03-28 08:37:33 +0200124 while (((sdhc_clk / (div * pre_div)) > clk) && (div < 16U)) {
Jun Nie8b659132018-06-28 16:38:02 +0800125 div++;
Ghennadi Procopciuc2e90f3e2025-03-28 08:37:33 +0200126 }
Jun Nie8b659132018-06-28 16:38:02 +0800127
128 pre_div >>= 1;
129 div -= 1;
130 clk = (pre_div << 8) | (div << 4);
131
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300132 ret = mmio_read_32_poll_timeout(reg_base + PSTATE, pstate,
133 (pstate & PSTATE_SDSTB) != 0U,
134 USDHC_TIMEOUT_US);
135 if (ret == -ETIMEDOUT) {
136 ERROR("Unstable SD clock\n");
137 return ret;
Ghennadi Procopciuc583a5442025-03-28 08:44:47 +0200138 }
139
Jun Nie8b659132018-06-28 16:38:02 +0800140 mmio_clrbits32(reg_base + VENDSPEC, VENDSPEC_CARD_CLKEN);
141 mmio_clrsetbits32(reg_base + SYSCTRL, SYSCTRL_CLOCK_MASK, clk);
142 udelay(10000);
143
144 mmio_setbits32(reg_base + VENDSPEC, VENDSPEC_PER_CLKEN | VENDSPEC_CARD_CLKEN);
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300145
146 return 0;
Jun Nie8b659132018-06-28 16:38:02 +0800147}
148
149static void imx_usdhc_initialize(void)
150{
Jun Nie8b659132018-06-28 16:38:02 +0800151 uintptr_t reg_base = imx_usdhc_params.reg_base;
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300152 uint32_t sysctrl;
153 int ret;
Jun Nie8b659132018-06-28 16:38:02 +0800154
155 assert((imx_usdhc_params.reg_base & MMC_BLOCK_MASK) == 0);
156
157 /* reset the controller */
158 mmio_setbits32(reg_base + SYSCTRL, SYSCTRL_RSTA);
159
160 /* wait for reset done */
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300161 ret = mmio_read_32_poll_timeout(reg_base + SYSCTRL, sysctrl,
162 (sysctrl & SYSCTRL_RSTA) == 0U,
163 USDHC_TIMEOUT_US);
164 if (ret == -ETIMEDOUT) {
165 ERROR("Failed to reset the USDHC controller\n");
166 panic();
Jun Nie8b659132018-06-28 16:38:02 +0800167 }
168
169 mmio_write_32(reg_base + MMCBOOT, 0);
170 mmio_write_32(reg_base + MIXCTRL, 0);
171 mmio_write_32(reg_base + CLKTUNECTRLSTS, 0);
172
173 mmio_write_32(reg_base + VENDSPEC, VENDSPEC_INIT);
174 mmio_write_32(reg_base + DLLCTRL, 0);
175 mmio_setbits32(reg_base + VENDSPEC, VENDSPEC_IPG_CLKEN | VENDSPEC_PER_CLKEN);
176
177 /* Set the initial boot clock rate */
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300178 ret = imx_usdhc_set_clk(MMC_BOOT_CLK_RATE);
179 if (ret != 0) {
180 panic();
181 }
182
Jun Nie8b659132018-06-28 16:38:02 +0800183 udelay(100);
184
185 /* Clear read/write ready status */
186 mmio_clrbits32(reg_base + INTSTATEN, INTSTATEN_BRR | INTSTATEN_BWR);
187
188 /* configure as little endian */
189 mmio_write_32(reg_base + PROTCTRL, PROTCTRL_LE);
190
191 /* Set timeout to the maximum value */
192 mmio_clrsetbits32(reg_base + SYSCTRL, SYSCTRL_TIMEOUT_MASK,
193 SYSCTRL_TIMEOUT(15));
194
195 /* set wartermark level as 16 for safe for MMC */
196 mmio_clrsetbits32(reg_base + WATERMARKLEV, WMKLV_MASK, 16 | (16 << 16));
197}
198
Ghennadi Procopciuc13a839a2025-03-17 15:17:54 +0200199static bool is_data_transfer_to_card(const struct mmc_cmd *cmd)
200{
201 unsigned int cmd_idx = cmd->cmd_idx;
202
203 return (cmd_idx == MMC_CMD(24)) || (cmd_idx == MMC_CMD(25));
204}
205
Ghennadi Procopciuca59d43f2025-03-17 12:21:13 +0200206static bool is_data_transfer_cmd(const struct mmc_cmd *cmd)
207{
208 uintptr_t reg_base = imx_usdhc_params.reg_base;
209 unsigned int cmd_idx = cmd->cmd_idx;
210 uint32_t xfer_type;
211
212 xfer_type = mmio_read_32(reg_base + XFERTYPE);
213
214 if (XFERTYPE_GET_CMD(xfer_type) == MMC_CMD(55)) {
215 return (ADTC_MASK_ACMD & BIT_64(cmd_idx)) != 0ULL;
216 }
217
218 if ((ADTC_MASK_SD & BIT_32(cmd->cmd_idx)) != 0U) {
219 return true;
220 }
221
222 return false;
223}
224
225static int get_xfr_type(const struct mmc_cmd *cmd, bool data, uint32_t *xfertype)
226{
227 *xfertype = XFERTYPE_CMD(cmd->cmd_idx);
228
229 switch (cmd->resp_type) {
230 case MMC_RESPONSE_R2:
231 *xfertype |= XFERTYPE_RSPTYP_136;
232 *xfertype |= XFERTYPE_CCCEN;
233 break;
234 case MMC_RESPONSE_R4:
235 *xfertype |= XFERTYPE_RSPTYP_48;
236 break;
237 case MMC_RESPONSE_R6:
238 *xfertype |= XFERTYPE_RSPTYP_48;
239 *xfertype |= XFERTYPE_CICEN;
240 *xfertype |= XFERTYPE_CCCEN;
241 break;
242 case MMC_RESPONSE_R1B:
243 *xfertype |= XFERTYPE_RSPTYP_48_BUSY;
244 *xfertype |= XFERTYPE_CICEN;
245 *xfertype |= XFERTYPE_CCCEN;
246 break;
Ghennadi Procopciuc92a7b542025-06-11 15:55:23 +0300247 case MMC_RESPONSE_NONE:
248 break;
Ghennadi Procopciuca59d43f2025-03-17 12:21:13 +0200249 default:
250 ERROR("Invalid CMD response: %u\n", cmd->resp_type);
251 return -EINVAL;
252 }
253
254 if (data) {
255 *xfertype |= XFERTYPE_DPSEL;
256 }
257
258 return 0;
259}
260
Jun Nie8b659132018-06-28 16:38:02 +0800261static int imx_usdhc_send_cmd(struct mmc_cmd *cmd)
262{
263 uintptr_t reg_base = imx_usdhc_params.reg_base;
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300264 unsigned int flags = INTSTATEN_CC | INTSTATEN_CTOE;
265 uint32_t xfertype, pstate, intstat, sysctrl;
Ghennadi Procopciucf9ed8552025-03-17 22:17:16 +0200266 unsigned int mixctl = 0;
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300267 int err = 0, ret;
Ghennadi Procopciuca59d43f2025-03-17 12:21:13 +0200268 bool data;
Jun Nie8b659132018-06-28 16:38:02 +0800269
270 assert(cmd);
271
Ghennadi Procopciuca59d43f2025-03-17 12:21:13 +0200272 data = is_data_transfer_cmd(cmd);
273
274 err = get_xfr_type(cmd, data, &xfertype);
275 if (err != 0) {
276 return err;
277 }
278
Jun Nie8b659132018-06-28 16:38:02 +0800279 /* clear all irq status */
280 mmio_write_32(reg_base + INTSTAT, 0xffffffff);
281
282 /* Wait for the bus to be idle */
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300283 err = mmio_read_32_poll_timeout(reg_base + PSTATE, pstate,
284 (pstate & (PSTATE_CDIHB | PSTATE_CIHB)) == 0U,
285 USDHC_TIMEOUT_US);
286 if (err == -ETIMEDOUT) {
287 ERROR("Failed to wait an idle bus\n");
288 return err;
289 }
Jun Nie8b659132018-06-28 16:38:02 +0800290
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300291 err = mmio_read_32_poll_timeout(reg_base + PSTATE, pstate,
292 (pstate & PSTATE_DLA) == 0U,
293 USDHC_TIMEOUT_US);
294 if (err == -ETIMEDOUT) {
295 ERROR("Active data line during the uSDHC init\n");
296 return err;
297 }
Jun Nie8b659132018-06-28 16:38:02 +0800298
299 mmio_write_32(reg_base + INTSIGEN, 0);
Jun Nie8b659132018-06-28 16:38:02 +0800300
Jun Nie8b659132018-06-28 16:38:02 +0800301 if (data) {
Jun Nie8b659132018-06-28 16:38:02 +0800302 mixctl |= MIXCTRL_DMAEN;
303 }
304
Ghennadi Procopciuc13a839a2025-03-17 15:17:54 +0200305 if (!is_data_transfer_to_card(cmd)) {
306 mixctl |= MIXCTRL_DTDSEL;
307 }
308
Ghennadi Procopciucb61379f2025-03-17 22:54:41 +0200309 if ((cmd->cmd_idx != MMC_CMD(55)) && imx_usdhc_is_buf_valid()) {
310 if (imx_usdhc_is_buf_multiblk()) {
311 mixctl |= MIXCTRL_MSBSEL | MIXCTRL_BCEN;
312 }
313
314 imx_usdhc_write_buf_data();
315 imx_usdhc_inval_buf_data();
316 }
317
Jun Nie8b659132018-06-28 16:38:02 +0800318 /* Send the command */
319 mmio_write_32(reg_base + CMDARG, cmd->cmd_arg);
320 mmio_clrsetbits32(reg_base + MIXCTRL, MIXCTRL_DATMASK, mixctl);
321 mmio_write_32(reg_base + XFERTYPE, xfertype);
322
323 /* Wait for the command done */
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300324 err = mmio_read_32_poll_timeout(reg_base + INTSTAT, intstat,
325 (intstat & flags) != 0U,
326 USDHC_TIMEOUT_US);
327 if ((err == -ETIMEDOUT) || ((intstat & (INTSTATEN_CTOE | CMD_ERR)) != 0U)) {
328 if ((intstat & (INTSTATEN_CTOE | CMD_ERR)) != 0U) {
Jun Nie8b659132018-06-28 16:38:02 +0800329 err = -EIO;
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300330 }
Jun Nie8b659132018-06-28 16:38:02 +0800331 ERROR("imx_usdhc mmc cmd %d state 0x%x errno=%d\n",
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300332 cmd->cmd_idx, intstat, err);
Jun Nie8b659132018-06-28 16:38:02 +0800333 goto out;
334 }
335
336 /* Copy the response to the response buffer */
337 if (cmd->resp_type & MMC_RSP_136) {
338 unsigned int cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0;
339
340 cmdrsp3 = mmio_read_32(reg_base + CMDRSP3);
341 cmdrsp2 = mmio_read_32(reg_base + CMDRSP2);
342 cmdrsp1 = mmio_read_32(reg_base + CMDRSP1);
343 cmdrsp0 = mmio_read_32(reg_base + CMDRSP0);
344 cmd->resp_data[3] = (cmdrsp3 << 8) | (cmdrsp2 >> 24);
345 cmd->resp_data[2] = (cmdrsp2 << 8) | (cmdrsp1 >> 24);
346 cmd->resp_data[1] = (cmdrsp1 << 8) | (cmdrsp0 >> 24);
347 cmd->resp_data[0] = (cmdrsp0 << 8);
348 } else {
349 cmd->resp_data[0] = mmio_read_32(reg_base + CMDRSP0);
350 }
351
352 /* Wait until all of the blocks are transferred */
353 if (data) {
354 flags = DATA_COMPLETE;
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300355 err = mmio_read_32_poll_timeout(reg_base + INTSTAT, intstat,
356 (((intstat & (INTSTATEN_DTOE | DATA_ERR)) != 0U) ||
357 ((intstat & flags) == flags)),
358 USDHC_TRANSFER_TIMEOUT);
359 if ((intstat & (INTSTATEN_DTOE | DATA_ERR)) != 0U) {
360 err = -EIO;
361 ERROR("imx_usdhc mmc data state 0x%x\n", intstat);
362 goto out;
363 }
Jun Nie8b659132018-06-28 16:38:02 +0800364
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300365 if (err == -ETIMEDOUT) {
366 ERROR("Timeout in block transfer\n");
367 goto out;
368 }
Jun Nie8b659132018-06-28 16:38:02 +0800369 }
370
371out:
372 /* Reset CMD and DATA on error */
373 if (err) {
374 mmio_setbits32(reg_base + SYSCTRL, SYSCTRL_RSTC);
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300375 ret = mmio_read_32_poll_timeout(reg_base + SYSCTRL, sysctrl,
376 (sysctrl & SYSCTRL_RSTC) == 0U,
377 USDHC_TIMEOUT_US);
378 if (ret == -ETIMEDOUT) {
379 ERROR("Failed to reset the CMD line\n");
380 }
Jun Nie8b659132018-06-28 16:38:02 +0800381
382 if (data) {
383 mmio_setbits32(reg_base + SYSCTRL, SYSCTRL_RSTD);
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300384 ret = mmio_read_32_poll_timeout(reg_base + SYSCTRL, sysctrl,
385 (sysctrl & SYSCTRL_RSTD) == 0U,
386 USDHC_TIMEOUT_US);
387 if (ret == -ETIMEDOUT) {
388 ERROR("Failed to reset the data line\n");
389 }
Jun Nie8b659132018-06-28 16:38:02 +0800390 }
391 }
392
393 /* clear all irq status */
394 mmio_write_32(reg_base + INTSTAT, 0xffffffff);
395
396 return err;
397}
398
399static int imx_usdhc_set_ios(unsigned int clk, unsigned int width)
400{
401 uintptr_t reg_base = imx_usdhc_params.reg_base;
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300402 int ret;
Jun Nie8b659132018-06-28 16:38:02 +0800403
Ghennadi Procopciuc01d24d62025-06-06 17:46:20 +0300404 ret = imx_usdhc_set_clk(clk);
405 if (ret != 0) {
406 return ret;
407 }
Jun Nie8b659132018-06-28 16:38:02 +0800408
409 if (width == MMC_BUS_WIDTH_4)
410 mmio_clrsetbits32(reg_base + PROTCTRL, PROTCTRL_WIDTH_MASK,
411 PROTCTRL_WIDTH_4);
412 else if (width == MMC_BUS_WIDTH_8)
413 mmio_clrsetbits32(reg_base + PROTCTRL, PROTCTRL_WIDTH_MASK,
414 PROTCTRL_WIDTH_8);
415
416 return 0;
417}
418
419static int imx_usdhc_prepare(int lba, uintptr_t buf, size_t size)
420{
Ghennadi Procopciuc7e2a4342025-03-17 23:07:23 +0200421 flush_dcache_range(buf, size);
Ghennadi Procopciucb61379f2025-03-17 22:54:41 +0200422 return imx_usdhc_save_buf_data(buf, size);
Jun Nie8b659132018-06-28 16:38:02 +0800423}
424
425static int imx_usdhc_read(int lba, uintptr_t buf, size_t size)
426{
Ghennadi Procopciuc7e2a4342025-03-17 23:07:23 +0200427 inv_dcache_range(buf, size);
Jun Nie8b659132018-06-28 16:38:02 +0800428 return 0;
429}
430
431static int imx_usdhc_write(int lba, uintptr_t buf, size_t size)
432{
433 return 0;
434}
435
436void imx_usdhc_init(imx_usdhc_params_t *params,
437 struct mmc_device_info *mmc_dev_info)
438{
Ghennadi Procopciuccdf002d2025-03-28 08:58:39 +0200439 int ret __maybe_unused;
440
Jun Nie8b659132018-06-28 16:38:02 +0800441 assert((params != 0) &&
442 ((params->reg_base & MMC_BLOCK_MASK) == 0) &&
Jun Nie8b659132018-06-28 16:38:02 +0800443 ((params->bus_width == MMC_BUS_WIDTH_1) ||
444 (params->bus_width == MMC_BUS_WIDTH_4) ||
445 (params->bus_width == MMC_BUS_WIDTH_8)));
446
Ghennadi Procopciuccdf002d2025-03-28 08:58:39 +0200447#if PLAT_XLAT_TABLES_DYNAMIC
448 ret = mmap_add_dynamic_region(params->reg_base, params->reg_base,
449 PAGE_SIZE,
450 MT_DEVICE | MT_RW | MT_SECURE);
451 if (ret != 0) {
452 ERROR("Failed to map the uSDHC registers\n");
453 panic();
454 }
455#endif
456
Jun Nie8b659132018-06-28 16:38:02 +0800457 memcpy(&imx_usdhc_params, params, sizeof(imx_usdhc_params_t));
458 mmc_init(&imx_usdhc_ops, params->clk_rate, params->bus_width,
459 params->flags, mmc_dev_info);
460}