]>
git.ipfire.org Git - people/ms/u-boot.git/blob - drivers/mmc/gen_atmel_mci.c
3 * Rob Emanuele <rob@emanuele.us>
4 * Reinhard Meyer, EMK Elektronik <reinhard.meyer@emk-elektronik.de>
7 * Copyright (C) 2004-2006 Atmel Corporation
9 * See file CREDITS for list of people who contributed to this
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License as
14 * published by the Free Software Foundation; either version 2 of
15 * the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
33 #include <asm/errno.h>
34 #include <asm/byteorder.h>
35 #include <asm/arch/clk.h>
36 #include <asm/arch/memory-map.h>
37 #include "atmel_mci.h"
39 #ifndef CONFIG_SYS_MMC_CLK_OD
40 # define CONFIG_SYS_MMC_CLK_OD 150000
43 #define MMC_DEFAULT_BLKLEN 512
45 #if defined(CONFIG_ATMEL_MCI_PORTB)
51 static int initialized
= 0;
54 * Print command and status:
56 * - always when DEBUG is defined
59 static void dump_cmd(u32 cmdr
, u32 arg
, u32 status
, const char* msg
)
61 printf("gen_atmel_mci: CMDR %08x (%2u) ARGR %08x (SR: %08x) %s\n",
62 cmdr
, cmdr
&0x3F, arg
, status
, msg
);
65 /* Setup for MCI Clock and Block Size */
66 static void mci_set_mode(struct mmc
*mmc
, u32 hz
, u32 blklen
)
68 atmel_mci_t
*mci
= (atmel_mci_t
*)mmc
->priv
;
69 u32 bus_hz
= get_mci_clk_rate();
72 debug("mci: bus_hz is %u, setting clock %u Hz, block size %u\n",
75 /* find lowest clkdiv yielding a rate <= than requested */
76 for (clkdiv
=0; clkdiv
<255; clkdiv
++) {
77 if ((bus_hz
/ (clkdiv
+1) / 2) <= hz
)
81 printf("mci: setting clock %u Hz, block size %u\n",
82 (bus_hz
/ (clkdiv
+1)) / 2, blklen
);
85 /* On some platforms RDPROOF and WRPROOF are ignored */
86 writel((MMCI_BF(CLKDIV
, clkdiv
)
87 | MMCI_BF(BLKLEN
, blklen
)
89 | MMCI_BIT(WRPROOF
)), &mci
->mr
);
93 /* Return the CMDR with flags for a given command and data packet */
94 static u32
mci_encode_cmd(
95 struct mmc_cmd
*cmd
, struct mmc_data
*data
, u32
* error_flags
)
99 /* Default Flags for Errors */
100 *error_flags
|= (MMCI_BIT(DTOE
) | MMCI_BIT(RDIRE
) | MMCI_BIT(RENDE
) |
101 MMCI_BIT(RINDE
) | MMCI_BIT(RTOE
));
103 /* Default Flags for the Command */
104 cmdr
|= MMCI_BIT(MAXLAT
);
107 cmdr
|= MMCI_BF(TRCMD
, 1);
108 if (data
->blocks
> 1)
109 cmdr
|= MMCI_BF(TRTYP
, 1);
110 if (data
->flags
& MMC_DATA_READ
)
111 cmdr
|= MMCI_BIT(TRDIR
);
114 if (cmd
->resp_type
& MMC_RSP_CRC
)
115 *error_flags
|= MMCI_BIT(RCRCE
);
116 if (cmd
->resp_type
& MMC_RSP_136
)
117 cmdr
|= MMCI_BF(RSPTYP
, 2);
118 else if (cmd
->resp_type
& MMC_RSP_BUSY
)
119 cmdr
|= MMCI_BF(RSPTYP
, 3);
120 else if (cmd
->resp_type
& MMC_RSP_PRESENT
)
121 cmdr
|= MMCI_BF(RSPTYP
, 1);
123 return cmdr
| MMCI_BF(CMDNB
, cmd
->cmdidx
);
126 /* Entered into function pointer in mci_send_cmd */
127 static u32
mci_data_read(atmel_mci_t
*mci
, u32
* data
, u32 error_flags
)
132 status
= readl(&mci
->sr
);
133 if (status
& (error_flags
| MMCI_BIT(OVRE
)))
135 } while (!(status
& MMCI_BIT(RXRDY
)));
137 if (status
& MMCI_BIT(RXRDY
)) {
138 *data
= readl(&mci
->rdr
);
145 /* Entered into function pointer in mci_send_cmd */
146 static u32
mci_data_write(atmel_mci_t
*mci
, u32
* data
, u32 error_flags
)
151 status
= readl(&mci
->sr
);
152 if (status
& (error_flags
| MMCI_BIT(UNRE
)))
154 } while (!(status
& MMCI_BIT(TXRDY
)));
156 if (status
& MMCI_BIT(TXRDY
)) {
157 writel(*data
, &mci
->tdr
);
165 * Entered into mmc structure during driver init
167 * Sends a command out on the bus and deals with the block data.
168 * Takes the mmc pointer, a command pointer, and an optional data pointer.
171 mci_send_cmd(struct mmc
*mmc
, struct mmc_cmd
*cmd
, struct mmc_data
*data
)
173 atmel_mci_t
*mci
= (atmel_mci_t
*)mmc
->priv
;
179 puts ("MCI not initialized!\n");
183 /* Figure out the transfer arguments */
184 cmdr
= mci_encode_cmd(cmd
, data
, &error_flags
);
186 /* Send the command */
187 writel(cmd
->cmdarg
, &mci
->argr
);
188 writel(cmdr
, &mci
->cmdr
);
191 dump_cmd(cmdr
, cmd
->cmdarg
, 0, "DEBUG");
194 /* Wait for the command to complete */
195 while (!((status
= readl(&mci
->sr
)) & MMCI_BIT(CMDRDY
)));
197 if (status
& error_flags
) {
198 dump_cmd(cmdr
, cmd
->cmdarg
, status
, "Command Failed");
202 /* Copy the response to the response buffer */
203 if (cmd
->resp_type
& MMC_RSP_136
) {
204 cmd
->response
[0] = readl(&mci
->rspr
);
205 cmd
->response
[1] = readl(&mci
->rspr1
);
206 cmd
->response
[2] = readl(&mci
->rspr2
);
207 cmd
->response
[3] = readl(&mci
->rspr3
);
209 cmd
->response
[0] = readl(&mci
->rspr
);
211 /* transfer all of the blocks */
213 u32 word_count
, block_count
;
215 u32 sys_blocksize
, dummy
, i
;
217 (atmel_mci_t
*mci
, u32
* data
, u32 error_flags
);
219 if (data
->flags
& MMC_DATA_READ
) {
220 mci_data_op
= mci_data_read
;
221 sys_blocksize
= mmc
->read_bl_len
;
222 ioptr
= (u32
*)data
->dest
;
224 mci_data_op
= mci_data_write
;
225 sys_blocksize
= mmc
->write_bl_len
;
226 ioptr
= (u32
*)data
->src
;
230 for (block_count
= 0;
231 block_count
< data
->blocks
&& !status
;
235 status
= mci_data_op(mci
, ioptr
, error_flags
);
238 } while (!status
&& word_count
< (data
->blocksize
/4));
240 if (data
->flags
& MMC_DATA_READ
)
242 printf("Read Data:\n");
243 print_buffer(0, data
->dest
, 1,
248 if (!status
&& word_count
< (sys_blocksize
/ 4))
249 printf("filling rest of block...\n");
251 /* fill the rest of a full block */
252 while (!status
&& word_count
< (sys_blocksize
/ 4)) {
253 status
= mci_data_op(mci
, &dummy
,
258 dump_cmd(cmdr
, cmd
->cmdarg
, status
,
259 "Data Transfer Failed");
264 /* Wait for Transfer End */
267 status
= readl(&mci
->sr
);
269 if (status
& error_flags
) {
270 dump_cmd(cmdr
, cmd
->cmdarg
, status
,
275 } while ((status
& MMCI_BIT(DTIP
)) && i
< 10000);
276 if (status
& MMCI_BIT(DTIP
)) {
277 dump_cmd(cmdr
, cmd
->cmdarg
, status
,
278 "XFER DTIP never unset, ignoring");
285 /* Entered into mmc structure during driver init */
286 static void mci_set_ios(struct mmc
*mmc
)
288 atmel_mci_t
*mci
= (atmel_mci_t
*)mmc
->priv
;
289 int busw
= (mmc
->bus_width
== 4) ? 1 : 0;
291 /* Set the clock speed */
292 mci_set_mode(mmc
, mmc
->clock
, MMC_DEFAULT_BLKLEN
);
295 * set the bus width and select slot for this interface
296 * there is no capability for multiple slots on the same interface yet
297 * Bitfield SCDBUS needs to be expanded to 2 bits for 8-bit buses
299 writel(MMCI_BF(SCDBUS
, busw
) | MMCI_BF(SCDSEL
, MCI_BUS
), &mci
->sdcr
);
302 /* Entered into mmc structure during driver init */
303 static int mci_init(struct mmc
*mmc
)
305 atmel_mci_t
*mci
= (atmel_mci_t
*)mmc
->priv
;
307 /* Initialize controller */
308 writel(MMCI_BIT(SWRST
), &mci
->cr
); /* soft reset */
309 writel(MMCI_BIT(PWSDIS
), &mci
->cr
); /* disable power save */
310 writel(MMCI_BIT(MCIEN
), &mci
->cr
); /* enable mci */
311 writel(MMCI_BF(SCDSEL
, MCI_BUS
), &mci
->sdcr
); /* select port */
313 /* Initial Time-outs */
314 writel(0x5f, &mci
->dtor
);
315 /* Disable Interrupts */
316 writel(~0UL, &mci
->idr
);
318 /* Set default clocks and blocklen */
319 mci_set_mode(mmc
, CONFIG_SYS_MMC_CLK_OD
, MMC_DEFAULT_BLKLEN
);
325 * This is the only exported function
327 * Call it with the MCI register base address
329 int atmel_mci_init(void *regs
)
331 struct mmc
*mmc
= malloc(sizeof(struct mmc
));
335 strcpy(mmc
->name
, "mci");
337 mmc
->send_cmd
= mci_send_cmd
;
338 mmc
->set_ios
= mci_set_ios
;
339 mmc
->init
= mci_init
;
341 /* need to be able to pass these in on a board by board basis */
342 mmc
->voltages
= MMC_VDD_32_33
| MMC_VDD_33_34
;
343 mmc
->host_caps
= MMC_MODE_4BIT
;
345 * min and max frequencies determined by
346 * max and min of clock divider
348 mmc
->f_min
= get_mci_clk_rate() / (2*256);
349 mmc
->f_max
= get_mci_clk_rate() / (2*1);