]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
71a758e1 MV |
2 | /* |
3 | * Freescale i.MX28 SSP MMC driver | |
4 | * | |
6116f4c5 LM |
5 | * Copyright (C) 2019 DENX Software Engineering |
6 | * Lukasz Majewski, DENX Software Engineering, [email protected] | |
7 | * | |
71a758e1 MV |
8 | * Copyright (C) 2011 Marek Vasut <[email protected]> |
9 | * on behalf of DENX Software Engineering GmbH | |
10 | * | |
11 | * Based on code from LTIB: | |
12 | * (C) Copyright 2008-2010 Freescale Semiconductor, Inc. | |
13 | * Terry Lv | |
14 | * | |
15 | * Copyright 2007, Freescale Semiconductor, Inc | |
16 | * Andy Fleming | |
17 | * | |
18 | * Based vaguely on the pxa mmc code: | |
19 | * (C) Copyright 2003 | |
20 | * Kyle Harris, Nexus Technologies, Inc. [email protected] | |
71a758e1 | 21 | */ |
6116f4c5 | 22 | |
71a758e1 | 23 | #include <common.h> |
f7ae49fc | 24 | #include <log.h> |
71a758e1 MV |
25 | #include <malloc.h> |
26 | #include <mmc.h> | |
cd93d625 | 27 | #include <linux/bitops.h> |
c05ed00a | 28 | #include <linux/delay.h> |
1221ce45 | 29 | #include <linux/errno.h> |
71a758e1 MV |
30 | #include <asm/io.h> |
31 | #include <asm/arch/clock.h> | |
32 | #include <asm/arch/imx-regs.h> | |
33 | #include <asm/arch/sys_proto.h> | |
552a848e | 34 | #include <asm/mach-imx/dma.h> |
4e6d81d1 | 35 | #include <bouncebuf.h> |
71a758e1 | 36 | |
6116f4c5 LM |
37 | #define MXSMMC_MAX_TIMEOUT 10000 |
38 | #define MXSMMC_SMALL_TRANSFER 512 | |
39 | ||
40 | #if !CONFIG_IS_ENABLED(DM_MMC) | |
71a758e1 MV |
41 | struct mxsmmc_priv { |
42 | int id; | |
71a758e1 | 43 | int (*mmc_is_wp)(int); |
90bc2bf2 | 44 | int (*mmc_cd)(int); |
93bfd616 | 45 | struct mmc_config cfg; /* mmc configuration */ |
6116f4c5 LM |
46 | struct mxs_dma_desc *desc; |
47 | uint32_t buswidth; | |
48 | struct mxs_ssp_regs *regs; | |
71a758e1 | 49 | }; |
6116f4c5 LM |
50 | #else /* CONFIG_IS_ENABLED(DM_MMC) */ |
51 | #include <dm/device.h> | |
52 | #include <dm/read.h> | |
53 | #include <dt-structs.h> | |
54 | ||
8a8d24bd | 55 | struct mxsmmc_plat { |
6116f4c5 | 56 | #if CONFIG_IS_ENABLED(OF_PLATDATA) |
df17cdc9 | 57 | struct dtd_fsl_imx23_mmc dtplat; |
6116f4c5 LM |
58 | #endif |
59 | struct mmc_config cfg; | |
60 | struct mmc mmc; | |
61 | fdt_addr_t base; | |
62 | int non_removable; | |
63 | int buswidth; | |
64 | int dma_id; | |
65 | int clk_id; | |
66 | }; | |
67 | ||
68 | struct mxsmmc_priv { | |
69 | int clkid; | |
70 | struct mxs_dma_desc *desc; | |
71 | u32 buswidth; | |
72 | struct mxs_ssp_regs *regs; | |
73 | unsigned int dma_channel; | |
74 | }; | |
75 | #endif | |
76 | ||
77 | #if !CONFIG_IS_ENABLED(DM_MMC) | |
78 | static int mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, | |
79 | struct mmc_data *data); | |
71a758e1 | 80 | |
90bc2bf2 MV |
81 | static int mxsmmc_cd(struct mxsmmc_priv *priv) |
82 | { | |
83 | struct mxs_ssp_regs *ssp_regs = priv->regs; | |
84 | ||
85 | if (priv->mmc_cd) | |
86 | return priv->mmc_cd(priv->id); | |
87 | ||
88 | return !(readl(&ssp_regs->hw_ssp_status) & SSP_STATUS_CARD_DETECT); | |
89 | } | |
90 | ||
6116f4c5 LM |
91 | static int mxsmmc_set_ios(struct mmc *mmc) |
92 | { | |
93 | struct mxsmmc_priv *priv = mmc->priv; | |
94 | struct mxs_ssp_regs *ssp_regs = priv->regs; | |
95 | ||
96 | /* Set the clock speed */ | |
97 | if (mmc->clock) | |
98 | mxs_set_ssp_busclock(priv->id, mmc->clock / 1000); | |
99 | ||
100 | switch (mmc->bus_width) { | |
101 | case 1: | |
102 | priv->buswidth = SSP_CTRL0_BUS_WIDTH_ONE_BIT; | |
103 | break; | |
104 | case 4: | |
105 | priv->buswidth = SSP_CTRL0_BUS_WIDTH_FOUR_BIT; | |
106 | break; | |
107 | case 8: | |
108 | priv->buswidth = SSP_CTRL0_BUS_WIDTH_EIGHT_BIT; | |
109 | break; | |
110 | } | |
111 | ||
112 | /* Set the bus width */ | |
113 | clrsetbits_le32(&ssp_regs->hw_ssp_ctrl0, | |
114 | SSP_CTRL0_BUS_WIDTH_MASK, priv->buswidth); | |
115 | ||
116 | debug("MMC%d: Set %d bits bus width\n", | |
117 | mmc->block_dev.devnum, mmc->bus_width); | |
118 | ||
119 | return 0; | |
120 | } | |
121 | ||
122 | static int mxsmmc_init(struct mmc *mmc) | |
123 | { | |
124 | struct mxsmmc_priv *priv = mmc->priv; | |
125 | struct mxs_ssp_regs *ssp_regs = priv->regs; | |
126 | ||
127 | /* Reset SSP */ | |
128 | mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg); | |
129 | ||
130 | /* Reconfigure the SSP block for MMC operation */ | |
131 | writel(SSP_CTRL1_SSP_MODE_SD_MMC | | |
132 | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS | | |
133 | SSP_CTRL1_DMA_ENABLE | | |
134 | SSP_CTRL1_POLARITY | | |
135 | SSP_CTRL1_RECV_TIMEOUT_IRQ_EN | | |
136 | SSP_CTRL1_DATA_CRC_IRQ_EN | | |
137 | SSP_CTRL1_DATA_TIMEOUT_IRQ_EN | | |
138 | SSP_CTRL1_RESP_TIMEOUT_IRQ_EN | | |
139 | SSP_CTRL1_RESP_ERR_IRQ_EN, | |
140 | &ssp_regs->hw_ssp_ctrl1_set); | |
141 | ||
142 | /* Set initial bit clock 400 KHz */ | |
143 | mxs_set_ssp_busclock(priv->id, 400); | |
144 | ||
145 | /* Send initial 74 clock cycles (185 us @ 400 KHz)*/ | |
146 | writel(SSP_CMD0_CONT_CLKING_EN, &ssp_regs->hw_ssp_cmd0_set); | |
147 | udelay(200); | |
148 | writel(SSP_CMD0_CONT_CLKING_EN, &ssp_regs->hw_ssp_cmd0_clr); | |
149 | ||
150 | return 0; | |
151 | } | |
152 | ||
153 | static const struct mmc_ops mxsmmc_ops = { | |
154 | .send_cmd = mxsmmc_send_cmd, | |
155 | .set_ios = mxsmmc_set_ios, | |
156 | .init = mxsmmc_init, | |
157 | }; | |
158 | ||
b75d8dc5 MY |
159 | int mxsmmc_initialize(struct bd_info *bis, int id, int (*wp)(int), |
160 | int (*cd)(int)) | |
6116f4c5 LM |
161 | { |
162 | struct mmc *mmc = NULL; | |
163 | struct mxsmmc_priv *priv = NULL; | |
164 | int ret; | |
165 | const unsigned int mxsmmc_clk_id = mxs_ssp_clock_by_bus(id); | |
166 | ||
167 | if (!mxs_ssp_bus_id_valid(id)) | |
168 | return -ENODEV; | |
169 | ||
170 | priv = malloc(sizeof(struct mxsmmc_priv)); | |
171 | if (!priv) | |
172 | return -ENOMEM; | |
173 | ||
174 | priv->desc = mxs_dma_desc_alloc(); | |
175 | if (!priv->desc) { | |
176 | free(priv); | |
177 | return -ENOMEM; | |
178 | } | |
179 | ||
180 | ret = mxs_dma_init_channel(MXS_DMA_CHANNEL_AHB_APBH_SSP0 + id); | |
181 | if (ret) | |
182 | return ret; | |
183 | ||
184 | priv->mmc_is_wp = wp; | |
185 | priv->mmc_cd = cd; | |
186 | priv->id = id; | |
187 | priv->regs = mxs_ssp_regs_by_bus(id); | |
188 | ||
189 | priv->cfg.name = "MXS MMC"; | |
190 | priv->cfg.ops = &mxsmmc_ops; | |
191 | ||
192 | priv->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34; | |
193 | ||
194 | priv->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT | | |
195 | MMC_MODE_HS_52MHz | MMC_MODE_HS; | |
196 | ||
197 | /* | |
198 | * SSPCLK = 480 * 18 / 29 / 1 = 297.731 MHz | |
199 | * SSP bit rate = SSPCLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE)), | |
200 | * CLOCK_DIVIDE has to be an even value from 2 to 254, and | |
201 | * CLOCK_RATE could be any integer from 0 to 255. | |
202 | */ | |
203 | priv->cfg.f_min = 400000; | |
204 | priv->cfg.f_max = mxc_get_clock(MXC_SSP0_CLK + mxsmmc_clk_id) | |
205 | * 1000 / 2; | |
206 | priv->cfg.b_max = 0x20; | |
207 | ||
208 | mmc = mmc_create(&priv->cfg, priv); | |
209 | if (!mmc) { | |
210 | mxs_dma_desc_free(priv->desc); | |
211 | free(priv); | |
212 | return -ENOMEM; | |
213 | } | |
214 | return 0; | |
215 | } | |
216 | #endif /* CONFIG_IS_ENABLED(DM_MMC) */ | |
217 | ||
86983328 MV |
218 | static int mxsmmc_send_cmd_pio(struct mxsmmc_priv *priv, struct mmc_data *data) |
219 | { | |
220 | struct mxs_ssp_regs *ssp_regs = priv->regs; | |
221 | uint32_t *data_ptr; | |
222 | int timeout = MXSMMC_MAX_TIMEOUT; | |
223 | uint32_t reg; | |
224 | uint32_t data_count = data->blocksize * data->blocks; | |
225 | ||
226 | if (data->flags & MMC_DATA_READ) { | |
227 | data_ptr = (uint32_t *)data->dest; | |
228 | while (data_count && --timeout) { | |
229 | reg = readl(&ssp_regs->hw_ssp_status); | |
230 | if (!(reg & SSP_STATUS_FIFO_EMPTY)) { | |
231 | *data_ptr++ = readl(&ssp_regs->hw_ssp_data); | |
232 | data_count -= 4; | |
233 | timeout = MXSMMC_MAX_TIMEOUT; | |
234 | } else | |
235 | udelay(1000); | |
236 | } | |
237 | } else { | |
238 | data_ptr = (uint32_t *)data->src; | |
239 | timeout *= 100; | |
240 | while (data_count && --timeout) { | |
241 | reg = readl(&ssp_regs->hw_ssp_status); | |
242 | if (!(reg & SSP_STATUS_FIFO_FULL)) { | |
243 | writel(*data_ptr++, &ssp_regs->hw_ssp_data); | |
244 | data_count -= 4; | |
245 | timeout = MXSMMC_MAX_TIMEOUT; | |
246 | } else | |
247 | udelay(1000); | |
248 | } | |
249 | } | |
250 | ||
915ffa52 | 251 | return timeout ? 0 : -ECOMM; |
86983328 | 252 | } |
20255900 | 253 | |
86983328 MV |
254 | static int mxsmmc_send_cmd_dma(struct mxsmmc_priv *priv, struct mmc_data *data) |
255 | { | |
256 | uint32_t data_count = data->blocksize * data->blocks; | |
86983328 | 257 | int dmach; |
abb85be7 | 258 | struct mxs_dma_desc *desc = priv->desc; |
84d35b28 SW |
259 | void *addr; |
260 | unsigned int flags; | |
261 | struct bounce_buffer bbstate; | |
abb85be7 MV |
262 | |
263 | memset(desc, 0, sizeof(struct mxs_dma_desc)); | |
264 | desc->address = (dma_addr_t)desc; | |
86983328 | 265 | |
86983328 MV |
266 | if (data->flags & MMC_DATA_READ) { |
267 | priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE; | |
4e6d81d1 MV |
268 | addr = data->dest; |
269 | flags = GEN_BB_WRITE; | |
86983328 MV |
270 | } else { |
271 | priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ; | |
4e6d81d1 MV |
272 | addr = (void *)data->src; |
273 | flags = GEN_BB_READ; | |
274 | } | |
275 | ||
84d35b28 | 276 | bounce_buffer_start(&bbstate, addr, data_count, flags); |
4e6d81d1 | 277 | |
84d35b28 | 278 | priv->desc->cmd.address = (dma_addr_t)bbstate.bounce_buffer; |
97ed12ce | 279 | |
86983328 MV |
280 | priv->desc->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM | |
281 | (data_count << MXS_DMA_DESC_BYTES_OFFSET); | |
282 | ||
6116f4c5 | 283 | #if !CONFIG_IS_ENABLED(DM_MMC) |
3430e0bd | 284 | dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + priv->id; |
6116f4c5 LM |
285 | #else |
286 | dmach = priv->dma_channel; | |
287 | #endif | |
86983328 | 288 | mxs_dma_desc_append(dmach, priv->desc); |
4e6d81d1 | 289 | if (mxs_dma_go(dmach)) { |
84d35b28 | 290 | bounce_buffer_stop(&bbstate); |
915ffa52 | 291 | return -ECOMM; |
4e6d81d1 | 292 | } |
86983328 | 293 | |
84d35b28 | 294 | bounce_buffer_stop(&bbstate); |
4e6d81d1 | 295 | |
86983328 MV |
296 | return 0; |
297 | } | |
86983328 | 298 | |
6116f4c5 | 299 | #if !CONFIG_IS_ENABLED(DM_MMC) |
71a758e1 MV |
300 | /* |
301 | * Sends a command out on the bus. Takes the mmc pointer, | |
302 | * a command pointer, and an optional data pointer. | |
303 | */ | |
304 | static int | |
305 | mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) | |
306 | { | |
93bfd616 | 307 | struct mxsmmc_priv *priv = mmc->priv; |
9c471142 | 308 | struct mxs_ssp_regs *ssp_regs = priv->regs; |
6116f4c5 LM |
309 | #else |
310 | static int | |
311 | mxsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, struct mmc_data *data) | |
312 | { | |
8a8d24bd | 313 | struct mxsmmc_plat *plat = dev_get_plat(dev); |
6116f4c5 LM |
314 | struct mxsmmc_priv *priv = dev_get_priv(dev); |
315 | struct mxs_ssp_regs *ssp_regs = priv->regs; | |
316 | struct mmc *mmc = &plat->mmc; | |
317 | #endif | |
71a758e1 MV |
318 | uint32_t reg; |
319 | int timeout; | |
71a758e1 | 320 | uint32_t ctrl0; |
86983328 | 321 | int ret; |
6116f4c5 LM |
322 | #if !CONFIG_IS_ENABLED(DM_MMC) |
323 | int devnum = mmc->block_dev.devnum; | |
324 | #else | |
325 | int devnum = mmc_get_blk_desc(mmc)->devnum; | |
326 | #endif | |
327 | debug("MMC%d: CMD%d\n", devnum, cmd->cmdidx); | |
71a758e1 MV |
328 | |
329 | /* Check bus busy */ | |
330 | timeout = MXSMMC_MAX_TIMEOUT; | |
331 | while (--timeout) { | |
332 | udelay(1000); | |
333 | reg = readl(&ssp_regs->hw_ssp_status); | |
334 | if (!(reg & | |
335 | (SSP_STATUS_BUSY | SSP_STATUS_DATA_BUSY | | |
336 | SSP_STATUS_CMD_BUSY))) { | |
337 | break; | |
338 | } | |
339 | } | |
340 | ||
341 | if (!timeout) { | |
6116f4c5 | 342 | printf("MMC%d: Bus busy timeout!\n", devnum); |
915ffa52 | 343 | return -ETIMEDOUT; |
71a758e1 | 344 | } |
6116f4c5 | 345 | #if !CONFIG_IS_ENABLED(DM_MMC) |
71a758e1 | 346 | /* See if card is present */ |
90bc2bf2 | 347 | if (!mxsmmc_cd(priv)) { |
6116f4c5 | 348 | printf("MMC%d: No card detected!\n", devnum); |
915ffa52 | 349 | return -ENOMEDIUM; |
71a758e1 | 350 | } |
6116f4c5 | 351 | #endif |
71a758e1 MV |
352 | /* Start building CTRL0 contents */ |
353 | ctrl0 = priv->buswidth; | |
354 | ||
355 | /* Set up command */ | |
356 | if (!(cmd->resp_type & MMC_RSP_CRC)) | |
357 | ctrl0 |= SSP_CTRL0_IGNORE_CRC; | |
358 | if (cmd->resp_type & MMC_RSP_PRESENT) /* Need to get response */ | |
359 | ctrl0 |= SSP_CTRL0_GET_RESP; | |
360 | if (cmd->resp_type & MMC_RSP_136) /* It's a 136 bits response */ | |
361 | ctrl0 |= SSP_CTRL0_LONG_RESP; | |
362 | ||
abb85be7 MV |
363 | if (data && (data->blocksize * data->blocks < MXSMMC_SMALL_TRANSFER)) |
364 | writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_clr); | |
365 | else | |
366 | writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_set); | |
367 | ||
71a758e1 MV |
368 | /* Command index */ |
369 | reg = readl(&ssp_regs->hw_ssp_cmd0); | |
370 | reg &= ~(SSP_CMD0_CMD_MASK | SSP_CMD0_APPEND_8CYC); | |
371 | reg |= cmd->cmdidx << SSP_CMD0_CMD_OFFSET; | |
372 | if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) | |
373 | reg |= SSP_CMD0_APPEND_8CYC; | |
374 | writel(reg, &ssp_regs->hw_ssp_cmd0); | |
375 | ||
376 | /* Command argument */ | |
377 | writel(cmd->cmdarg, &ssp_regs->hw_ssp_cmd1); | |
378 | ||
379 | /* Set up data */ | |
380 | if (data) { | |
381 | /* READ or WRITE */ | |
382 | if (data->flags & MMC_DATA_READ) { | |
383 | ctrl0 |= SSP_CTRL0_READ; | |
6116f4c5 | 384 | #if !CONFIG_IS_ENABLED(DM_MMC) |
c7527b70 | 385 | } else if (priv->mmc_is_wp && |
6116f4c5 LM |
386 | priv->mmc_is_wp(devnum)) { |
387 | printf("MMC%d: Can not write a locked card!\n", devnum); | |
915ffa52 | 388 | return -EOPNOTSUPP; |
6116f4c5 | 389 | #endif |
71a758e1 | 390 | } |
71a758e1 | 391 | ctrl0 |= SSP_CTRL0_DATA_XFER; |
e5b380ac MV |
392 | |
393 | reg = data->blocksize * data->blocks; | |
394 | #if defined(CONFIG_MX23) | |
395 | ctrl0 |= reg & SSP_CTRL0_XFER_COUNT_MASK; | |
396 | ||
397 | clrsetbits_le32(&ssp_regs->hw_ssp_cmd0, | |
398 | SSP_CMD0_BLOCK_SIZE_MASK | SSP_CMD0_BLOCK_COUNT_MASK, | |
399 | ((data->blocks - 1) << SSP_CMD0_BLOCK_COUNT_OFFSET) | | |
400 | ((ffs(data->blocksize) - 1) << | |
401 | SSP_CMD0_BLOCK_SIZE_OFFSET)); | |
402 | #elif defined(CONFIG_MX28) | |
403 | writel(reg, &ssp_regs->hw_ssp_xfer_size); | |
404 | ||
71a758e1 MV |
405 | reg = ((data->blocks - 1) << |
406 | SSP_BLOCK_SIZE_BLOCK_COUNT_OFFSET) | | |
407 | ((ffs(data->blocksize) - 1) << | |
408 | SSP_BLOCK_SIZE_BLOCK_SIZE_OFFSET); | |
409 | writel(reg, &ssp_regs->hw_ssp_block_size); | |
e5b380ac | 410 | #endif |
71a758e1 MV |
411 | } |
412 | ||
413 | /* Kick off the command */ | |
414 | ctrl0 |= SSP_CTRL0_WAIT_FOR_IRQ | SSP_CTRL0_ENABLE | SSP_CTRL0_RUN; | |
415 | writel(ctrl0, &ssp_regs->hw_ssp_ctrl0); | |
416 | ||
417 | /* Wait for the command to complete */ | |
418 | timeout = MXSMMC_MAX_TIMEOUT; | |
419 | while (--timeout) { | |
420 | udelay(1000); | |
421 | reg = readl(&ssp_regs->hw_ssp_status); | |
422 | if (!(reg & SSP_STATUS_CMD_BUSY)) | |
423 | break; | |
424 | } | |
425 | ||
426 | if (!timeout) { | |
6116f4c5 | 427 | printf("MMC%d: Command %d busy\n", devnum, cmd->cmdidx); |
915ffa52 | 428 | return -ETIMEDOUT; |
71a758e1 MV |
429 | } |
430 | ||
431 | /* Check command timeout */ | |
432 | if (reg & SSP_STATUS_RESP_TIMEOUT) { | |
cf31914c LM |
433 | debug("MMC%d: Command %d timeout (status 0x%08x)\n", |
434 | devnum, cmd->cmdidx, reg); | |
915ffa52 | 435 | return -ETIMEDOUT; |
71a758e1 MV |
436 | } |
437 | ||
438 | /* Check command errors */ | |
439 | if (reg & (SSP_STATUS_RESP_CRC_ERR | SSP_STATUS_RESP_ERR)) { | |
440 | printf("MMC%d: Command %d error (status 0x%08x)!\n", | |
6116f4c5 | 441 | devnum, cmd->cmdidx, reg); |
915ffa52 | 442 | return -ECOMM; |
71a758e1 MV |
443 | } |
444 | ||
445 | /* Copy response to response buffer */ | |
446 | if (cmd->resp_type & MMC_RSP_136) { | |
447 | cmd->response[3] = readl(&ssp_regs->hw_ssp_sdresp0); | |
448 | cmd->response[2] = readl(&ssp_regs->hw_ssp_sdresp1); | |
449 | cmd->response[1] = readl(&ssp_regs->hw_ssp_sdresp2); | |
450 | cmd->response[0] = readl(&ssp_regs->hw_ssp_sdresp3); | |
451 | } else | |
452 | cmd->response[0] = readl(&ssp_regs->hw_ssp_sdresp0); | |
453 | ||
454 | /* Return if no data to process */ | |
455 | if (!data) | |
456 | return 0; | |
457 | ||
20255900 | 458 | if (data->blocksize * data->blocks < MXSMMC_SMALL_TRANSFER) { |
20255900 MV |
459 | ret = mxsmmc_send_cmd_pio(priv, data); |
460 | if (ret) { | |
461 | printf("MMC%d: Data timeout with command %d " | |
6116f4c5 | 462 | "(status 0x%08x)!\n", devnum, cmd->cmdidx, reg); |
20255900 MV |
463 | return ret; |
464 | } | |
abb85be7 MV |
465 | } else { |
466 | ret = mxsmmc_send_cmd_dma(priv, data); | |
467 | if (ret) { | |
6116f4c5 | 468 | printf("MMC%d: DMA transfer failed\n", devnum); |
abb85be7 MV |
469 | return ret; |
470 | } | |
4cc76c60 | 471 | } |
3687c415 | 472 | |
71a758e1 MV |
473 | /* Check data errors */ |
474 | reg = readl(&ssp_regs->hw_ssp_status); | |
475 | if (reg & | |
476 | (SSP_STATUS_TIMEOUT | SSP_STATUS_DATA_CRC_ERR | | |
477 | SSP_STATUS_FIFO_OVRFLW | SSP_STATUS_FIFO_UNDRFLW)) { | |
478 | printf("MMC%d: Data error with command %d (status 0x%08x)!\n", | |
6116f4c5 | 479 | devnum, cmd->cmdidx, reg); |
915ffa52 | 480 | return -ECOMM; |
71a758e1 MV |
481 | } |
482 | ||
483 | return 0; | |
484 | } | |
485 | ||
6116f4c5 LM |
486 | #if CONFIG_IS_ENABLED(DM_MMC) |
487 | /* Base numbers of i.MX2[38] clk for ssp0 IP block */ | |
488 | #define MXS_SSP_IMX23_CLKID_SSP0 33 | |
489 | #define MXS_SSP_IMX28_CLKID_SSP0 46 | |
490 | ||
491 | static int mxsmmc_get_cd(struct udevice *dev) | |
71a758e1 | 492 | { |
8a8d24bd | 493 | struct mxsmmc_plat *plat = dev_get_plat(dev); |
6116f4c5 LM |
494 | struct mxsmmc_priv *priv = dev_get_priv(dev); |
495 | struct mxs_ssp_regs *ssp_regs = priv->regs; | |
496 | ||
497 | if (plat->non_removable) | |
498 | return 1; | |
499 | ||
500 | return !(readl(&ssp_regs->hw_ssp_status) & SSP_STATUS_CARD_DETECT); | |
501 | } | |
502 | ||
503 | static int mxsmmc_set_ios(struct udevice *dev) | |
504 | { | |
8a8d24bd | 505 | struct mxsmmc_plat *plat = dev_get_plat(dev); |
6116f4c5 | 506 | struct mxsmmc_priv *priv = dev_get_priv(dev); |
9c471142 | 507 | struct mxs_ssp_regs *ssp_regs = priv->regs; |
6116f4c5 | 508 | struct mmc *mmc = &plat->mmc; |
71a758e1 MV |
509 | |
510 | /* Set the clock speed */ | |
511 | if (mmc->clock) | |
6116f4c5 | 512 | mxs_set_ssp_busclock(priv->clkid, mmc->clock / 1000); |
71a758e1 MV |
513 | |
514 | switch (mmc->bus_width) { | |
515 | case 1: | |
516 | priv->buswidth = SSP_CTRL0_BUS_WIDTH_ONE_BIT; | |
517 | break; | |
518 | case 4: | |
519 | priv->buswidth = SSP_CTRL0_BUS_WIDTH_FOUR_BIT; | |
520 | break; | |
521 | case 8: | |
522 | priv->buswidth = SSP_CTRL0_BUS_WIDTH_EIGHT_BIT; | |
523 | break; | |
524 | } | |
525 | ||
526 | /* Set the bus width */ | |
527 | clrsetbits_le32(&ssp_regs->hw_ssp_ctrl0, | |
528 | SSP_CTRL0_BUS_WIDTH_MASK, priv->buswidth); | |
529 | ||
6116f4c5 LM |
530 | debug("MMC%d: Set %d bits bus width\n", mmc_get_blk_desc(mmc)->devnum, |
531 | mmc->bus_width); | |
07b0b9c0 JC |
532 | |
533 | return 0; | |
71a758e1 MV |
534 | } |
535 | ||
6116f4c5 | 536 | static int mxsmmc_init(struct udevice *dev) |
71a758e1 | 537 | { |
6116f4c5 | 538 | struct mxsmmc_priv *priv = dev_get_priv(dev); |
9c471142 | 539 | struct mxs_ssp_regs *ssp_regs = priv->regs; |
71a758e1 MV |
540 | |
541 | /* Reset SSP */ | |
fa7a51cb | 542 | mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg); |
71a758e1 | 543 | |
8000d8a8 OS |
544 | /* Reconfigure the SSP block for MMC operation */ |
545 | writel(SSP_CTRL1_SSP_MODE_SD_MMC | | |
546 | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS | | |
547 | SSP_CTRL1_DMA_ENABLE | | |
548 | SSP_CTRL1_POLARITY | | |
549 | SSP_CTRL1_RECV_TIMEOUT_IRQ_EN | | |
550 | SSP_CTRL1_DATA_CRC_IRQ_EN | | |
551 | SSP_CTRL1_DATA_TIMEOUT_IRQ_EN | | |
552 | SSP_CTRL1_RESP_TIMEOUT_IRQ_EN | | |
553 | SSP_CTRL1_RESP_ERR_IRQ_EN, | |
554 | &ssp_regs->hw_ssp_ctrl1_set); | |
71a758e1 MV |
555 | |
556 | /* Set initial bit clock 400 KHz */ | |
6116f4c5 | 557 | mxs_set_ssp_busclock(priv->clkid, 400); |
71a758e1 MV |
558 | |
559 | /* Send initial 74 clock cycles (185 us @ 400 KHz)*/ | |
560 | writel(SSP_CMD0_CONT_CLKING_EN, &ssp_regs->hw_ssp_cmd0_set); | |
561 | udelay(200); | |
562 | writel(SSP_CMD0_CONT_CLKING_EN, &ssp_regs->hw_ssp_cmd0_clr); | |
563 | ||
564 | return 0; | |
565 | } | |
566 | ||
6116f4c5 | 567 | static int mxsmmc_probe(struct udevice *dev) |
71a758e1 | 568 | { |
6116f4c5 | 569 | struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); |
8a8d24bd | 570 | struct mxsmmc_plat *plat = dev_get_plat(dev); |
6116f4c5 LM |
571 | struct mxsmmc_priv *priv = dev_get_priv(dev); |
572 | struct blk_desc *bdesc; | |
573 | struct mmc *mmc; | |
574 | int ret, clkid; | |
575 | ||
576 | debug("%s: probe\n", __func__); | |
577 | ||
578 | #if CONFIG_IS_ENABLED(OF_PLATDATA) | |
df17cdc9 | 579 | struct dtd_fsl_imx23_mmc *dtplat = &plat->dtplat; |
6116f4c5 LM |
580 | struct phandle_1_arg *p1a = &dtplat->clocks[0]; |
581 | ||
582 | priv->buswidth = dtplat->bus_width; | |
583 | priv->regs = (struct mxs_ssp_regs *)dtplat->reg[0]; | |
584 | priv->dma_channel = dtplat->dmas[1]; | |
585 | clkid = p1a->arg[0]; | |
586 | plat->non_removable = dtplat->non_removable; | |
587 | ||
588 | debug("OF_PLATDATA: regs: 0x%p bw: %d clkid: %d non_removable: %d\n", | |
589 | priv->regs, priv->buswidth, clkid, plat->non_removable); | |
590 | #else | |
591 | priv->regs = (struct mxs_ssp_regs *)plat->base; | |
592 | priv->dma_channel = plat->dma_id; | |
593 | clkid = plat->clk_id; | |
594 | #endif | |
71a758e1 | 595 | |
6116f4c5 LM |
596 | #ifdef CONFIG_MX28 |
597 | priv->clkid = clkid - MXS_SSP_IMX28_CLKID_SSP0; | |
598 | #else /* CONFIG_MX23 */ | |
599 | priv->clkid = clkid - MXS_SSP_IMX23_CLKID_SSP0; | |
600 | #endif | |
601 | mmc = &plat->mmc; | |
602 | mmc->cfg = &plat->cfg; | |
603 | mmc->dev = dev; | |
71a758e1 | 604 | |
3687c415 MV |
605 | priv->desc = mxs_dma_desc_alloc(); |
606 | if (!priv->desc) { | |
6116f4c5 | 607 | printf("%s: Cannot allocate DMA descriptor\n", __func__); |
3687c415 MV |
608 | return -ENOMEM; |
609 | } | |
610 | ||
6116f4c5 | 611 | ret = mxs_dma_init_channel(priv->dma_channel); |
96666a39 MV |
612 | if (ret) |
613 | return ret; | |
614 | ||
6116f4c5 LM |
615 | plat->cfg.name = "MXS MMC"; |
616 | plat->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34; | |
71a758e1 | 617 | |
6116f4c5 LM |
618 | plat->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT | |
619 | MMC_MODE_HS_52MHz | MMC_MODE_HS; | |
71a758e1 MV |
620 | |
621 | /* | |
622 | * SSPCLK = 480 * 18 / 29 / 1 = 297.731 MHz | |
623 | * SSP bit rate = SSPCLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE)), | |
624 | * CLOCK_DIVIDE has to be an even value from 2 to 254, and | |
625 | * CLOCK_RATE could be any integer from 0 to 255. | |
626 | */ | |
6116f4c5 LM |
627 | plat->cfg.f_min = 400000; |
628 | plat->cfg.f_max = mxc_get_clock(MXC_SSP0_CLK + priv->clkid) * 1000 / 2; | |
629 | plat->cfg.b_max = 0x20; | |
71a758e1 | 630 | |
6116f4c5 LM |
631 | bdesc = mmc_get_blk_desc(mmc); |
632 | if (!bdesc) { | |
633 | printf("%s: No block device descriptor!\n", __func__); | |
634 | return -ENODEV; | |
635 | } | |
636 | ||
637 | if (plat->non_removable) | |
638 | bdesc->removable = 0; | |
639 | ||
640 | ret = mxsmmc_init(dev); | |
641 | if (ret) | |
642 | printf("%s: MMC%d init error %d\n", __func__, | |
643 | bdesc->devnum, ret); | |
644 | ||
645 | /* Set the initial clock speed */ | |
646 | mmc_set_clock(mmc, 400000, MMC_CLK_ENABLE); | |
647 | ||
648 | upriv->mmc = mmc; | |
649 | ||
650 | return 0; | |
651 | }; | |
652 | ||
653 | #if CONFIG_IS_ENABLED(BLK) | |
654 | static int mxsmmc_bind(struct udevice *dev) | |
655 | { | |
8a8d24bd | 656 | struct mxsmmc_plat *plat = dev_get_plat(dev); |
6116f4c5 LM |
657 | |
658 | return mmc_bind(dev, &plat->mmc, &plat->cfg); | |
659 | } | |
660 | #endif | |
661 | ||
662 | static const struct dm_mmc_ops mxsmmc_ops = { | |
663 | .get_cd = mxsmmc_get_cd, | |
664 | .send_cmd = mxsmmc_send_cmd, | |
665 | .set_ios = mxsmmc_set_ios, | |
666 | }; | |
667 | ||
668 | #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) | |
d1998a9f | 669 | static int mxsmmc_of_to_plat(struct udevice *bus) |
6116f4c5 | 670 | { |
0fd3d911 | 671 | struct mxsmmc_plat *plat = dev_get_plat(bus); |
6116f4c5 LM |
672 | u32 prop[2]; |
673 | int ret; | |
674 | ||
675 | plat->base = dev_read_addr(bus); | |
676 | plat->buswidth = | |
677 | dev_read_u32_default(bus, "bus-width", 1); | |
678 | plat->non_removable = dev_read_bool(bus, "non-removable"); | |
679 | ||
680 | ret = dev_read_u32_array(bus, "dmas", prop, ARRAY_SIZE(prop)); | |
681 | if (ret) { | |
682 | printf("%s: Reading 'dmas' property failed!\n", __func__); | |
683 | return ret; | |
684 | } | |
685 | plat->dma_id = prop[1]; | |
686 | ||
687 | ret = dev_read_u32_array(bus, "clocks", prop, ARRAY_SIZE(prop)); | |
688 | if (ret) { | |
689 | printf("%s: Reading 'clocks' property failed!\n", __func__); | |
690 | return ret; | |
93bfd616 | 691 | } |
6116f4c5 LM |
692 | plat->clk_id = prop[1]; |
693 | ||
694 | debug("%s: base=0x%x, bus_width=%d %s dma_id=%d clk_id=%d\n", | |
695 | __func__, (uint)plat->base, plat->buswidth, | |
696 | plat->non_removable ? "non-removable" : NULL, | |
697 | plat->dma_id, plat->clk_id); | |
698 | ||
71a758e1 MV |
699 | return 0; |
700 | } | |
6116f4c5 LM |
701 | |
702 | static const struct udevice_id mxsmmc_ids[] = { | |
703 | { .compatible = "fsl,imx23-mmc", }, | |
704 | { .compatible = "fsl,imx28-mmc", }, | |
705 | { /* sentinel */ } | |
706 | }; | |
707 | #endif | |
708 | ||
e3e2470f | 709 | U_BOOT_DRIVER(fsl_imx23_mmc) = { |
6116f4c5 | 710 | .name = "fsl_imx23_mmc", |
6116f4c5 LM |
711 | .id = UCLASS_MMC, |
712 | #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) | |
713 | .of_match = mxsmmc_ids, | |
d1998a9f | 714 | .of_to_plat = mxsmmc_of_to_plat, |
6116f4c5 LM |
715 | #endif |
716 | .ops = &mxsmmc_ops, | |
717 | #if CONFIG_IS_ENABLED(BLK) | |
718 | .bind = mxsmmc_bind, | |
719 | #endif | |
720 | .probe = mxsmmc_probe, | |
41575d8e | 721 | .priv_auto = sizeof(struct mxsmmc_priv), |
8a8d24bd | 722 | .plat_auto = sizeof(struct mxsmmc_plat), |
6116f4c5 LM |
723 | }; |
724 | ||
bdf8fd76 | 725 | DM_DRIVER_ALIAS(fsl_imx23_mmc, fsl_imx28_mmc) |
6116f4c5 | 726 | #endif /* CONFIG_DM_MMC */ |