]>
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 | ||
23b93e1d | 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> |
3c0dbed2 | 21 | |
3c0dbed2 | 22 | #include <asm/io.h> |
5829fe2d PC |
23 | #include <asm-generic/gpio.h> |
24 | ||
25 | #include "arm_pl180_mmci.h" | |
c05ed00a | 26 | #include <linux/delay.h> |
3c0dbed2 PC |
27 | |
28 | #ifdef CONFIG_DM_MMC | |
29 | #include <dm.h> | |
3c0dbed2 PC |
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 | ||
c95b0297 UA |
285 | static int check_peripheral_id(struct pl180_mmc_host *host, u32 periph_id) |
286 | { | |
287 | return readl(&host->base->periph_id0) == (periph_id & 0xFF) && | |
288 | readl(&host->base->periph_id1) == ((periph_id >> 8) & 0xFF) && | |
289 | readl(&host->base->periph_id2) == ((periph_id >> 16) & 0xFF) && | |
290 | readl(&host->base->periph_id3) == ((periph_id >> 24) & 0xFF); | |
291 | } | |
292 | ||
07b0b9c0 | 293 | static int host_set_ios(struct mmc *dev) |
23b93e1d | 294 | { |
10ed93dc | 295 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
296 | u32 sdi_clkcr; |
297 | ||
298 | sdi_clkcr = readl(&host->base->clock); | |
299 | ||
300 | /* Ramp up the clock rate */ | |
301 | if (dev->clock) { | |
302 | u32 clkdiv = 0; | |
10ed93dc | 303 | u32 tmp_clock; |
23b93e1d | 304 | |
93bfd616 | 305 | if (dev->clock >= dev->cfg->f_max) { |
10ed93dc | 306 | clkdiv = 0; |
93bfd616 | 307 | dev->clock = dev->cfg->f_max; |
10ed93dc JR |
308 | } else { |
309 | clkdiv = (host->clock_in / dev->clock) - 2; | |
310 | } | |
23b93e1d | 311 | |
10ed93dc JR |
312 | tmp_clock = host->clock_in / (clkdiv + 2); |
313 | while (tmp_clock > dev->clock) { | |
314 | clkdiv++; | |
315 | tmp_clock = host->clock_in / (clkdiv + 2); | |
316 | } | |
23b93e1d MW |
317 | |
318 | if (clkdiv > SDI_CLKCR_CLKDIV_MASK) | |
319 | clkdiv = SDI_CLKCR_CLKDIV_MASK; | |
320 | ||
10ed93dc JR |
321 | tmp_clock = host->clock_in / (clkdiv + 2); |
322 | dev->clock = tmp_clock; | |
23b93e1d MW |
323 | sdi_clkcr &= ~(SDI_CLKCR_CLKDIV_MASK); |
324 | sdi_clkcr |= clkdiv; | |
325 | } | |
326 | ||
327 | /* Set the bus width */ | |
328 | if (dev->bus_width) { | |
329 | u32 buswidth = 0; | |
330 | ||
331 | switch (dev->bus_width) { | |
332 | case 1: | |
333 | buswidth |= SDI_CLKCR_WIDBUS_1; | |
334 | break; | |
335 | case 4: | |
336 | buswidth |= SDI_CLKCR_WIDBUS_4; | |
337 | break; | |
10ed93dc JR |
338 | case 8: |
339 | buswidth |= SDI_CLKCR_WIDBUS_8; | |
340 | break; | |
23b93e1d | 341 | default: |
10ed93dc | 342 | printf("Invalid bus width: %d\n", dev->bus_width); |
23b93e1d MW |
343 | break; |
344 | } | |
345 | sdi_clkcr &= ~(SDI_CLKCR_WIDBUS_MASK); | |
346 | sdi_clkcr |= buswidth; | |
347 | } | |
c95b0297 UA |
348 | /* For MMCs' with peripheral id 0x02041180 and 0x03041180, H/W flow control |
349 | * needs to be enabled for multi block writes (MMC CMD 18). | |
350 | */ | |
351 | if (check_peripheral_id(host, 0x02041180) || | |
352 | check_peripheral_id(host, 0x03041180)) | |
353 | sdi_clkcr |= SDI_CLKCR_HWFCEN; | |
23b93e1d MW |
354 | |
355 | writel(sdi_clkcr, &host->base->clock); | |
356 | udelay(CLK_CHANGE_DELAY); | |
07b0b9c0 JC |
357 | |
358 | return 0; | |
23b93e1d MW |
359 | } |
360 | ||
3c0dbed2 PC |
361 | #ifndef CONFIG_DM_MMC |
362 | /* MMC uses open drain drivers in the enumeration phase */ | |
363 | static int mmc_host_reset(struct mmc *dev) | |
364 | { | |
365 | struct pl180_mmc_host *host = dev->priv; | |
366 | ||
367 | writel(host->pwr_init, &host->base->power); | |
368 | ||
369 | return 0; | |
370 | } | |
371 | ||
ab769f22 PA |
372 | static const struct mmc_ops arm_pl180_mmci_ops = { |
373 | .send_cmd = host_request, | |
374 | .set_ios = host_set_ios, | |
375 | .init = mmc_host_reset, | |
376 | }; | |
377 | ||
23b93e1d MW |
378 | /* |
379 | * mmc_host_init - initialize the mmc controller. | |
380 | * Set initial clock and power for mmc slot. | |
381 | * Initialize mmc struct and register with mmc framework. | |
382 | */ | |
80150938 | 383 | |
cb0060e8 | 384 | int arm_pl180_mmci_init(struct pl180_mmc_host *host, struct mmc **mmc) |
23b93e1d | 385 | { |
23b93e1d MW |
386 | u32 sdi_u32; |
387 | ||
10ed93dc JR |
388 | writel(host->pwr_init, &host->base->power); |
389 | writel(host->clkdiv_init, &host->base->clock); | |
23b93e1d MW |
390 | udelay(CLK_CHANGE_DELAY); |
391 | ||
392 | /* Disable mmc interrupts */ | |
393 | sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK; | |
394 | writel(sdi_u32, &host->base->mask0); | |
93bfd616 PA |
395 | |
396 | host->cfg.name = host->name; | |
397 | host->cfg.ops = &arm_pl180_mmci_ops; | |
80150938 | 398 | |
93bfd616 PA |
399 | /* TODO remove the duplicates */ |
400 | host->cfg.host_caps = host->caps; | |
401 | host->cfg.voltages = host->voltages; | |
402 | host->cfg.f_min = host->clock_min; | |
403 | host->cfg.f_max = host->clock_max; | |
404 | if (host->b_max != 0) | |
405 | host->cfg.b_max = host->b_max; | |
406 | else | |
407 | host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; | |
408 | ||
cb0060e8 PC |
409 | *mmc = mmc_create(&host->cfg, host); |
410 | if (!*mmc) | |
93bfd616 | 411 | return -1; |
cb0060e8 PC |
412 | debug("registered mmc interface number is:%d\n", |
413 | (*mmc)->block_dev.devnum); | |
23b93e1d MW |
414 | |
415 | return 0; | |
416 | } | |
80150938 | 417 | #endif |
3c0dbed2 PC |
418 | |
419 | #ifdef CONFIG_DM_MMC | |
80150938 PC |
420 | static void arm_pl180_mmc_init(struct pl180_mmc_host *host) |
421 | { | |
422 | u32 sdi_u32; | |
423 | ||
424 | writel(host->pwr_init, &host->base->power); | |
425 | writel(host->clkdiv_init, &host->base->clock); | |
426 | udelay(CLK_CHANGE_DELAY); | |
427 | ||
428 | /* Disable mmc interrupts */ | |
429 | sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK; | |
430 | writel(sdi_u32, &host->base->mask0); | |
431 | } | |
432 | ||
3c0dbed2 PC |
433 | static int arm_pl180_mmc_probe(struct udevice *dev) |
434 | { | |
c69cda25 | 435 | struct arm_pl180_mmc_plat *pdata = dev_get_plat(dev); |
3c0dbed2 PC |
436 | struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); |
437 | struct mmc *mmc = &pdata->mmc; | |
0fd3d911 | 438 | struct pl180_mmc_host *host = dev_get_priv(dev); |
80150938 | 439 | struct mmc_config *cfg = &pdata->cfg; |
5f256fe7 | 440 | struct clk clk; |
6f41d1a1 | 441 | u32 periphid; |
3c0dbed2 PC |
442 | int ret; |
443 | ||
5f256fe7 PC |
444 | ret = clk_get_by_index(dev, 0, &clk); |
445 | if (ret < 0) | |
446 | return ret; | |
447 | ||
448 | ret = clk_enable(&clk); | |
449 | if (ret) { | |
43d36a0b | 450 | clk_free(&clk); |
5f256fe7 PC |
451 | dev_err(dev, "failed to enable clock\n"); |
452 | return ret; | |
453 | } | |
454 | ||
3c0dbed2 PC |
455 | host->pwr_init = INIT_PWR; |
456 | host->clkdiv_init = SDI_CLKCR_CLKDIV_INIT_V1 | SDI_CLKCR_CLKEN | | |
457 | SDI_CLKCR_HWFC_EN; | |
5f256fe7 | 458 | host->clock_in = clk_get_rate(&clk); |
6f41d1a1 | 459 | |
d890f234 SG |
460 | cfg->name = dev->name; |
461 | cfg->voltages = VOLTAGE_WINDOW_SD; | |
462 | cfg->host_caps = 0; | |
463 | cfg->f_min = host->clock_in / (2 * (SDI_CLKCR_CLKDIV_INIT_V1 + 1)); | |
464 | cfg->f_max = MMC_CLOCK_MAX; | |
465 | cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; | |
466 | ||
6f41d1a1 PC |
467 | periphid = dev_read_u32_default(dev, "arm,primecell-periphid", 0); |
468 | switch (periphid) { | |
469 | case STM32_MMCI_ID: /* stm32 variant */ | |
470 | host->version2 = false; | |
471 | break; | |
d890f234 SG |
472 | case UX500V2_MMCI_ID: |
473 | host->pwr_init = SDI_PWR_OPD | SDI_PWR_PWRCTRL_ON; | |
474 | host->clkdiv_init = SDI_CLKCR_CLKDIV_INIT_V2 | SDI_CLKCR_CLKEN | | |
475 | SDI_CLKCR_HWFC_EN; | |
476 | cfg->voltages = VOLTAGE_WINDOW_MMC; | |
477 | cfg->f_min = host->clock_in / (2 + SDI_CLKCR_CLKDIV_INIT_V2); | |
478 | host->version2 = true; | |
479 | break; | |
6f41d1a1 PC |
480 | default: |
481 | host->version2 = true; | |
482 | } | |
9035bb74 | 483 | |
5829fe2d PC |
484 | gpio_request_by_name(dev, "cd-gpios", 0, &host->cd_gpio, GPIOD_IS_IN); |
485 | ||
4daf2ec3 SG |
486 | ret = mmc_of_parse(dev, cfg); |
487 | if (ret) | |
488 | return ret; | |
9035bb74 | 489 | |
80150938 PC |
490 | arm_pl180_mmc_init(host); |
491 | mmc->priv = host; | |
3c0dbed2 | 492 | mmc->dev = dev; |
3c0dbed2 PC |
493 | upriv->mmc = mmc; |
494 | ||
495 | return 0; | |
496 | } | |
497 | ||
80150938 PC |
498 | int arm_pl180_mmc_bind(struct udevice *dev) |
499 | { | |
c69cda25 | 500 | struct arm_pl180_mmc_plat *plat = dev_get_plat(dev); |
80150938 PC |
501 | |
502 | return mmc_bind(dev, &plat->mmc, &plat->cfg); | |
503 | } | |
504 | ||
3c0dbed2 PC |
505 | static int dm_host_request(struct udevice *dev, struct mmc_cmd *cmd, |
506 | struct mmc_data *data) | |
507 | { | |
508 | struct mmc *mmc = mmc_get_mmc_dev(dev); | |
509 | ||
510 | return host_request(mmc, cmd, data); | |
511 | } | |
512 | ||
513 | static int dm_host_set_ios(struct udevice *dev) | |
514 | { | |
515 | struct mmc *mmc = mmc_get_mmc_dev(dev); | |
516 | ||
517 | return host_set_ios(mmc); | |
518 | } | |
519 | ||
5829fe2d PC |
520 | static int dm_mmc_getcd(struct udevice *dev) |
521 | { | |
0fd3d911 | 522 | struct pl180_mmc_host *host = dev_get_priv(dev); |
5829fe2d PC |
523 | int value = 1; |
524 | ||
fa911561 | 525 | if (dm_gpio_is_valid(&host->cd_gpio)) |
5829fe2d | 526 | value = dm_gpio_get_value(&host->cd_gpio); |
5829fe2d PC |
527 | |
528 | return value; | |
529 | } | |
530 | ||
3c0dbed2 PC |
531 | static const struct dm_mmc_ops arm_pl180_dm_mmc_ops = { |
532 | .send_cmd = dm_host_request, | |
533 | .set_ios = dm_host_set_ios, | |
5829fe2d | 534 | .get_cd = dm_mmc_getcd, |
3c0dbed2 PC |
535 | }; |
536 | ||
d1998a9f | 537 | static int arm_pl180_mmc_of_to_plat(struct udevice *dev) |
3c0dbed2 | 538 | { |
0fd3d911 | 539 | struct pl180_mmc_host *host = dev_get_priv(dev); |
3c0dbed2 | 540 | |
19e1da0c SG |
541 | host->base = dev_read_addr_ptr(dev); |
542 | if (!host->base) | |
3c0dbed2 PC |
543 | return -EINVAL; |
544 | ||
3c0dbed2 PC |
545 | return 0; |
546 | } | |
547 | ||
548 | static const struct udevice_id arm_pl180_mmc_match[] = { | |
6f41d1a1 | 549 | { .compatible = "arm,pl180" }, |
936e9cd3 | 550 | { .compatible = "arm,pl18x" }, |
3c0dbed2 PC |
551 | { /* sentinel */ } |
552 | }; | |
553 | ||
554 | U_BOOT_DRIVER(arm_pl180_mmc) = { | |
555 | .name = "arm_pl180_mmc", | |
556 | .id = UCLASS_MMC, | |
557 | .of_match = arm_pl180_mmc_match, | |
558 | .ops = &arm_pl180_dm_mmc_ops, | |
559 | .probe = arm_pl180_mmc_probe, | |
d1998a9f | 560 | .of_to_plat = arm_pl180_mmc_of_to_plat, |
80150938 | 561 | .bind = arm_pl180_mmc_bind, |
41575d8e | 562 | .priv_auto = sizeof(struct pl180_mmc_host), |
caa4daa2 | 563 | .plat_auto = sizeof(struct arm_pl180_mmc_plat), |
3c0dbed2 PC |
564 | }; |
565 | #endif |