blob: c544233fc28f4c1207d807af50cb4fbc1715dc13 [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
7#include <arch.h>
8#include <arch_helpers.h>
9#include <assert.h>
10#include <debug.h>
11#include <delay_timer.h>
12#include <dw_mmc.h>
Haojian Zhuang06249dc2016-03-18 22:14:16 +080013#include <errno.h>
Haojian Zhuanga885d5e2018-08-04 18:06:52 +080014#include <mmc.h>
Haojian Zhuang06249dc2016-03-18 22:14:16 +080015#include <mmio.h>
16#include <string.h>
17
18#define DWMMC_CTRL (0x00)
19#define CTRL_IDMAC_EN (1 << 25)
20#define CTRL_DMA_EN (1 << 5)
21#define CTRL_INT_EN (1 << 4)
22#define CTRL_DMA_RESET (1 << 2)
23#define CTRL_FIFO_RESET (1 << 1)
24#define CTRL_RESET (1 << 0)
25#define CTRL_RESET_ALL (CTRL_DMA_RESET | CTRL_FIFO_RESET | \
26 CTRL_RESET)
27
28#define DWMMC_PWREN (0x04)
29#define DWMMC_CLKDIV (0x08)
30#define DWMMC_CLKSRC (0x0c)
31#define DWMMC_CLKENA (0x10)
32#define DWMMC_TMOUT (0x14)
33#define DWMMC_CTYPE (0x18)
34#define CTYPE_8BIT (1 << 16)
35#define CTYPE_4BIT (1)
36#define CTYPE_1BIT (0)
37
38#define DWMMC_BLKSIZ (0x1c)
39#define DWMMC_BYTCNT (0x20)
40#define DWMMC_INTMASK (0x24)
41#define INT_EBE (1 << 15)
42#define INT_SBE (1 << 13)
43#define INT_HLE (1 << 12)
44#define INT_FRUN (1 << 11)
45#define INT_DRT (1 << 9)
46#define INT_RTO (1 << 8)
47#define INT_DCRC (1 << 7)
48#define INT_RCRC (1 << 6)
49#define INT_RXDR (1 << 5)
50#define INT_TXDR (1 << 4)
51#define INT_DTO (1 << 3)
52#define INT_CMD_DONE (1 << 2)
53#define INT_RE (1 << 1)
54
55#define DWMMC_CMDARG (0x28)
56#define DWMMC_CMD (0x2c)
57#define CMD_START (1 << 31)
58#define CMD_USE_HOLD_REG (1 << 29) /* 0 if SDR50/100 */
59#define CMD_UPDATE_CLK_ONLY (1 << 21)
60#define CMD_SEND_INIT (1 << 15)
61#define CMD_STOP_ABORT_CMD (1 << 14)
62#define CMD_WAIT_PRVDATA_COMPLETE (1 << 13)
63#define CMD_WRITE (1 << 10)
64#define CMD_DATA_TRANS_EXPECT (1 << 9)
65#define CMD_CHECK_RESP_CRC (1 << 8)
66#define CMD_RESP_LEN (1 << 7)
67#define CMD_RESP_EXPECT (1 << 6)
68#define CMD(x) (x & 0x3f)
69
70#define DWMMC_RESP0 (0x30)
71#define DWMMC_RESP1 (0x34)
72#define DWMMC_RESP2 (0x38)
73#define DWMMC_RESP3 (0x3c)
74#define DWMMC_RINTSTS (0x44)
75#define DWMMC_STATUS (0x48)
76#define STATUS_DATA_BUSY (1 << 9)
77
78#define DWMMC_FIFOTH (0x4c)
79#define FIFOTH_TWMARK(x) (x & 0xfff)
80#define FIFOTH_RWMARK(x) ((x & 0x1ff) << 16)
81#define FIFOTH_DMA_BURST_SIZE(x) ((x & 0x7) << 28)
82
83#define DWMMC_DEBNCE (0x64)
84#define DWMMC_BMOD (0x80)
85#define BMOD_ENABLE (1 << 7)
86#define BMOD_FB (1 << 1)
87#define BMOD_SWRESET (1 << 0)
88
89#define DWMMC_DBADDR (0x88)
90#define DWMMC_IDSTS (0x8c)
91#define DWMMC_IDINTEN (0x90)
92#define DWMMC_CARDTHRCTL (0x100)
93#define CARDTHRCTL_RD_THR(x) ((x & 0xfff) << 16)
94#define CARDTHRCTL_RD_THR_EN (1 << 0)
95
96#define IDMAC_DES0_DIC (1 << 1)
97#define IDMAC_DES0_LD (1 << 2)
98#define IDMAC_DES0_FS (1 << 3)
99#define IDMAC_DES0_CH (1 << 4)
100#define IDMAC_DES0_ER (1 << 5)
101#define IDMAC_DES0_CES (1 << 30)
102#define IDMAC_DES0_OWN (1 << 31)
103#define IDMAC_DES1_BS1(x) ((x) & 0x1fff)
104#define IDMAC_DES2_BS2(x) (((x) & 0x1fff) << 13)
105
106#define DWMMC_DMA_MAX_BUFFER_SIZE (512 * 8)
107
108#define DWMMC_8BIT_MODE (1 << 6)
109
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800110#define DWMMC_ADDRESS_MASK U(0x0f)
111
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800112#define TIMEOUT 100000
113
114struct dw_idmac_desc {
115 unsigned int des0;
116 unsigned int des1;
117 unsigned int des2;
118 unsigned int des3;
119};
120
121static void dw_init(void);
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800122static int dw_send_cmd(struct mmc_cmd *cmd);
123static int dw_set_ios(unsigned int clk, unsigned int width);
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800124static int dw_prepare(int lba, uintptr_t buf, size_t size);
125static int dw_read(int lba, uintptr_t buf, size_t size);
126static int dw_write(int lba, uintptr_t buf, size_t size);
127
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800128static const struct mmc_ops dw_mmc_ops = {
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800129 .init = dw_init,
130 .send_cmd = dw_send_cmd,
131 .set_ios = dw_set_ios,
132 .prepare = dw_prepare,
133 .read = dw_read,
134 .write = dw_write,
135};
136
137static dw_mmc_params_t dw_params;
138
139static void dw_update_clk(void)
140{
141 unsigned int data;
142
143 mmio_write_32(dw_params.reg_base + DWMMC_CMD,
144 CMD_WAIT_PRVDATA_COMPLETE | CMD_UPDATE_CLK_ONLY |
145 CMD_START);
146 while (1) {
147 data = mmio_read_32(dw_params.reg_base + DWMMC_CMD);
148 if ((data & CMD_START) == 0)
149 break;
150 data = mmio_read_32(dw_params.reg_base + DWMMC_RINTSTS);
Haojian Zhuang5f386c02018-01-11 13:49:56 +0800151 assert((data & INT_HLE) == 0);
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800152 }
153}
154
155static void dw_set_clk(int clk)
156{
157 unsigned int data;
158 int div;
159
160 assert(clk > 0);
161
162 for (div = 1; div < 256; div++) {
163 if ((dw_params.clk_rate / (2 * div)) <= clk) {
164 break;
165 }
166 }
167 assert(div < 256);
168
169 /* wait until controller is idle */
170 do {
171 data = mmio_read_32(dw_params.reg_base + DWMMC_STATUS);
172 } while (data & STATUS_DATA_BUSY);
173
174 /* disable clock before change clock rate */
175 mmio_write_32(dw_params.reg_base + DWMMC_CLKENA, 0);
176 dw_update_clk();
177
178 mmio_write_32(dw_params.reg_base + DWMMC_CLKDIV, div);
179 dw_update_clk();
180
181 /* enable clock */
182 mmio_write_32(dw_params.reg_base + DWMMC_CLKENA, 1);
183 mmio_write_32(dw_params.reg_base + DWMMC_CLKSRC, 0);
184 dw_update_clk();
185}
186
187static void dw_init(void)
188{
189 unsigned int data;
190 uintptr_t base;
191
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800192 assert((dw_params.reg_base & MMC_BLOCK_MASK) == 0);
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800193
194 base = dw_params.reg_base;
195 mmio_write_32(base + DWMMC_PWREN, 1);
196 mmio_write_32(base + DWMMC_CTRL, CTRL_RESET_ALL);
197 do {
198 data = mmio_read_32(base + DWMMC_CTRL);
199 } while (data);
200
201 /* enable DMA in CTRL */
202 data = CTRL_INT_EN | CTRL_DMA_EN | CTRL_IDMAC_EN;
203 mmio_write_32(base + DWMMC_CTRL, data);
204 mmio_write_32(base + DWMMC_RINTSTS, ~0);
205 mmio_write_32(base + DWMMC_INTMASK, 0);
206 mmio_write_32(base + DWMMC_TMOUT, ~0);
207 mmio_write_32(base + DWMMC_IDINTEN, ~0);
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800208 mmio_write_32(base + DWMMC_BLKSIZ, MMC_BLOCK_SIZE);
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800209 mmio_write_32(base + DWMMC_BYTCNT, 256 * 1024);
210 mmio_write_32(base + DWMMC_DEBNCE, 0x00ffffff);
211 mmio_write_32(base + DWMMC_BMOD, BMOD_SWRESET);
212 do {
213 data = mmio_read_32(base + DWMMC_BMOD);
214 } while (data & BMOD_SWRESET);
215 /* enable DMA in BMOD */
216 data |= BMOD_ENABLE | BMOD_FB;
217 mmio_write_32(base + DWMMC_BMOD, data);
218
219 udelay(100);
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800220 dw_set_clk(MMC_BOOT_CLK_RATE);
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800221 udelay(100);
222}
223
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800224static int dw_send_cmd(struct mmc_cmd *cmd)
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800225{
226 unsigned int op, data, err_mask;
227 uintptr_t base;
228 int timeout;
229
230 assert(cmd);
231
232 base = dw_params.reg_base;
233
234 switch (cmd->cmd_idx) {
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800235 case 0:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800236 op = CMD_SEND_INIT;
237 break;
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800238 case 12:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800239 op = CMD_STOP_ABORT_CMD;
240 break;
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800241 case 13:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800242 op = CMD_WAIT_PRVDATA_COMPLETE;
243 break;
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800244 case 8:
245 case 17:
246 case 18:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800247 op = CMD_DATA_TRANS_EXPECT | CMD_WAIT_PRVDATA_COMPLETE;
248 break;
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800249 case 24:
250 case 25:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800251 op = CMD_WRITE | CMD_DATA_TRANS_EXPECT |
252 CMD_WAIT_PRVDATA_COMPLETE;
253 break;
254 default:
255 op = 0;
256 break;
257 }
258 op |= CMD_USE_HOLD_REG | CMD_START;
259 switch (cmd->resp_type) {
260 case 0:
261 break;
Jun Nie028cb122018-06-28 16:38:00 +0800262 case MMC_RESPONSE_R2:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800263 op |= CMD_RESP_EXPECT | CMD_CHECK_RESP_CRC |
264 CMD_RESP_LEN;
265 break;
Jun Nie028cb122018-06-28 16:38:00 +0800266 case MMC_RESPONSE_R1:
267 case MMC_RESPONSE_R1B:
268 case MMC_RESPONSE_R3:
269 case MMC_RESPONSE_R5:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800270 op |= CMD_RESP_EXPECT;
271 break;
272 default:
273 op |= CMD_RESP_EXPECT | CMD_CHECK_RESP_CRC;
274 break;
275 }
276 timeout = TIMEOUT;
277 do {
278 data = mmio_read_32(base + DWMMC_STATUS);
279 if (--timeout <= 0)
280 panic();
281 } while (data & STATUS_DATA_BUSY);
282
283 mmio_write_32(base + DWMMC_RINTSTS, ~0);
284 mmio_write_32(base + DWMMC_CMDARG, cmd->cmd_arg);
285 mmio_write_32(base + DWMMC_CMD, op | cmd->cmd_idx);
286
287 err_mask = INT_EBE | INT_HLE | INT_RTO | INT_RCRC | INT_RE |
288 INT_DCRC | INT_DRT | INT_SBE;
289 timeout = TIMEOUT;
290 do {
291 udelay(500);
292 data = mmio_read_32(base + DWMMC_RINTSTS);
293
294 if (data & err_mask)
295 return -EIO;
296 if (data & INT_DTO)
297 break;
298 if (--timeout == 0) {
299 ERROR("%s, RINTSTS:0x%x\n", __func__, data);
300 panic();
301 }
302 } while (!(data & INT_CMD_DONE));
303
304 if (op & CMD_RESP_EXPECT) {
305 cmd->resp_data[0] = mmio_read_32(base + DWMMC_RESP0);
306 if (op & CMD_RESP_LEN) {
307 cmd->resp_data[1] = mmio_read_32(base + DWMMC_RESP1);
308 cmd->resp_data[2] = mmio_read_32(base + DWMMC_RESP2);
309 cmd->resp_data[3] = mmio_read_32(base + DWMMC_RESP3);
310 }
311 }
312 return 0;
313}
314
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800315static int dw_set_ios(unsigned int clk, unsigned int width)
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800316{
317 switch (width) {
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800318 case MMC_BUS_WIDTH_1:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800319 mmio_write_32(dw_params.reg_base + DWMMC_CTYPE, CTYPE_1BIT);
320 break;
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800321 case MMC_BUS_WIDTH_4:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800322 mmio_write_32(dw_params.reg_base + DWMMC_CTYPE, CTYPE_4BIT);
323 break;
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800324 case MMC_BUS_WIDTH_8:
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800325 mmio_write_32(dw_params.reg_base + DWMMC_CTYPE, CTYPE_8BIT);
326 break;
327 default:
328 assert(0);
Jonathan Wright39b42212018-03-13 15:24:29 +0000329 break;
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800330 }
331 dw_set_clk(clk);
332 return 0;
333}
334
335static int dw_prepare(int lba, uintptr_t buf, size_t size)
336{
337 struct dw_idmac_desc *desc;
338 int desc_cnt, i, last;
339 uintptr_t base;
340
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800341 assert(((buf & DWMMC_ADDRESS_MASK) == 0) &&
342 ((size % MMC_BLOCK_SIZE) == 0) &&
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800343 (dw_params.desc_size > 0) &&
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800344 ((dw_params.reg_base & MMC_BLOCK_MASK) == 0) &&
345 ((dw_params.desc_base & MMC_BLOCK_MASK) == 0) &&
346 ((dw_params.desc_size & MMC_BLOCK_MASK) == 0));
347
348 flush_dcache_range(buf, size);
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800349
350 desc_cnt = (size + DWMMC_DMA_MAX_BUFFER_SIZE - 1) /
351 DWMMC_DMA_MAX_BUFFER_SIZE;
352 assert(desc_cnt * sizeof(struct dw_idmac_desc) < dw_params.desc_size);
353
354 base = dw_params.reg_base;
355 desc = (struct dw_idmac_desc *)dw_params.desc_base;
356 mmio_write_32(base + DWMMC_BYTCNT, size);
357 mmio_write_32(base + DWMMC_RINTSTS, ~0);
358 for (i = 0; i < desc_cnt; i++) {
359 desc[i].des0 = IDMAC_DES0_OWN | IDMAC_DES0_CH | IDMAC_DES0_DIC;
360 desc[i].des1 = IDMAC_DES1_BS1(DWMMC_DMA_MAX_BUFFER_SIZE);
361 desc[i].des2 = buf + DWMMC_DMA_MAX_BUFFER_SIZE * i;
362 desc[i].des3 = dw_params.desc_base +
363 (sizeof(struct dw_idmac_desc)) * (i + 1);
364 }
365 /* first descriptor */
366 desc->des0 |= IDMAC_DES0_FS;
367 /* last descriptor */
368 last = desc_cnt - 1;
369 (desc + last)->des0 |= IDMAC_DES0_LD;
370 (desc + last)->des0 &= ~(IDMAC_DES0_DIC | IDMAC_DES0_CH);
371 (desc + last)->des1 = IDMAC_DES1_BS1(size - (last *
372 DWMMC_DMA_MAX_BUFFER_SIZE));
373 /* set next descriptor address as 0 */
374 (desc + last)->des3 = 0;
375
376 mmio_write_32(base + DWMMC_DBADDR, dw_params.desc_base);
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800377 flush_dcache_range(dw_params.desc_base,
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800378 desc_cnt * DWMMC_DMA_MAX_BUFFER_SIZE);
379
380 return 0;
381}
382
383static int dw_read(int lba, uintptr_t buf, size_t size)
384{
385 return 0;
386}
387
388static int dw_write(int lba, uintptr_t buf, size_t size)
389{
390 return 0;
391}
392
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800393void dw_mmc_init(dw_mmc_params_t *params, struct mmc_device_info *info)
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800394{
395 assert((params != 0) &&
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800396 ((params->reg_base & MMC_BLOCK_MASK) == 0) &&
397 ((params->desc_base & MMC_BLOCK_MASK) == 0) &&
398 ((params->desc_size & MMC_BLOCK_MASK) == 0) &&
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800399 (params->desc_size > 0) &&
400 (params->clk_rate > 0) &&
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800401 ((params->bus_width == MMC_BUS_WIDTH_1) ||
402 (params->bus_width == MMC_BUS_WIDTH_4) ||
403 (params->bus_width == MMC_BUS_WIDTH_8)));
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800404
405 memcpy(&dw_params, params, sizeof(dw_mmc_params_t));
Haojian Zhuanga885d5e2018-08-04 18:06:52 +0800406 mmc_init(&dw_mmc_ops, params->clk_rate, params->bus_width,
407 params->flags, info);
Haojian Zhuang06249dc2016-03-18 22:14:16 +0800408}