blob: 04f467327bf91c5ee49bf988c472a6607456f7ab [file] [log] [blame]
Haojian Zhuang06249dc2016-03-18 22:14:16 +08001/*
2 * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved.
3 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Haojian Zhuang06249dc2016-03-18 22:14:16 +08005 */
6
Haojian Zhuang06249dc2016-03-18 22:14:16 +08007#include <assert.h>
Haojian Zhuang06249dc2016-03-18 22:14:16 +08008#include <errno.h>
Haojian Zhuang06249dc2016-03-18 22:14:16 +08009#include <string.h>
10
Antonio Nino Diaze0f90632018-12-14 00:18:21 +000011#include <arch.h>
12#include <arch_helpers.h>
13#include <common/debug.h>
14#include <drivers/delay_timer.h>
15#include <drivers/mmc.h>
16#include <drivers/synopsys/dw_mmc.h>
Justin Chadwell0d309592019-07-03 14:14:46 +010017#include <lib/utils_def.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +000018#include <lib/mmio.h>
19
Haojian Zhuang06249dc2016-03-18 22:14:16 +080020#define DWMMC_CTRL (0x00)
21#define CTRL_IDMAC_EN (1 << 25)
22#define CTRL_DMA_EN (1 << 5)
23#define CTRL_INT_EN (1 << 4)
24#define CTRL_DMA_RESET (1 << 2)
25#define CTRL_FIFO_RESET (1 << 1)
26#define CTRL_RESET (1 << 0)
27#define CTRL_RESET_ALL (CTRL_DMA_RESET | CTRL_FIFO_RESET | \
28 CTRL_RESET)
29
30#define DWMMC_PWREN (0x04)
31#define DWMMC_CLKDIV (0x08)
32#define DWMMC_CLKSRC (0x0c)
33#define DWMMC_CLKENA (0x10)
34#define DWMMC_TMOUT (0x14)
35#define DWMMC_CTYPE (0x18)
36#define CTYPE_8BIT (1 << 16)
37#define CTYPE_4BIT (1)
38#define CTYPE_1BIT (0)
39
40#define DWMMC_BLKSIZ (0x1c)
41#define DWMMC_BYTCNT (0x20)
42#define DWMMC_INTMASK (0x24)
43#define INT_EBE (1 << 15)
44#define INT_SBE (1 << 13)
45#define INT_HLE (1 << 12)
46#define INT_FRUN (1 << 11)
47#define INT_DRT (1 << 9)
48#define INT_RTO (1 << 8)
49#define INT_DCRC (1 << 7)
50#define INT_RCRC (1 << 6)
51#define INT_RXDR (1 << 5)
52#define INT_TXDR (1 << 4)
53#define INT_DTO (1 << 3)
54#define INT_CMD_DONE (1 << 2)
55#define INT_RE (1 << 1)
56
57#define DWMMC_CMDARG (0x28)
58#define DWMMC_CMD (0x2c)
Justin Chadwell0d309592019-07-03 14:14:46 +010059#define CMD_START (U(1) << 31)
Haojian Zhuang06249dc2016-03-18 22:14:16 +080060#define CMD_USE_HOLD_REG (1 << 29) /* 0 if SDR50/100 */
61#define CMD_UPDATE_CLK_ONLY (1 << 21)
62#define CMD_SEND_INIT (1 << 15)
63#define CMD_STOP_ABORT_CMD (1 << 14)
64#define CMD_WAIT_PRVDATA_COMPLETE (1 << 13)
65#define CMD_WRITE (1 << 10)
66#define CMD_DATA_TRANS_EXPECT (1 << 9)
67#define CMD_CHECK_RESP_CRC (1 << 8)
68#define CMD_RESP_LEN (1 << 7)
69#define CMD_RESP_EXPECT (1 << 6)
70#define CMD(x) (x & 0x3f)
71
72#define DWMMC_RESP0 (0x30)
73#define DWMMC_RESP1 (0x34)
74#define DWMMC_RESP2 (0x38)
75#define DWMMC_RESP3 (0x3c)
76#define DWMMC_RINTSTS (0x44)
77#define DWMMC_STATUS (0x48)
78#define STATUS_DATA_BUSY (1 << 9)
79
80#define DWMMC_FIFOTH (0x4c)
81#define FIFOTH_TWMARK(x) (x & 0xfff)
82#define FIFOTH_RWMARK(x) ((x & 0x1ff) << 16)
83#define FIFOTH_DMA_BURST_SIZE(x) ((x & 0x7) << 28)
84
85#define DWMMC_DEBNCE (0x64)
86#define DWMMC_BMOD (0x80)
87#define BMOD_ENABLE (1 << 7)
88#define BMOD_FB (1 << 1)
89#define BMOD_SWRESET (1 << 0)
90
91#define DWMMC_DBADDR (0x88)
92#define DWMMC_IDSTS (0x8c)
93#define DWMMC_IDINTEN (0x90)
94#define DWMMC_CARDTHRCTL (0x100)
95#define CARDTHRCTL_RD_THR(x) ((x & 0xfff) << 16)
96#define CARDTHRCTL_RD_THR_EN (1 << 0)
97
98#define IDMAC_DES0_DIC (1 << 1)
99#define IDMAC_DES0_LD (1 << 2)
100#define IDMAC_DES0_FS (1 << 3)
101#define IDMAC_DES0_CH (1 << 4)
102#define IDMAC_DES0_ER (1 << 5)
103#define IDMAC_DES0_CES (1 << 30)
Justin Chadwell0d309592019-07-03 14:14:46 +0100104#define IDMAC_DES0_OWN (U(1) << 31)
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800105#define IDMAC_DES1_BS1(x) ((x) & 0x1fff)
106#define IDMAC_DES2_BS2(x) (((x) & 0x1fff) << 13)
107
108#define DWMMC_DMA_MAX_BUFFER_SIZE (512 * 8)
109
110#define DWMMC_8BIT_MODE (1 << 6)
111
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800112#define DWMMC_ADDRESS_MASK U(0x0f)
113
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800114#define TIMEOUT 100000
115
116struct dw_idmac_desc {
117 unsigned int des0;
118 unsigned int des1;
119 unsigned int des2;
120 unsigned int des3;
121};
122
123static void dw_init(void);
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800124static int dw_send_cmd(struct mmc_cmd *cmd);
125static int dw_set_ios(unsigned int clk, unsigned int width);
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800126static int dw_prepare(int lba, uintptr_t buf, size_t size);
127static int dw_read(int lba, uintptr_t buf, size_t size);
128static int dw_write(int lba, uintptr_t buf, size_t size);
129
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800130static const struct mmc_ops dw_mmc_ops = {
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800131 .init = dw_init,
132 .send_cmd = dw_send_cmd,
133 .set_ios = dw_set_ios,
134 .prepare = dw_prepare,
135 .read = dw_read,
136 .write = dw_write,
137};
138
139static dw_mmc_params_t dw_params;
140
141static void dw_update_clk(void)
142{
143 unsigned int data;
144
145 mmio_write_32(dw_params.reg_base + DWMMC_CMD,
146 CMD_WAIT_PRVDATA_COMPLETE | CMD_UPDATE_CLK_ONLY |
147 CMD_START);
148 while (1) {
149 data = mmio_read_32(dw_params.reg_base + DWMMC_CMD);
150 if ((data & CMD_START) == 0)
151 break;
152 data = mmio_read_32(dw_params.reg_base + DWMMC_RINTSTS);
Haojian Zhuang5f386c02018-01-11 13:49:56 +0800153 assert((data & INT_HLE) == 0);
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800154 }
155}
156
157static void dw_set_clk(int clk)
158{
159 unsigned int data;
160 int div;
161
162 assert(clk > 0);
163
164 for (div = 1; div < 256; div++) {
165 if ((dw_params.clk_rate / (2 * div)) <= clk) {
166 break;
167 }
168 }
169 assert(div < 256);
170
171 /* wait until controller is idle */
172 do {
173 data = mmio_read_32(dw_params.reg_base + DWMMC_STATUS);
174 } while (data & STATUS_DATA_BUSY);
175
176 /* disable clock before change clock rate */
177 mmio_write_32(dw_params.reg_base + DWMMC_CLKENA, 0);
178 dw_update_clk();
179
180 mmio_write_32(dw_params.reg_base + DWMMC_CLKDIV, div);
181 dw_update_clk();
182
183 /* enable clock */
184 mmio_write_32(dw_params.reg_base + DWMMC_CLKENA, 1);
185 mmio_write_32(dw_params.reg_base + DWMMC_CLKSRC, 0);
186 dw_update_clk();
187}
188
189static void dw_init(void)
190{
191 unsigned int data;
192 uintptr_t base;
193
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800194 assert((dw_params.reg_base & MMC_BLOCK_MASK) == 0);
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800195
196 base = dw_params.reg_base;
197 mmio_write_32(base + DWMMC_PWREN, 1);
198 mmio_write_32(base + DWMMC_CTRL, CTRL_RESET_ALL);
199 do {
200 data = mmio_read_32(base + DWMMC_CTRL);
201 } while (data);
202
203 /* enable DMA in CTRL */
204 data = CTRL_INT_EN | CTRL_DMA_EN | CTRL_IDMAC_EN;
205 mmio_write_32(base + DWMMC_CTRL, data);
206 mmio_write_32(base + DWMMC_RINTSTS, ~0);
207 mmio_write_32(base + DWMMC_INTMASK, 0);
208 mmio_write_32(base + DWMMC_TMOUT, ~0);
209 mmio_write_32(base + DWMMC_IDINTEN, ~0);
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800210 mmio_write_32(base + DWMMC_BLKSIZ, MMC_BLOCK_SIZE);
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800211 mmio_write_32(base + DWMMC_BYTCNT, 256 * 1024);
212 mmio_write_32(base + DWMMC_DEBNCE, 0x00ffffff);
213 mmio_write_32(base + DWMMC_BMOD, BMOD_SWRESET);
214 do {
215 data = mmio_read_32(base + DWMMC_BMOD);
216 } while (data & BMOD_SWRESET);
217 /* enable DMA in BMOD */
218 data |= BMOD_ENABLE | BMOD_FB;
219 mmio_write_32(base + DWMMC_BMOD, data);
220
221 udelay(100);
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800222 dw_set_clk(MMC_BOOT_CLK_RATE);
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800223 udelay(100);
224}
225
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800226static int dw_send_cmd(struct mmc_cmd *cmd)
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800227{
228 unsigned int op, data, err_mask;
229 uintptr_t base;
230 int timeout;
231
232 assert(cmd);
233
234 base = dw_params.reg_base;
235
236 switch (cmd->cmd_idx) {
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800237 case 0:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800238 op = CMD_SEND_INIT;
239 break;
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800240 case 12:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800241 op = CMD_STOP_ABORT_CMD;
242 break;
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800243 case 13:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800244 op = CMD_WAIT_PRVDATA_COMPLETE;
245 break;
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800246 case 8:
Tien Hock, Loh986e51d2019-03-12 14:36:19 +0800247 if (dw_params.mmc_dev_type == MMC_IS_EMMC)
248 op = CMD_DATA_TRANS_EXPECT | CMD_WAIT_PRVDATA_COMPLETE;
249 else
250 op = CMD_WAIT_PRVDATA_COMPLETE;
251 break;
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800252 case 17:
253 case 18:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800254 op = CMD_DATA_TRANS_EXPECT | CMD_WAIT_PRVDATA_COMPLETE;
255 break;
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800256 case 24:
257 case 25:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800258 op = CMD_WRITE | CMD_DATA_TRANS_EXPECT |
259 CMD_WAIT_PRVDATA_COMPLETE;
260 break;
Tien Hock, Loh986e51d2019-03-12 14:36:19 +0800261 case 51:
262 op = CMD_DATA_TRANS_EXPECT;
263 break;
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800264 default:
265 op = 0;
266 break;
267 }
268 op |= CMD_USE_HOLD_REG | CMD_START;
269 switch (cmd->resp_type) {
270 case 0:
271 break;
Jun Nie028cb122018-06-28 16:38:00 +0800272 case MMC_RESPONSE_R2:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800273 op |= CMD_RESP_EXPECT | CMD_CHECK_RESP_CRC |
274 CMD_RESP_LEN;
275 break;
Jun Nie028cb122018-06-28 16:38:00 +0800276 case MMC_RESPONSE_R3:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800277 op |= CMD_RESP_EXPECT;
278 break;
279 default:
280 op |= CMD_RESP_EXPECT | CMD_CHECK_RESP_CRC;
281 break;
282 }
283 timeout = TIMEOUT;
284 do {
285 data = mmio_read_32(base + DWMMC_STATUS);
286 if (--timeout <= 0)
287 panic();
288 } while (data & STATUS_DATA_BUSY);
289
290 mmio_write_32(base + DWMMC_RINTSTS, ~0);
291 mmio_write_32(base + DWMMC_CMDARG, cmd->cmd_arg);
292 mmio_write_32(base + DWMMC_CMD, op | cmd->cmd_idx);
293
294 err_mask = INT_EBE | INT_HLE | INT_RTO | INT_RCRC | INT_RE |
295 INT_DCRC | INT_DRT | INT_SBE;
296 timeout = TIMEOUT;
297 do {
298 udelay(500);
299 data = mmio_read_32(base + DWMMC_RINTSTS);
300
301 if (data & err_mask)
302 return -EIO;
303 if (data & INT_DTO)
304 break;
305 if (--timeout == 0) {
306 ERROR("%s, RINTSTS:0x%x\n", __func__, data);
307 panic();
308 }
309 } while (!(data & INT_CMD_DONE));
310
311 if (op & CMD_RESP_EXPECT) {
312 cmd->resp_data[0] = mmio_read_32(base + DWMMC_RESP0);
313 if (op & CMD_RESP_LEN) {
314 cmd->resp_data[1] = mmio_read_32(base + DWMMC_RESP1);
315 cmd->resp_data[2] = mmio_read_32(base + DWMMC_RESP2);
316 cmd->resp_data[3] = mmio_read_32(base + DWMMC_RESP3);
317 }
318 }
319 return 0;
320}
321
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800322static int dw_set_ios(unsigned int clk, unsigned int width)
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800323{
324 switch (width) {
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800325 case MMC_BUS_WIDTH_1:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800326 mmio_write_32(dw_params.reg_base + DWMMC_CTYPE, CTYPE_1BIT);
327 break;
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800328 case MMC_BUS_WIDTH_4:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800329 mmio_write_32(dw_params.reg_base + DWMMC_CTYPE, CTYPE_4BIT);
330 break;
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800331 case MMC_BUS_WIDTH_8:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800332 mmio_write_32(dw_params.reg_base + DWMMC_CTYPE, CTYPE_8BIT);
333 break;
334 default:
335 assert(0);
Jonathan Wright39b42212018-03-13 15:24:29 +0000336 break;
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800337 }
338 dw_set_clk(clk);
339 return 0;
340}
341
342static int dw_prepare(int lba, uintptr_t buf, size_t size)
343{
344 struct dw_idmac_desc *desc;
345 int desc_cnt, i, last;
346 uintptr_t base;
347
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800348 assert(((buf & DWMMC_ADDRESS_MASK) == 0) &&
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800349 (dw_params.desc_size > 0) &&
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800350 ((dw_params.reg_base & MMC_BLOCK_MASK) == 0) &&
351 ((dw_params.desc_base & MMC_BLOCK_MASK) == 0) &&
352 ((dw_params.desc_size & MMC_BLOCK_MASK) == 0));
353
354 flush_dcache_range(buf, size);
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800355
356 desc_cnt = (size + DWMMC_DMA_MAX_BUFFER_SIZE - 1) /
357 DWMMC_DMA_MAX_BUFFER_SIZE;
358 assert(desc_cnt * sizeof(struct dw_idmac_desc) < dw_params.desc_size);
359
360 base = dw_params.reg_base;
361 desc = (struct dw_idmac_desc *)dw_params.desc_base;
362 mmio_write_32(base + DWMMC_BYTCNT, size);
Tien Hock, Loh986e51d2019-03-12 14:36:19 +0800363
364 if (size < MMC_BLOCK_SIZE)
365 mmio_write_32(base + DWMMC_BLKSIZ, size);
366 else
367 mmio_write_32(base + DWMMC_BLKSIZ, MMC_BLOCK_SIZE);
368
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800369 mmio_write_32(base + DWMMC_RINTSTS, ~0);
370 for (i = 0; i < desc_cnt; i++) {
371 desc[i].des0 = IDMAC_DES0_OWN | IDMAC_DES0_CH | IDMAC_DES0_DIC;
372 desc[i].des1 = IDMAC_DES1_BS1(DWMMC_DMA_MAX_BUFFER_SIZE);
373 desc[i].des2 = buf + DWMMC_DMA_MAX_BUFFER_SIZE * i;
374 desc[i].des3 = dw_params.desc_base +
375 (sizeof(struct dw_idmac_desc)) * (i + 1);
376 }
377 /* first descriptor */
378 desc->des0 |= IDMAC_DES0_FS;
379 /* last descriptor */
380 last = desc_cnt - 1;
381 (desc + last)->des0 |= IDMAC_DES0_LD;
382 (desc + last)->des0 &= ~(IDMAC_DES0_DIC | IDMAC_DES0_CH);
383 (desc + last)->des1 = IDMAC_DES1_BS1(size - (last *
384 DWMMC_DMA_MAX_BUFFER_SIZE));
385 /* set next descriptor address as 0 */
386 (desc + last)->des3 = 0;
387
388 mmio_write_32(base + DWMMC_DBADDR, dw_params.desc_base);
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800389 flush_dcache_range(dw_params.desc_base,
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800390 desc_cnt * DWMMC_DMA_MAX_BUFFER_SIZE);
391
Tien Hock, Loh986e51d2019-03-12 14:36:19 +0800392
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800393 return 0;
394}
395
396static int dw_read(int lba, uintptr_t buf, size_t size)
397{
Tien Hock, Loh986e51d2019-03-12 14:36:19 +0800398 uint32_t data = 0;
399 int timeout = TIMEOUT;
400
401 do {
402 data = mmio_read_32(dw_params.reg_base + DWMMC_RINTSTS);
403 udelay(50);
404 } while (!(data & INT_DTO) && timeout-- > 0);
405
406 inv_dcache_range(buf, size);
407
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800408 return 0;
409}
410
411static int dw_write(int lba, uintptr_t buf, size_t size)
412{
413 return 0;
414}
415
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800416void dw_mmc_init(dw_mmc_params_t *params, struct mmc_device_info *info)
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800417{
418 assert((params != 0) &&
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800419 ((params->reg_base & MMC_BLOCK_MASK) == 0) &&
420 ((params->desc_base & MMC_BLOCK_MASK) == 0) &&
421 ((params->desc_size & MMC_BLOCK_MASK) == 0) &&
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800422 (params->desc_size > 0) &&
423 (params->clk_rate > 0) &&
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800424 ((params->bus_width == MMC_BUS_WIDTH_1) ||
425 (params->bus_width == MMC_BUS_WIDTH_4) ||
426 (params->bus_width == MMC_BUS_WIDTH_8)));
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800427
428 memcpy(&dw_params, params, sizeof(dw_mmc_params_t));
Tien Hock, Loh12560772019-03-22 12:54:31 +0800429 dw_params.mmc_dev_type = info->mmc_dev_type;
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800430 mmc_init(&dw_mmc_ops, params->clk_rate, params->bus_width,
431 params->flags, info);
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800432}