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