]>
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 | ||
15 | #include <asm/io.h> | |
16 | #include "common.h" | |
17 | #include <errno.h> | |
18 | #include <mmc.h> | |
19 | #include "arm_pl180_mmci.h" | |
20 | #include <malloc.h> | |
21 | ||
23b93e1d MW |
22 | static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd) |
23 | { | |
24 | u32 hoststatus, statusmask; | |
10ed93dc | 25 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
26 | |
27 | statusmask = SDI_STA_CTIMEOUT | SDI_STA_CCRCFAIL; | |
28 | if ((cmd->resp_type & MMC_RSP_PRESENT)) | |
29 | statusmask |= SDI_STA_CMDREND; | |
30 | else | |
31 | statusmask |= SDI_STA_CMDSENT; | |
32 | ||
33 | do | |
34 | hoststatus = readl(&host->base->status) & statusmask; | |
35 | while (!hoststatus); | |
36 | ||
37 | writel(statusmask, &host->base->status_clear); | |
38 | if (hoststatus & SDI_STA_CTIMEOUT) { | |
10ed93dc | 39 | debug("CMD%d time out\n", cmd->cmdidx); |
915ffa52 | 40 | return -ETIMEDOUT; |
23b93e1d | 41 | } else if ((hoststatus & SDI_STA_CCRCFAIL) && |
95b01c47 | 42 | (cmd->resp_type & MMC_RSP_CRC)) { |
23b93e1d MW |
43 | printf("CMD%d CRC error\n", cmd->cmdidx); |
44 | return -EILSEQ; | |
45 | } | |
46 | ||
47 | if (cmd->resp_type & MMC_RSP_PRESENT) { | |
48 | cmd->response[0] = readl(&host->base->response0); | |
49 | cmd->response[1] = readl(&host->base->response1); | |
50 | cmd->response[2] = readl(&host->base->response2); | |
51 | cmd->response[3] = readl(&host->base->response3); | |
52 | debug("CMD%d response[0]:0x%08X, response[1]:0x%08X, " | |
53 | "response[2]:0x%08X, response[3]:0x%08X\n", | |
54 | cmd->cmdidx, cmd->response[0], cmd->response[1], | |
55 | cmd->response[2], cmd->response[3]); | |
56 | } | |
57 | ||
58 | return 0; | |
59 | } | |
60 | ||
61 | /* send command to the mmc card and wait for results */ | |
62 | static int do_command(struct mmc *dev, struct mmc_cmd *cmd) | |
63 | { | |
64 | int result; | |
65 | u32 sdi_cmd = 0; | |
10ed93dc | 66 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
67 | |
68 | sdi_cmd = ((cmd->cmdidx & SDI_CMD_CMDINDEX_MASK) | SDI_CMD_CPSMEN); | |
69 | ||
70 | if (cmd->resp_type) { | |
71 | sdi_cmd |= SDI_CMD_WAITRESP; | |
72 | if (cmd->resp_type & MMC_RSP_136) | |
73 | sdi_cmd |= SDI_CMD_LONGRESP; | |
74 | } | |
75 | ||
76 | writel((u32)cmd->cmdarg, &host->base->argument); | |
77 | udelay(COMMAND_REG_DELAY); | |
78 | writel(sdi_cmd, &host->base->command); | |
79 | result = wait_for_command_end(dev, cmd); | |
80 | ||
81 | /* After CMD2 set RCA to a none zero value. */ | |
82 | if ((result == 0) && (cmd->cmdidx == MMC_CMD_ALL_SEND_CID)) | |
83 | dev->rca = 10; | |
84 | ||
85 | /* After CMD3 open drain is switched off and push pull is used. */ | |
86 | if ((result == 0) && (cmd->cmdidx == MMC_CMD_SET_RELATIVE_ADDR)) { | |
87 | u32 sdi_pwr = readl(&host->base->power) & ~SDI_PWR_OPD; | |
88 | writel(sdi_pwr, &host->base->power); | |
89 | } | |
90 | ||
91 | return result; | |
92 | } | |
93 | ||
94 | static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize) | |
95 | { | |
96 | u32 *tempbuff = dest; | |
23b93e1d | 97 | u64 xfercount = blkcount * blksize; |
10ed93dc | 98 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
99 | u32 status, status_err; |
100 | ||
101 | debug("read_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); | |
102 | ||
103 | status = readl(&host->base->status); | |
104 | status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | | |
105 | SDI_STA_RXOVERR); | |
23b93e1d MW |
106 | while ((!status_err) && (xfercount >= sizeof(u32))) { |
107 | if (status & SDI_STA_RXDAVL) { | |
108 | *(tempbuff) = readl(&host->base->fifo); | |
109 | tempbuff++; | |
110 | xfercount -= sizeof(u32); | |
111 | } | |
112 | status = readl(&host->base->status); | |
113 | status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | | |
114 | SDI_STA_RXOVERR); | |
115 | } | |
116 | ||
117 | status_err = status & | |
118 | (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND | | |
119 | SDI_STA_RXOVERR); | |
120 | while (!status_err) { | |
121 | status = readl(&host->base->status); | |
122 | status_err = status & | |
123 | (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND | | |
124 | SDI_STA_RXOVERR); | |
125 | } | |
126 | ||
127 | if (status & SDI_STA_DTIMEOUT) { | |
128 | printf("Read data timed out, xfercount: %llu, status: 0x%08X\n", | |
129 | xfercount, status); | |
130 | return -ETIMEDOUT; | |
131 | } else if (status & SDI_STA_DCRCFAIL) { | |
132 | printf("Read data bytes CRC error: 0x%x\n", status); | |
133 | return -EILSEQ; | |
134 | } else if (status & SDI_STA_RXOVERR) { | |
135 | printf("Read data RX overflow error\n"); | |
136 | return -EIO; | |
137 | } | |
138 | ||
139 | writel(SDI_ICR_MASK, &host->base->status_clear); | |
140 | ||
141 | if (xfercount) { | |
142 | printf("Read data error, xfercount: %llu\n", xfercount); | |
143 | return -ENOBUFS; | |
144 | } | |
145 | ||
146 | return 0; | |
147 | } | |
148 | ||
149 | static int write_bytes(struct mmc *dev, u32 *src, u32 blkcount, u32 blksize) | |
150 | { | |
151 | u32 *tempbuff = src; | |
152 | int i; | |
153 | u64 xfercount = blkcount * blksize; | |
10ed93dc | 154 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
155 | u32 status, status_err; |
156 | ||
157 | debug("write_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); | |
158 | ||
159 | status = readl(&host->base->status); | |
160 | status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT); | |
161 | while (!status_err && xfercount) { | |
162 | if (status & SDI_STA_TXFIFOBW) { | |
163 | if (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32)) { | |
164 | for (i = 0; i < SDI_FIFO_BURST_SIZE; i++) | |
165 | writel(*(tempbuff + i), | |
166 | &host->base->fifo); | |
167 | tempbuff += SDI_FIFO_BURST_SIZE; | |
168 | xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32); | |
169 | } else { | |
170 | while (xfercount >= sizeof(u32)) { | |
171 | writel(*(tempbuff), &host->base->fifo); | |
172 | tempbuff++; | |
173 | xfercount -= sizeof(u32); | |
174 | } | |
175 | } | |
176 | } | |
177 | status = readl(&host->base->status); | |
178 | status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT); | |
179 | } | |
180 | ||
181 | status_err = status & | |
182 | (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND); | |
183 | while (!status_err) { | |
184 | status = readl(&host->base->status); | |
185 | status_err = status & | |
186 | (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND); | |
187 | } | |
188 | ||
189 | if (status & SDI_STA_DTIMEOUT) { | |
190 | printf("Write data timed out, xfercount:%llu,status:0x%08X\n", | |
191 | xfercount, status); | |
192 | return -ETIMEDOUT; | |
193 | } else if (status & SDI_STA_DCRCFAIL) { | |
194 | printf("Write data CRC error\n"); | |
195 | return -EILSEQ; | |
196 | } | |
197 | ||
198 | writel(SDI_ICR_MASK, &host->base->status_clear); | |
199 | ||
200 | if (xfercount) { | |
201 | printf("Write data error, xfercount:%llu", xfercount); | |
202 | return -ENOBUFS; | |
203 | } | |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
208 | static int do_data_transfer(struct mmc *dev, | |
209 | struct mmc_cmd *cmd, | |
210 | struct mmc_data *data) | |
211 | { | |
212 | int error = -ETIMEDOUT; | |
10ed93dc | 213 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
214 | u32 blksz = 0; |
215 | u32 data_ctrl = 0; | |
216 | u32 data_len = (u32) (data->blocks * data->blocksize); | |
217 | ||
10ed93dc JR |
218 | if (!host->version2) { |
219 | blksz = (ffs(data->blocksize) - 1); | |
220 | data_ctrl |= ((blksz << 4) & SDI_DCTRL_DBLKSIZE_MASK); | |
221 | } else { | |
222 | blksz = data->blocksize; | |
223 | data_ctrl |= (blksz << SDI_DCTRL_DBLOCKSIZE_V2_SHIFT); | |
224 | } | |
225 | data_ctrl |= SDI_DCTRL_DTEN | SDI_DCTRL_BUSYMODE; | |
23b93e1d MW |
226 | |
227 | writel(SDI_DTIMER_DEFAULT, &host->base->datatimer); | |
228 | writel(data_len, &host->base->datalength); | |
229 | udelay(DATA_REG_DELAY); | |
230 | ||
231 | if (data->flags & MMC_DATA_READ) { | |
232 | data_ctrl |= SDI_DCTRL_DTDIR_IN; | |
233 | writel(data_ctrl, &host->base->datactrl); | |
234 | ||
235 | error = do_command(dev, cmd); | |
236 | if (error) | |
237 | return error; | |
238 | ||
239 | error = read_bytes(dev, (u32 *)data->dest, (u32)data->blocks, | |
240 | (u32)data->blocksize); | |
241 | } else if (data->flags & MMC_DATA_WRITE) { | |
242 | error = do_command(dev, cmd); | |
243 | if (error) | |
244 | return error; | |
245 | ||
246 | writel(data_ctrl, &host->base->datactrl); | |
247 | error = write_bytes(dev, (u32 *)data->src, (u32)data->blocks, | |
10ed93dc | 248 | (u32)data->blocksize); |
23b93e1d MW |
249 | } |
250 | ||
251 | return error; | |
252 | } | |
253 | ||
254 | static int host_request(struct mmc *dev, | |
255 | struct mmc_cmd *cmd, | |
256 | struct mmc_data *data) | |
257 | { | |
258 | int result; | |
259 | ||
260 | if (data) | |
261 | result = do_data_transfer(dev, cmd, data); | |
262 | else | |
263 | result = do_command(dev, cmd); | |
264 | ||
265 | return result; | |
266 | } | |
267 | ||
268 | /* MMC uses open drain drivers in the enumeration phase */ | |
269 | static int mmc_host_reset(struct mmc *dev) | |
270 | { | |
10ed93dc | 271 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d | 272 | |
10ed93dc | 273 | writel(host->pwr_init, &host->base->power); |
23b93e1d MW |
274 | |
275 | return 0; | |
276 | } | |
277 | ||
278 | static void host_set_ios(struct mmc *dev) | |
279 | { | |
10ed93dc | 280 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
281 | u32 sdi_clkcr; |
282 | ||
283 | sdi_clkcr = readl(&host->base->clock); | |
284 | ||
285 | /* Ramp up the clock rate */ | |
286 | if (dev->clock) { | |
287 | u32 clkdiv = 0; | |
10ed93dc | 288 | u32 tmp_clock; |
23b93e1d | 289 | |
93bfd616 | 290 | if (dev->clock >= dev->cfg->f_max) { |
10ed93dc | 291 | clkdiv = 0; |
93bfd616 | 292 | dev->clock = dev->cfg->f_max; |
10ed93dc JR |
293 | } else { |
294 | clkdiv = (host->clock_in / dev->clock) - 2; | |
295 | } | |
23b93e1d | 296 | |
10ed93dc JR |
297 | tmp_clock = host->clock_in / (clkdiv + 2); |
298 | while (tmp_clock > dev->clock) { | |
299 | clkdiv++; | |
300 | tmp_clock = host->clock_in / (clkdiv + 2); | |
301 | } | |
23b93e1d MW |
302 | |
303 | if (clkdiv > SDI_CLKCR_CLKDIV_MASK) | |
304 | clkdiv = SDI_CLKCR_CLKDIV_MASK; | |
305 | ||
10ed93dc JR |
306 | tmp_clock = host->clock_in / (clkdiv + 2); |
307 | dev->clock = tmp_clock; | |
23b93e1d MW |
308 | sdi_clkcr &= ~(SDI_CLKCR_CLKDIV_MASK); |
309 | sdi_clkcr |= clkdiv; | |
310 | } | |
311 | ||
312 | /* Set the bus width */ | |
313 | if (dev->bus_width) { | |
314 | u32 buswidth = 0; | |
315 | ||
316 | switch (dev->bus_width) { | |
317 | case 1: | |
318 | buswidth |= SDI_CLKCR_WIDBUS_1; | |
319 | break; | |
320 | case 4: | |
321 | buswidth |= SDI_CLKCR_WIDBUS_4; | |
322 | break; | |
10ed93dc JR |
323 | case 8: |
324 | buswidth |= SDI_CLKCR_WIDBUS_8; | |
325 | break; | |
23b93e1d | 326 | default: |
10ed93dc | 327 | printf("Invalid bus width: %d\n", dev->bus_width); |
23b93e1d MW |
328 | break; |
329 | } | |
330 | sdi_clkcr &= ~(SDI_CLKCR_WIDBUS_MASK); | |
331 | sdi_clkcr |= buswidth; | |
332 | } | |
333 | ||
334 | writel(sdi_clkcr, &host->base->clock); | |
335 | udelay(CLK_CHANGE_DELAY); | |
336 | } | |
337 | ||
ab769f22 PA |
338 | static const struct mmc_ops arm_pl180_mmci_ops = { |
339 | .send_cmd = host_request, | |
340 | .set_ios = host_set_ios, | |
341 | .init = mmc_host_reset, | |
342 | }; | |
343 | ||
23b93e1d MW |
344 | /* |
345 | * mmc_host_init - initialize the mmc controller. | |
346 | * Set initial clock and power for mmc slot. | |
347 | * Initialize mmc struct and register with mmc framework. | |
348 | */ | |
10ed93dc | 349 | int arm_pl180_mmci_init(struct pl180_mmc_host *host) |
23b93e1d | 350 | { |
93bfd616 | 351 | struct mmc *mmc; |
23b93e1d MW |
352 | u32 sdi_u32; |
353 | ||
10ed93dc JR |
354 | writel(host->pwr_init, &host->base->power); |
355 | writel(host->clkdiv_init, &host->base->clock); | |
23b93e1d MW |
356 | udelay(CLK_CHANGE_DELAY); |
357 | ||
358 | /* Disable mmc interrupts */ | |
359 | sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK; | |
360 | writel(sdi_u32, &host->base->mask0); | |
93bfd616 PA |
361 | |
362 | host->cfg.name = host->name; | |
363 | host->cfg.ops = &arm_pl180_mmci_ops; | |
364 | /* TODO remove the duplicates */ | |
365 | host->cfg.host_caps = host->caps; | |
366 | host->cfg.voltages = host->voltages; | |
367 | host->cfg.f_min = host->clock_min; | |
368 | host->cfg.f_max = host->clock_max; | |
369 | if (host->b_max != 0) | |
370 | host->cfg.b_max = host->b_max; | |
371 | else | |
372 | host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; | |
373 | ||
374 | mmc = mmc_create(&host->cfg, host); | |
375 | if (mmc == NULL) | |
376 | return -1; | |
377 | ||
bcce53d0 | 378 | debug("registered mmc interface number is:%d\n", mmc->block_dev.devnum); |
23b93e1d MW |
379 | |
380 | return 0; | |
381 | } |