]>
Commit | Line | Data |
---|---|---|
3fe3b4fb D |
1 | /* |
2 | * Marvell MMC/SD/SDIO driver | |
3 | * | |
2591fbdb | 4 | * (C) Copyright 2012-2014 |
3fe3b4fb D |
5 | * Marvell Semiconductor <www.marvell.com> |
6 | * Written-by: Maen Suleiman, Gerald Kerma | |
7 | * | |
8 | * SPDX-License-Identifier: GPL-2.0+ | |
9 | */ | |
10 | ||
11 | #include <common.h> | |
915ffa52 | 12 | #include <errno.h> |
3fe3b4fb D |
13 | #include <malloc.h> |
14 | #include <part.h> | |
15 | #include <mmc.h> | |
16 | #include <asm/io.h> | |
17 | #include <asm/arch/cpu.h> | |
3dc23f78 | 18 | #include <asm/arch/soc.h> |
3fe3b4fb D |
19 | #include <mvebu_mmc.h> |
20 | ||
bcd06989 MS |
21 | DECLARE_GLOBAL_DATA_PTR; |
22 | ||
3fe3b4fb D |
23 | #define DRIVER_NAME "MVEBU_MMC" |
24 | ||
bcd06989 MS |
25 | #define MVEBU_TARGET_DRAM 0 |
26 | ||
28d27b79 GK |
27 | #define TIMEOUT_DELAY 5*CONFIG_SYS_HZ /* wait 5 seconds */ |
28 | ||
3fe3b4fb D |
29 | static void mvebu_mmc_write(u32 offs, u32 val) |
30 | { | |
31 | writel(val, CONFIG_SYS_MMC_BASE + (offs)); | |
32 | } | |
33 | ||
34 | static u32 mvebu_mmc_read(u32 offs) | |
35 | { | |
36 | return readl(CONFIG_SYS_MMC_BASE + (offs)); | |
37 | } | |
38 | ||
39 | static int mvebu_mmc_setup_data(struct mmc_data *data) | |
40 | { | |
41 | u32 ctrl_reg; | |
42 | ||
43 | debug("%s, data %s : blocks=%d blksz=%d\n", DRIVER_NAME, | |
44 | (data->flags & MMC_DATA_READ) ? "read" : "write", | |
45 | data->blocks, data->blocksize); | |
46 | ||
47 | /* default to maximum timeout */ | |
48 | ctrl_reg = mvebu_mmc_read(SDIO_HOST_CTRL); | |
49 | ctrl_reg |= SDIO_HOST_CTRL_TMOUT(SDIO_HOST_CTRL_TMOUT_MAX); | |
50 | mvebu_mmc_write(SDIO_HOST_CTRL, ctrl_reg); | |
51 | ||
52 | if (data->flags & MMC_DATA_READ) { | |
53 | mvebu_mmc_write(SDIO_SYS_ADDR_LOW, (u32)data->dest & 0xffff); | |
54 | mvebu_mmc_write(SDIO_SYS_ADDR_HI, (u32)data->dest >> 16); | |
55 | } else { | |
56 | mvebu_mmc_write(SDIO_SYS_ADDR_LOW, (u32)data->src & 0xffff); | |
57 | mvebu_mmc_write(SDIO_SYS_ADDR_HI, (u32)data->src >> 16); | |
58 | } | |
59 | ||
60 | mvebu_mmc_write(SDIO_BLK_COUNT, data->blocks); | |
61 | mvebu_mmc_write(SDIO_BLK_SIZE, data->blocksize); | |
62 | ||
63 | return 0; | |
64 | } | |
65 | ||
66 | static int mvebu_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, | |
67 | struct mmc_data *data) | |
68 | { | |
28d27b79 | 69 | ulong start; |
3fe3b4fb D |
70 | ushort waittype = 0; |
71 | ushort resptype = 0; | |
72 | ushort xfertype = 0; | |
73 | ushort resp_indx = 0; | |
74 | ||
fc0f25f9 GK |
75 | debug("%s: cmdidx [0x%x] resp_type[0x%x] cmdarg[0x%x]\n", |
76 | DRIVER_NAME, cmd->cmdidx, cmd->resp_type, cmd->cmdarg); | |
3fe3b4fb | 77 | |
3fe3b4fb D |
78 | debug("%s: cmd %d (hw state 0x%04x)\n", DRIVER_NAME, |
79 | cmd->cmdidx, mvebu_mmc_read(SDIO_HW_STATE)); | |
80 | ||
28d27b79 GK |
81 | /* |
82 | * Hardware weirdness. The FIFO_EMPTY bit of the HW_STATE | |
83 | * register is sometimes not set before a while when some | |
84 | * "unusual" data block sizes are used (such as with the SWITCH | |
85 | * command), even despite the fact that the XFER_DONE interrupt | |
86 | * was raised. And if another data transfer starts before | |
87 | * this bit comes to good sense (which eventually happens by | |
88 | * itself) then the new transfer simply fails with a timeout. | |
89 | */ | |
90 | if (!(mvebu_mmc_read(SDIO_HW_STATE) & CMD_FIFO_EMPTY)) { | |
91 | ushort hw_state, count = 0; | |
92 | ||
93 | start = get_timer(0); | |
94 | do { | |
95 | hw_state = mvebu_mmc_read(SDIO_HW_STATE); | |
96 | if ((get_timer(0) - start) > TIMEOUT_DELAY) { | |
97 | printf("%s : FIFO_EMPTY bit missing\n", | |
98 | DRIVER_NAME); | |
99 | break; | |
100 | } | |
101 | count++; | |
102 | } while (!(hw_state & CMD_FIFO_EMPTY)); | |
103 | debug("%s *** wait for FIFO_EMPTY bit (hw=0x%04x, count=%d, jiffies=%ld)\n", | |
104 | DRIVER_NAME, hw_state, count, (get_timer(0) - (start))); | |
3fe3b4fb D |
105 | } |
106 | ||
02b2739e GK |
107 | /* Clear status */ |
108 | mvebu_mmc_write(SDIO_NOR_INTR_STATUS, SDIO_POLL_MASK); | |
109 | mvebu_mmc_write(SDIO_ERR_INTR_STATUS, SDIO_POLL_MASK); | |
3fe3b4fb D |
110 | |
111 | resptype = SDIO_CMD_INDEX(cmd->cmdidx); | |
112 | ||
113 | /* Analyzing resptype/xfertype/waittype for the command */ | |
114 | if (cmd->resp_type & MMC_RSP_BUSY) | |
115 | resptype |= SDIO_CMD_RSP_48BUSY; | |
116 | else if (cmd->resp_type & MMC_RSP_136) | |
117 | resptype |= SDIO_CMD_RSP_136; | |
118 | else if (cmd->resp_type & MMC_RSP_PRESENT) | |
119 | resptype |= SDIO_CMD_RSP_48; | |
120 | else | |
121 | resptype |= SDIO_CMD_RSP_NONE; | |
122 | ||
123 | if (cmd->resp_type & MMC_RSP_CRC) | |
124 | resptype |= SDIO_CMD_CHECK_CMDCRC; | |
125 | ||
126 | if (cmd->resp_type & MMC_RSP_OPCODE) | |
127 | resptype |= SDIO_CMD_INDX_CHECK; | |
128 | ||
129 | if (cmd->resp_type & MMC_RSP_PRESENT) { | |
130 | resptype |= SDIO_UNEXPECTED_RESP; | |
131 | waittype |= SDIO_NOR_UNEXP_RSP; | |
132 | } | |
133 | ||
134 | if (data) { | |
02b2739e GK |
135 | int err = mvebu_mmc_setup_data(data); |
136 | ||
137 | if (err) { | |
138 | debug("%s: command DATA error :%x\n", | |
139 | DRIVER_NAME, err); | |
140 | return err; | |
141 | } | |
142 | ||
3fe3b4fb D |
143 | resptype |= SDIO_CMD_DATA_PRESENT | SDIO_CMD_CHECK_DATACRC16; |
144 | xfertype |= SDIO_XFER_MODE_HW_WR_DATA_EN; | |
145 | if (data->flags & MMC_DATA_READ) { | |
146 | xfertype |= SDIO_XFER_MODE_TO_HOST; | |
147 | waittype = SDIO_NOR_DMA_INI; | |
148 | } else { | |
149 | waittype |= SDIO_NOR_XFER_DONE; | |
150 | } | |
151 | } else { | |
152 | waittype |= SDIO_NOR_CMD_DONE; | |
153 | } | |
154 | ||
155 | /* Setting cmd arguments */ | |
156 | mvebu_mmc_write(SDIO_ARG_LOW, cmd->cmdarg & 0xffff); | |
157 | mvebu_mmc_write(SDIO_ARG_HI, cmd->cmdarg >> 16); | |
158 | ||
159 | /* Setting Xfer mode */ | |
160 | mvebu_mmc_write(SDIO_XFER_MODE, xfertype); | |
161 | ||
3fe3b4fb D |
162 | /* Sending command */ |
163 | mvebu_mmc_write(SDIO_CMD, resptype); | |
164 | ||
28d27b79 | 165 | start = get_timer(0); |
3fe3b4fb D |
166 | |
167 | while (!((mvebu_mmc_read(SDIO_NOR_INTR_STATUS)) & waittype)) { | |
168 | if (mvebu_mmc_read(SDIO_NOR_INTR_STATUS) & SDIO_NOR_ERROR) { | |
169 | debug("%s: error! cmdidx : %d, err reg: %04x\n", | |
170 | DRIVER_NAME, cmd->cmdidx, | |
171 | mvebu_mmc_read(SDIO_ERR_INTR_STATUS)); | |
172 | if (mvebu_mmc_read(SDIO_ERR_INTR_STATUS) & | |
fc0f25f9 GK |
173 | (SDIO_ERR_CMD_TIMEOUT | SDIO_ERR_DATA_TIMEOUT)) { |
174 | debug("%s: command READ timed out\n", | |
175 | DRIVER_NAME); | |
915ffa52 | 176 | return -ETIMEDOUT; |
fc0f25f9 GK |
177 | } |
178 | debug("%s: command READ error\n", DRIVER_NAME); | |
915ffa52 | 179 | return -ECOMM; |
3fe3b4fb D |
180 | } |
181 | ||
28d27b79 GK |
182 | if ((get_timer(0) - start) > TIMEOUT_DELAY) { |
183 | debug("%s: command timed out\n", DRIVER_NAME); | |
915ffa52 | 184 | return -ETIMEDOUT; |
3fe3b4fb D |
185 | } |
186 | } | |
28d27b79 | 187 | |
3fe3b4fb D |
188 | /* Handling response */ |
189 | if (cmd->resp_type & MMC_RSP_136) { | |
190 | uint response[8]; | |
191 | ||
192 | for (resp_indx = 0; resp_indx < 8; resp_indx++) | |
193 | response[resp_indx] | |
194 | = mvebu_mmc_read(SDIO_RSP(resp_indx)); | |
195 | ||
196 | cmd->response[0] = ((response[0] & 0x03ff) << 22) | | |
197 | ((response[1] & 0xffff) << 6) | | |
198 | ((response[2] & 0xfc00) >> 10); | |
199 | cmd->response[1] = ((response[2] & 0x03ff) << 22) | | |
200 | ((response[3] & 0xffff) << 6) | | |
201 | ((response[4] & 0xfc00) >> 10); | |
202 | cmd->response[2] = ((response[4] & 0x03ff) << 22) | | |
203 | ((response[5] & 0xffff) << 6) | | |
204 | ((response[6] & 0xfc00) >> 10); | |
205 | cmd->response[3] = ((response[6] & 0x03ff) << 22) | | |
206 | ((response[7] & 0x3fff) << 8); | |
207 | } else if (cmd->resp_type & MMC_RSP_PRESENT) { | |
208 | uint response[3]; | |
209 | ||
210 | for (resp_indx = 0; resp_indx < 3; resp_indx++) | |
211 | response[resp_indx] | |
212 | = mvebu_mmc_read(SDIO_RSP(resp_indx)); | |
213 | ||
214 | cmd->response[0] = ((response[2] & 0x003f) << (8 - 8)) | | |
215 | ((response[1] & 0xffff) << (14 - 8)) | | |
216 | ((response[0] & 0x03ff) << (30 - 8)); | |
217 | cmd->response[1] = ((response[0] & 0xfc00) >> 10); | |
218 | cmd->response[2] = 0; | |
219 | cmd->response[3] = 0; | |
02b2739e GK |
220 | } else { |
221 | cmd->response[0] = 0; | |
222 | cmd->response[1] = 0; | |
223 | cmd->response[2] = 0; | |
224 | cmd->response[3] = 0; | |
3fe3b4fb D |
225 | } |
226 | ||
227 | debug("%s: resp[0x%x] ", DRIVER_NAME, cmd->resp_type); | |
228 | debug("[0x%x] ", cmd->response[0]); | |
229 | debug("[0x%x] ", cmd->response[1]); | |
230 | debug("[0x%x] ", cmd->response[2]); | |
231 | debug("[0x%x] ", cmd->response[3]); | |
232 | debug("\n"); | |
233 | ||
02b2739e GK |
234 | if (mvebu_mmc_read(SDIO_ERR_INTR_STATUS) & |
235 | (SDIO_ERR_CMD_TIMEOUT | SDIO_ERR_DATA_TIMEOUT)) | |
915ffa52 | 236 | return -ETIMEDOUT; |
02b2739e | 237 | |
3fe3b4fb D |
238 | return 0; |
239 | } | |
240 | ||
241 | static void mvebu_mmc_power_up(void) | |
242 | { | |
243 | debug("%s: power up\n", DRIVER_NAME); | |
244 | ||
245 | /* disable interrupts */ | |
246 | mvebu_mmc_write(SDIO_NOR_INTR_EN, 0); | |
247 | mvebu_mmc_write(SDIO_ERR_INTR_EN, 0); | |
248 | ||
249 | /* SW reset */ | |
250 | mvebu_mmc_write(SDIO_SW_RESET, SDIO_SW_RESET_NOW); | |
251 | ||
252 | mvebu_mmc_write(SDIO_XFER_MODE, 0); | |
253 | ||
254 | /* enable status */ | |
255 | mvebu_mmc_write(SDIO_NOR_STATUS_EN, SDIO_POLL_MASK); | |
256 | mvebu_mmc_write(SDIO_ERR_STATUS_EN, SDIO_POLL_MASK); | |
257 | ||
258 | /* enable interrupts status */ | |
259 | mvebu_mmc_write(SDIO_NOR_INTR_STATUS, SDIO_POLL_MASK); | |
260 | mvebu_mmc_write(SDIO_ERR_INTR_STATUS, SDIO_POLL_MASK); | |
261 | } | |
262 | ||
263 | static void mvebu_mmc_set_clk(unsigned int clock) | |
264 | { | |
265 | unsigned int m; | |
266 | ||
267 | if (clock == 0) { | |
268 | debug("%s: clock off\n", DRIVER_NAME); | |
269 | mvebu_mmc_write(SDIO_XFER_MODE, SDIO_XFER_MODE_STOP_CLK); | |
270 | mvebu_mmc_write(SDIO_CLK_DIV, MVEBU_MMC_BASE_DIV_MAX); | |
271 | } else { | |
272 | m = MVEBU_MMC_BASE_FAST_CLOCK/(2*clock) - 1; | |
273 | if (m > MVEBU_MMC_BASE_DIV_MAX) | |
274 | m = MVEBU_MMC_BASE_DIV_MAX; | |
275 | mvebu_mmc_write(SDIO_CLK_DIV, m & MVEBU_MMC_BASE_DIV_MAX); | |
fc0f25f9 | 276 | debug("%s: clock (%d) div : %d\n", DRIVER_NAME, clock, m); |
3fe3b4fb | 277 | } |
3fe3b4fb D |
278 | } |
279 | ||
280 | static void mvebu_mmc_set_bus(unsigned int bus) | |
281 | { | |
282 | u32 ctrl_reg = 0; | |
283 | ||
284 | ctrl_reg = mvebu_mmc_read(SDIO_HOST_CTRL); | |
285 | ctrl_reg &= ~SDIO_HOST_CTRL_DATA_WIDTH_4_BITS; | |
286 | ||
287 | switch (bus) { | |
288 | case 4: | |
289 | ctrl_reg |= SDIO_HOST_CTRL_DATA_WIDTH_4_BITS; | |
290 | break; | |
291 | case 1: | |
292 | default: | |
293 | ctrl_reg |= SDIO_HOST_CTRL_DATA_WIDTH_1_BIT; | |
294 | } | |
295 | ||
296 | /* default transfer mode */ | |
297 | ctrl_reg |= SDIO_HOST_CTRL_BIG_ENDIAN; | |
298 | ctrl_reg &= ~SDIO_HOST_CTRL_LSB_FIRST; | |
299 | ||
300 | /* default to maximum timeout */ | |
301 | ctrl_reg |= SDIO_HOST_CTRL_TMOUT(SDIO_HOST_CTRL_TMOUT_MAX); | |
bcd06989 | 302 | ctrl_reg |= SDIO_HOST_CTRL_TMOUT_EN; |
3fe3b4fb D |
303 | |
304 | ctrl_reg |= SDIO_HOST_CTRL_PUSH_PULL_EN; | |
305 | ||
306 | ctrl_reg |= SDIO_HOST_CTRL_CARD_TYPE_MEM_ONLY; | |
307 | ||
308 | debug("%s: ctrl 0x%04x: %s %s %s\n", DRIVER_NAME, ctrl_reg, | |
309 | (ctrl_reg & SDIO_HOST_CTRL_PUSH_PULL_EN) ? | |
310 | "push-pull" : "open-drain", | |
311 | (ctrl_reg & SDIO_HOST_CTRL_DATA_WIDTH_4_BITS) ? | |
312 | "4bit-width" : "1bit-width", | |
313 | (ctrl_reg & SDIO_HOST_CTRL_HI_SPEED_EN) ? | |
314 | "high-speed" : ""); | |
315 | ||
316 | mvebu_mmc_write(SDIO_HOST_CTRL, ctrl_reg); | |
3fe3b4fb D |
317 | } |
318 | ||
07b0b9c0 | 319 | static int mvebu_mmc_set_ios(struct mmc *mmc) |
3fe3b4fb D |
320 | { |
321 | debug("%s: bus[%d] clock[%d]\n", DRIVER_NAME, | |
322 | mmc->bus_width, mmc->clock); | |
323 | mvebu_mmc_set_bus(mmc->bus_width); | |
324 | mvebu_mmc_set_clk(mmc->clock); | |
07b0b9c0 JC |
325 | |
326 | return 0; | |
3fe3b4fb D |
327 | } |
328 | ||
bcd06989 MS |
329 | /* |
330 | * Set window register. | |
331 | */ | |
332 | static void mvebu_window_setup(void) | |
333 | { | |
334 | int i; | |
335 | ||
336 | for (i = 0; i < 4; i++) { | |
337 | mvebu_mmc_write(WINDOW_CTRL(i), 0); | |
338 | mvebu_mmc_write(WINDOW_BASE(i), 0); | |
339 | } | |
340 | for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { | |
341 | u32 size, base, attrib; | |
342 | ||
343 | /* Enable DRAM bank */ | |
344 | switch (i) { | |
345 | case 0: | |
346 | attrib = KWCPU_ATTR_DRAM_CS0; | |
347 | break; | |
348 | case 1: | |
349 | attrib = KWCPU_ATTR_DRAM_CS1; | |
350 | break; | |
351 | case 2: | |
352 | attrib = KWCPU_ATTR_DRAM_CS2; | |
353 | break; | |
354 | case 3: | |
355 | attrib = KWCPU_ATTR_DRAM_CS3; | |
356 | break; | |
357 | default: | |
358 | /* invalide bank, disable access */ | |
359 | attrib = 0; | |
360 | break; | |
361 | } | |
362 | ||
363 | size = gd->bd->bi_dram[i].size; | |
364 | base = gd->bd->bi_dram[i].start; | |
365 | if (size && attrib) { | |
366 | mvebu_mmc_write(WINDOW_CTRL(i), | |
367 | MVCPU_WIN_CTRL_DATA(size, | |
368 | MVEBU_TARGET_DRAM, | |
369 | attrib, | |
370 | MVCPU_WIN_ENABLE)); | |
371 | } else { | |
372 | mvebu_mmc_write(WINDOW_CTRL(i), MVCPU_WIN_DISABLE); | |
373 | } | |
374 | mvebu_mmc_write(WINDOW_BASE(i), base); | |
375 | } | |
376 | } | |
377 | ||
3fe3b4fb D |
378 | static int mvebu_mmc_initialize(struct mmc *mmc) |
379 | { | |
fc0f25f9 | 380 | debug("%s: mvebu_mmc_initialize\n", DRIVER_NAME); |
3fe3b4fb D |
381 | |
382 | /* | |
383 | * Setting host parameters | |
384 | * Initial Host Ctrl : Timeout : max , Normal Speed mode, | |
385 | * 4-bit data mode, Big Endian, SD memory Card, Push_pull CMD Line | |
386 | */ | |
387 | mvebu_mmc_write(SDIO_HOST_CTRL, | |
388 | SDIO_HOST_CTRL_TMOUT(SDIO_HOST_CTRL_TMOUT_MAX) | | |
389 | SDIO_HOST_CTRL_DATA_WIDTH_4_BITS | | |
390 | SDIO_HOST_CTRL_BIG_ENDIAN | | |
391 | SDIO_HOST_CTRL_PUSH_PULL_EN | | |
392 | SDIO_HOST_CTRL_CARD_TYPE_MEM_ONLY); | |
393 | ||
394 | mvebu_mmc_write(SDIO_CLK_CTRL, 0); | |
395 | ||
396 | /* enable status */ | |
397 | mvebu_mmc_write(SDIO_NOR_STATUS_EN, SDIO_POLL_MASK); | |
398 | mvebu_mmc_write(SDIO_ERR_STATUS_EN, SDIO_POLL_MASK); | |
399 | ||
400 | /* disable interrupts */ | |
401 | mvebu_mmc_write(SDIO_NOR_INTR_EN, 0); | |
402 | mvebu_mmc_write(SDIO_ERR_INTR_EN, 0); | |
403 | ||
bcd06989 MS |
404 | mvebu_window_setup(); |
405 | ||
3fe3b4fb D |
406 | /* SW reset */ |
407 | mvebu_mmc_write(SDIO_SW_RESET, SDIO_SW_RESET_NOW); | |
408 | ||
3fe3b4fb D |
409 | return 0; |
410 | } | |
411 | ||
412 | static const struct mmc_ops mvebu_mmc_ops = { | |
413 | .send_cmd = mvebu_mmc_send_cmd, | |
414 | .set_ios = mvebu_mmc_set_ios, | |
415 | .init = mvebu_mmc_initialize, | |
416 | }; | |
417 | ||
418 | static struct mmc_config mvebu_mmc_cfg = { | |
419 | .name = DRIVER_NAME, | |
420 | .ops = &mvebu_mmc_ops, | |
421 | .f_min = MVEBU_MMC_BASE_FAST_CLOCK / MVEBU_MMC_BASE_DIV_MAX, | |
422 | .f_max = MVEBU_MMC_CLOCKRATE_MAX, | |
423 | .voltages = MMC_VDD_32_33 | MMC_VDD_33_34, | |
5a20397b | 424 | .host_caps = MMC_MODE_4BIT | MMC_MODE_HS | |
bcd06989 | 425 | MMC_MODE_HS_52MHz, |
3fe3b4fb D |
426 | .part_type = PART_TYPE_DOS, |
427 | .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT, | |
428 | }; | |
429 | ||
430 | int mvebu_mmc_init(bd_t *bis) | |
431 | { | |
432 | struct mmc *mmc; | |
433 | ||
434 | mvebu_mmc_power_up(); | |
435 | ||
436 | mmc = mmc_create(&mvebu_mmc_cfg, bis); | |
437 | if (mmc == NULL) | |
438 | return -1; | |
439 | ||
440 | return 0; | |
441 | } |