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