]>
Commit | Line | Data |
---|---|---|
23b93e1d MW |
1 | /* |
2 | * ARM PrimeCell MultiMedia Card Interface - PL180 | |
3 | * | |
4 | * Copyright (C) ST-Ericsson SA 2010 | |
5 | * | |
6 | * Author: Ulf Hansson <ulf.hansson@stericsson.com> | |
7 | * Author: Martin Lundholm <martin.xa.lundholm@stericsson.com> | |
8 | * Ported to drivers/mmc/ by: Matt Waddel <matt.waddel@linaro.org> | |
9 | * | |
1a459660 | 10 | * SPDX-License-Identifier: GPL-2.0+ |
23b93e1d MW |
11 | */ |
12 | ||
13 | /* #define DEBUG */ | |
14 | ||
23b93e1d | 15 | #include "common.h" |
5f256fe7 | 16 | #include <clk.h> |
23b93e1d | 17 | #include <errno.h> |
3c0dbed2 | 18 | #include <malloc.h> |
23b93e1d | 19 | #include <mmc.h> |
3c0dbed2 | 20 | |
3c0dbed2 | 21 | #include <asm/io.h> |
5829fe2d PC |
22 | #include <asm-generic/gpio.h> |
23 | ||
24 | #include "arm_pl180_mmci.h" | |
3c0dbed2 PC |
25 | |
26 | #ifdef CONFIG_DM_MMC | |
27 | #include <dm.h> | |
28 | DECLARE_GLOBAL_DATA_PTR; | |
29 | ||
30 | #define MMC_CLOCK_MAX 48000000 | |
31 | #define MMC_CLOCK_MIN 400000 | |
32 | ||
33 | struct arm_pl180_mmc_plat { | |
34 | struct mmc_config cfg; | |
35 | struct mmc mmc; | |
36 | }; | |
37 | #endif | |
23b93e1d | 38 | |
23b93e1d MW |
39 | static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd) |
40 | { | |
41 | u32 hoststatus, statusmask; | |
10ed93dc | 42 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
43 | |
44 | statusmask = SDI_STA_CTIMEOUT | SDI_STA_CCRCFAIL; | |
45 | if ((cmd->resp_type & MMC_RSP_PRESENT)) | |
46 | statusmask |= SDI_STA_CMDREND; | |
47 | else | |
48 | statusmask |= SDI_STA_CMDSENT; | |
49 | ||
50 | do | |
51 | hoststatus = readl(&host->base->status) & statusmask; | |
52 | while (!hoststatus); | |
53 | ||
54 | writel(statusmask, &host->base->status_clear); | |
55 | if (hoststatus & SDI_STA_CTIMEOUT) { | |
10ed93dc | 56 | debug("CMD%d time out\n", cmd->cmdidx); |
915ffa52 | 57 | return -ETIMEDOUT; |
23b93e1d | 58 | } else if ((hoststatus & SDI_STA_CCRCFAIL) && |
95b01c47 | 59 | (cmd->resp_type & MMC_RSP_CRC)) { |
23b93e1d MW |
60 | printf("CMD%d CRC error\n", cmd->cmdidx); |
61 | return -EILSEQ; | |
62 | } | |
63 | ||
64 | if (cmd->resp_type & MMC_RSP_PRESENT) { | |
65 | cmd->response[0] = readl(&host->base->response0); | |
66 | cmd->response[1] = readl(&host->base->response1); | |
67 | cmd->response[2] = readl(&host->base->response2); | |
68 | cmd->response[3] = readl(&host->base->response3); | |
69 | debug("CMD%d response[0]:0x%08X, response[1]:0x%08X, " | |
70 | "response[2]:0x%08X, response[3]:0x%08X\n", | |
71 | cmd->cmdidx, cmd->response[0], cmd->response[1], | |
72 | cmd->response[2], cmd->response[3]); | |
73 | } | |
74 | ||
75 | return 0; | |
76 | } | |
77 | ||
78 | /* send command to the mmc card and wait for results */ | |
79 | static int do_command(struct mmc *dev, struct mmc_cmd *cmd) | |
80 | { | |
81 | int result; | |
82 | u32 sdi_cmd = 0; | |
10ed93dc | 83 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
84 | |
85 | sdi_cmd = ((cmd->cmdidx & SDI_CMD_CMDINDEX_MASK) | SDI_CMD_CPSMEN); | |
86 | ||
87 | if (cmd->resp_type) { | |
88 | sdi_cmd |= SDI_CMD_WAITRESP; | |
89 | if (cmd->resp_type & MMC_RSP_136) | |
90 | sdi_cmd |= SDI_CMD_LONGRESP; | |
91 | } | |
92 | ||
93 | writel((u32)cmd->cmdarg, &host->base->argument); | |
94 | udelay(COMMAND_REG_DELAY); | |
95 | writel(sdi_cmd, &host->base->command); | |
96 | result = wait_for_command_end(dev, cmd); | |
97 | ||
98 | /* After CMD2 set RCA to a none zero value. */ | |
99 | if ((result == 0) && (cmd->cmdidx == MMC_CMD_ALL_SEND_CID)) | |
100 | dev->rca = 10; | |
101 | ||
102 | /* After CMD3 open drain is switched off and push pull is used. */ | |
103 | if ((result == 0) && (cmd->cmdidx == MMC_CMD_SET_RELATIVE_ADDR)) { | |
104 | u32 sdi_pwr = readl(&host->base->power) & ~SDI_PWR_OPD; | |
105 | writel(sdi_pwr, &host->base->power); | |
106 | } | |
107 | ||
108 | return result; | |
109 | } | |
110 | ||
111 | static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize) | |
112 | { | |
113 | u32 *tempbuff = dest; | |
23b93e1d | 114 | u64 xfercount = blkcount * blksize; |
10ed93dc | 115 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
116 | u32 status, status_err; |
117 | ||
118 | debug("read_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); | |
119 | ||
120 | status = readl(&host->base->status); | |
121 | status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | | |
122 | SDI_STA_RXOVERR); | |
23b93e1d MW |
123 | while ((!status_err) && (xfercount >= sizeof(u32))) { |
124 | if (status & SDI_STA_RXDAVL) { | |
125 | *(tempbuff) = readl(&host->base->fifo); | |
126 | tempbuff++; | |
127 | xfercount -= sizeof(u32); | |
128 | } | |
129 | status = readl(&host->base->status); | |
130 | status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | | |
131 | SDI_STA_RXOVERR); | |
132 | } | |
133 | ||
134 | status_err = status & | |
135 | (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND | | |
136 | SDI_STA_RXOVERR); | |
137 | while (!status_err) { | |
138 | status = readl(&host->base->status); | |
139 | status_err = status & | |
140 | (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND | | |
141 | SDI_STA_RXOVERR); | |
142 | } | |
143 | ||
144 | if (status & SDI_STA_DTIMEOUT) { | |
145 | printf("Read data timed out, xfercount: %llu, status: 0x%08X\n", | |
146 | xfercount, status); | |
147 | return -ETIMEDOUT; | |
148 | } else if (status & SDI_STA_DCRCFAIL) { | |
149 | printf("Read data bytes CRC error: 0x%x\n", status); | |
150 | return -EILSEQ; | |
151 | } else if (status & SDI_STA_RXOVERR) { | |
152 | printf("Read data RX overflow error\n"); | |
153 | return -EIO; | |
154 | } | |
155 | ||
156 | writel(SDI_ICR_MASK, &host->base->status_clear); | |
157 | ||
158 | if (xfercount) { | |
159 | printf("Read data error, xfercount: %llu\n", xfercount); | |
160 | return -ENOBUFS; | |
161 | } | |
162 | ||
163 | return 0; | |
164 | } | |
165 | ||
166 | static int write_bytes(struct mmc *dev, u32 *src, u32 blkcount, u32 blksize) | |
167 | { | |
168 | u32 *tempbuff = src; | |
169 | int i; | |
170 | u64 xfercount = blkcount * blksize; | |
10ed93dc | 171 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
172 | u32 status, status_err; |
173 | ||
174 | debug("write_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); | |
175 | ||
176 | status = readl(&host->base->status); | |
177 | status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT); | |
178 | while (!status_err && xfercount) { | |
179 | if (status & SDI_STA_TXFIFOBW) { | |
180 | if (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32)) { | |
181 | for (i = 0; i < SDI_FIFO_BURST_SIZE; i++) | |
182 | writel(*(tempbuff + i), | |
183 | &host->base->fifo); | |
184 | tempbuff += SDI_FIFO_BURST_SIZE; | |
185 | xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32); | |
186 | } else { | |
187 | while (xfercount >= sizeof(u32)) { | |
188 | writel(*(tempbuff), &host->base->fifo); | |
189 | tempbuff++; | |
190 | xfercount -= sizeof(u32); | |
191 | } | |
192 | } | |
193 | } | |
194 | status = readl(&host->base->status); | |
195 | status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT); | |
196 | } | |
197 | ||
198 | status_err = status & | |
199 | (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND); | |
200 | while (!status_err) { | |
201 | status = readl(&host->base->status); | |
202 | status_err = status & | |
203 | (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND); | |
204 | } | |
205 | ||
206 | if (status & SDI_STA_DTIMEOUT) { | |
207 | printf("Write data timed out, xfercount:%llu,status:0x%08X\n", | |
208 | xfercount, status); | |
209 | return -ETIMEDOUT; | |
210 | } else if (status & SDI_STA_DCRCFAIL) { | |
211 | printf("Write data CRC error\n"); | |
212 | return -EILSEQ; | |
213 | } | |
214 | ||
215 | writel(SDI_ICR_MASK, &host->base->status_clear); | |
216 | ||
217 | if (xfercount) { | |
218 | printf("Write data error, xfercount:%llu", xfercount); | |
219 | return -ENOBUFS; | |
220 | } | |
221 | ||
222 | return 0; | |
223 | } | |
224 | ||
225 | static int do_data_transfer(struct mmc *dev, | |
226 | struct mmc_cmd *cmd, | |
227 | struct mmc_data *data) | |
228 | { | |
229 | int error = -ETIMEDOUT; | |
10ed93dc | 230 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
231 | u32 blksz = 0; |
232 | u32 data_ctrl = 0; | |
233 | u32 data_len = (u32) (data->blocks * data->blocksize); | |
234 | ||
10ed93dc JR |
235 | if (!host->version2) { |
236 | blksz = (ffs(data->blocksize) - 1); | |
237 | data_ctrl |= ((blksz << 4) & SDI_DCTRL_DBLKSIZE_MASK); | |
238 | } else { | |
239 | blksz = data->blocksize; | |
240 | data_ctrl |= (blksz << SDI_DCTRL_DBLOCKSIZE_V2_SHIFT); | |
241 | } | |
242 | data_ctrl |= SDI_DCTRL_DTEN | SDI_DCTRL_BUSYMODE; | |
23b93e1d MW |
243 | |
244 | writel(SDI_DTIMER_DEFAULT, &host->base->datatimer); | |
245 | writel(data_len, &host->base->datalength); | |
246 | udelay(DATA_REG_DELAY); | |
247 | ||
248 | if (data->flags & MMC_DATA_READ) { | |
249 | data_ctrl |= SDI_DCTRL_DTDIR_IN; | |
250 | writel(data_ctrl, &host->base->datactrl); | |
251 | ||
252 | error = do_command(dev, cmd); | |
253 | if (error) | |
254 | return error; | |
255 | ||
256 | error = read_bytes(dev, (u32 *)data->dest, (u32)data->blocks, | |
257 | (u32)data->blocksize); | |
258 | } else if (data->flags & MMC_DATA_WRITE) { | |
259 | error = do_command(dev, cmd); | |
260 | if (error) | |
261 | return error; | |
262 | ||
263 | writel(data_ctrl, &host->base->datactrl); | |
264 | error = write_bytes(dev, (u32 *)data->src, (u32)data->blocks, | |
10ed93dc | 265 | (u32)data->blocksize); |
23b93e1d MW |
266 | } |
267 | ||
268 | return error; | |
269 | } | |
270 | ||
271 | static int host_request(struct mmc *dev, | |
272 | struct mmc_cmd *cmd, | |
273 | struct mmc_data *data) | |
274 | { | |
275 | int result; | |
276 | ||
277 | if (data) | |
278 | result = do_data_transfer(dev, cmd, data); | |
279 | else | |
280 | result = do_command(dev, cmd); | |
281 | ||
282 | return result; | |
283 | } | |
284 | ||
07b0b9c0 | 285 | static int host_set_ios(struct mmc *dev) |
23b93e1d | 286 | { |
10ed93dc | 287 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
288 | u32 sdi_clkcr; |
289 | ||
290 | sdi_clkcr = readl(&host->base->clock); | |
291 | ||
292 | /* Ramp up the clock rate */ | |
293 | if (dev->clock) { | |
294 | u32 clkdiv = 0; | |
10ed93dc | 295 | u32 tmp_clock; |
23b93e1d | 296 | |
93bfd616 | 297 | if (dev->clock >= dev->cfg->f_max) { |
10ed93dc | 298 | clkdiv = 0; |
93bfd616 | 299 | dev->clock = dev->cfg->f_max; |
10ed93dc JR |
300 | } else { |
301 | clkdiv = (host->clock_in / dev->clock) - 2; | |
302 | } | |
23b93e1d | 303 | |
10ed93dc JR |
304 | tmp_clock = host->clock_in / (clkdiv + 2); |
305 | while (tmp_clock > dev->clock) { | |
306 | clkdiv++; | |
307 | tmp_clock = host->clock_in / (clkdiv + 2); | |
308 | } | |
23b93e1d MW |
309 | |
310 | if (clkdiv > SDI_CLKCR_CLKDIV_MASK) | |
311 | clkdiv = SDI_CLKCR_CLKDIV_MASK; | |
312 | ||
10ed93dc JR |
313 | tmp_clock = host->clock_in / (clkdiv + 2); |
314 | dev->clock = tmp_clock; | |
23b93e1d MW |
315 | sdi_clkcr &= ~(SDI_CLKCR_CLKDIV_MASK); |
316 | sdi_clkcr |= clkdiv; | |
317 | } | |
318 | ||
319 | /* Set the bus width */ | |
320 | if (dev->bus_width) { | |
321 | u32 buswidth = 0; | |
322 | ||
323 | switch (dev->bus_width) { | |
324 | case 1: | |
325 | buswidth |= SDI_CLKCR_WIDBUS_1; | |
326 | break; | |
327 | case 4: | |
328 | buswidth |= SDI_CLKCR_WIDBUS_4; | |
329 | break; | |
10ed93dc JR |
330 | case 8: |
331 | buswidth |= SDI_CLKCR_WIDBUS_8; | |
332 | break; | |
23b93e1d | 333 | default: |
10ed93dc | 334 | printf("Invalid bus width: %d\n", dev->bus_width); |
23b93e1d MW |
335 | break; |
336 | } | |
337 | sdi_clkcr &= ~(SDI_CLKCR_WIDBUS_MASK); | |
338 | sdi_clkcr |= buswidth; | |
339 | } | |
340 | ||
341 | writel(sdi_clkcr, &host->base->clock); | |
342 | udelay(CLK_CHANGE_DELAY); | |
07b0b9c0 JC |
343 | |
344 | return 0; | |
23b93e1d MW |
345 | } |
346 | ||
3c0dbed2 PC |
347 | #ifndef CONFIG_DM_MMC |
348 | /* MMC uses open drain drivers in the enumeration phase */ | |
349 | static int mmc_host_reset(struct mmc *dev) | |
350 | { | |
351 | struct pl180_mmc_host *host = dev->priv; | |
352 | ||
353 | writel(host->pwr_init, &host->base->power); | |
354 | ||
355 | return 0; | |
356 | } | |
357 | ||
ab769f22 PA |
358 | static const struct mmc_ops arm_pl180_mmci_ops = { |
359 | .send_cmd = host_request, | |
360 | .set_ios = host_set_ios, | |
361 | .init = mmc_host_reset, | |
362 | }; | |
3c0dbed2 | 363 | #endif |
ab769f22 | 364 | |
23b93e1d MW |
365 | /* |
366 | * mmc_host_init - initialize the mmc controller. | |
367 | * Set initial clock and power for mmc slot. | |
368 | * Initialize mmc struct and register with mmc framework. | |
369 | */ | |
cb0060e8 | 370 | int arm_pl180_mmci_init(struct pl180_mmc_host *host, struct mmc **mmc) |
23b93e1d | 371 | { |
23b93e1d MW |
372 | u32 sdi_u32; |
373 | ||
10ed93dc JR |
374 | writel(host->pwr_init, &host->base->power); |
375 | writel(host->clkdiv_init, &host->base->clock); | |
23b93e1d MW |
376 | udelay(CLK_CHANGE_DELAY); |
377 | ||
378 | /* Disable mmc interrupts */ | |
379 | sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK; | |
380 | writel(sdi_u32, &host->base->mask0); | |
93bfd616 PA |
381 | |
382 | host->cfg.name = host->name; | |
3c0dbed2 | 383 | #ifndef CONFIG_DM_MMC |
93bfd616 | 384 | host->cfg.ops = &arm_pl180_mmci_ops; |
3c0dbed2 | 385 | #endif |
93bfd616 PA |
386 | /* TODO remove the duplicates */ |
387 | host->cfg.host_caps = host->caps; | |
388 | host->cfg.voltages = host->voltages; | |
389 | host->cfg.f_min = host->clock_min; | |
390 | host->cfg.f_max = host->clock_max; | |
391 | if (host->b_max != 0) | |
392 | host->cfg.b_max = host->b_max; | |
393 | else | |
394 | host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; | |
395 | ||
cb0060e8 PC |
396 | *mmc = mmc_create(&host->cfg, host); |
397 | if (!*mmc) | |
93bfd616 PA |
398 | return -1; |
399 | ||
cb0060e8 PC |
400 | debug("registered mmc interface number is:%d\n", |
401 | (*mmc)->block_dev.devnum); | |
23b93e1d MW |
402 | |
403 | return 0; | |
404 | } | |
3c0dbed2 PC |
405 | |
406 | #ifdef CONFIG_DM_MMC | |
407 | static int arm_pl180_mmc_probe(struct udevice *dev) | |
408 | { | |
409 | struct arm_pl180_mmc_plat *pdata = dev_get_platdata(dev); | |
410 | struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); | |
411 | struct mmc *mmc = &pdata->mmc; | |
412 | struct pl180_mmc_host *host = mmc->priv; | |
5f256fe7 | 413 | struct clk clk; |
9035bb74 | 414 | u32 bus_width; |
3c0dbed2 PC |
415 | int ret; |
416 | ||
5f256fe7 PC |
417 | ret = clk_get_by_index(dev, 0, &clk); |
418 | if (ret < 0) | |
419 | return ret; | |
420 | ||
421 | ret = clk_enable(&clk); | |
422 | if (ret) { | |
423 | dev_err(dev, "failed to enable clock\n"); | |
424 | return ret; | |
425 | } | |
426 | ||
3c0dbed2 PC |
427 | strcpy(host->name, "MMC"); |
428 | host->pwr_init = INIT_PWR; | |
429 | host->clkdiv_init = SDI_CLKCR_CLKDIV_INIT_V1 | SDI_CLKCR_CLKEN | | |
430 | SDI_CLKCR_HWFC_EN; | |
431 | host->voltages = VOLTAGE_WINDOW_SD; | |
432 | host->caps = 0; | |
5f256fe7 PC |
433 | host->clock_in = clk_get_rate(&clk); |
434 | host->clock_min = host->clock_in / (2 * (SDI_CLKCR_CLKDIV_INIT_V1 + 1)); | |
3c0dbed2 PC |
435 | host->clock_max = dev_read_u32_default(dev, "max-frequency", |
436 | MMC_CLOCK_MAX); | |
437 | host->version2 = dev_get_driver_data(dev); | |
9035bb74 | 438 | |
5829fe2d PC |
439 | gpio_request_by_name(dev, "cd-gpios", 0, &host->cd_gpio, GPIOD_IS_IN); |
440 | ||
9035bb74 PC |
441 | bus_width = dev_read_u32_default(dev, "bus-width", 1); |
442 | switch (bus_width) { | |
443 | case 8: | |
444 | host->caps |= MMC_MODE_8BIT; | |
445 | /* Hosts capable of 8-bit transfers can also do 4 bits */ | |
446 | case 4: | |
447 | host->caps |= MMC_MODE_4BIT; | |
448 | break; | |
449 | case 1: | |
450 | break; | |
451 | default: | |
452 | dev_err(dev, "Invalid bus-width value %u\n", bus_width); | |
453 | } | |
454 | ||
3c0dbed2 PC |
455 | ret = arm_pl180_mmci_init(host, &mmc); |
456 | if (ret) { | |
457 | dev_err(dev, "arm_pl180_mmci init failed\n"); | |
458 | return ret; | |
459 | } | |
460 | ||
461 | mmc->dev = dev; | |
462 | dev->priv = host; | |
463 | upriv->mmc = mmc; | |
464 | ||
465 | return 0; | |
466 | } | |
467 | ||
468 | static int dm_host_request(struct udevice *dev, struct mmc_cmd *cmd, | |
469 | struct mmc_data *data) | |
470 | { | |
471 | struct mmc *mmc = mmc_get_mmc_dev(dev); | |
472 | ||
473 | return host_request(mmc, cmd, data); | |
474 | } | |
475 | ||
476 | static int dm_host_set_ios(struct udevice *dev) | |
477 | { | |
478 | struct mmc *mmc = mmc_get_mmc_dev(dev); | |
479 | ||
480 | return host_set_ios(mmc); | |
481 | } | |
482 | ||
5829fe2d PC |
483 | static int dm_mmc_getcd(struct udevice *dev) |
484 | { | |
485 | struct arm_pl180_mmc_plat *pdata = dev_get_platdata(dev); | |
486 | struct mmc *mmc = &pdata->mmc; | |
487 | struct pl180_mmc_host *host = mmc->priv; | |
488 | int value = 1; | |
489 | ||
490 | if (dm_gpio_is_valid(&host->cd_gpio)) { | |
491 | value = dm_gpio_get_value(&host->cd_gpio); | |
492 | if (host->cd_inverted) | |
493 | return !value; | |
494 | } | |
495 | ||
496 | return value; | |
497 | } | |
498 | ||
3c0dbed2 PC |
499 | static const struct dm_mmc_ops arm_pl180_dm_mmc_ops = { |
500 | .send_cmd = dm_host_request, | |
501 | .set_ios = dm_host_set_ios, | |
5829fe2d | 502 | .get_cd = dm_mmc_getcd, |
3c0dbed2 PC |
503 | }; |
504 | ||
505 | static int arm_pl180_mmc_ofdata_to_platdata(struct udevice *dev) | |
506 | { | |
507 | struct arm_pl180_mmc_plat *pdata = dev_get_platdata(dev); | |
508 | struct mmc *mmc = &pdata->mmc; | |
509 | struct pl180_mmc_host *host = mmc->priv; | |
510 | fdt_addr_t addr; | |
511 | ||
512 | addr = devfdt_get_addr(dev); | |
513 | if (addr == FDT_ADDR_T_NONE) | |
514 | return -EINVAL; | |
515 | ||
516 | host->base = (void *)addr; | |
517 | ||
518 | return 0; | |
519 | } | |
520 | ||
521 | static const struct udevice_id arm_pl180_mmc_match[] = { | |
522 | { .compatible = "st,stm32f4xx-sdio", .data = VERSION1 }, | |
523 | { /* sentinel */ } | |
524 | }; | |
525 | ||
526 | U_BOOT_DRIVER(arm_pl180_mmc) = { | |
527 | .name = "arm_pl180_mmc", | |
528 | .id = UCLASS_MMC, | |
529 | .of_match = arm_pl180_mmc_match, | |
530 | .ops = &arm_pl180_dm_mmc_ops, | |
531 | .probe = arm_pl180_mmc_probe, | |
532 | .ofdata_to_platdata = arm_pl180_mmc_ofdata_to_platdata, | |
533 | .priv_auto_alloc_size = sizeof(struct pl180_mmc_host), | |
534 | .platdata_auto_alloc_size = sizeof(struct arm_pl180_mmc_plat), | |
535 | }; | |
536 | #endif |