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