xref: /openbmc/u-boot/drivers/mmc/mmc.c (revision e85649c7e683faea1ccfddc9fa9abc62f38e4201)
1272cc70bSAndy Fleming /*
2272cc70bSAndy Fleming  * Copyright 2008, Freescale Semiconductor, Inc
3272cc70bSAndy Fleming  * Andy Fleming
4272cc70bSAndy Fleming  *
5272cc70bSAndy Fleming  * Based vaguely on the Linux code
6272cc70bSAndy Fleming  *
7272cc70bSAndy Fleming  * See file CREDITS for list of people who contributed to this
8272cc70bSAndy Fleming  * project.
9272cc70bSAndy Fleming  *
10272cc70bSAndy Fleming  * This program is free software; you can redistribute it and/or
11272cc70bSAndy Fleming  * modify it under the terms of the GNU General Public License as
12272cc70bSAndy Fleming  * published by the Free Software Foundation; either version 2 of
13272cc70bSAndy Fleming  * the License, or (at your option) any later version.
14272cc70bSAndy Fleming  *
15272cc70bSAndy Fleming  * This program is distributed in the hope that it will be useful,
16272cc70bSAndy Fleming  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17272cc70bSAndy Fleming  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18272cc70bSAndy Fleming  * GNU General Public License for more details.
19272cc70bSAndy Fleming  *
20272cc70bSAndy Fleming  * You should have received a copy of the GNU General Public License
21272cc70bSAndy Fleming  * along with this program; if not, write to the Free Software
22272cc70bSAndy Fleming  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23272cc70bSAndy Fleming  * MA 02111-1307 USA
24272cc70bSAndy Fleming  */
25272cc70bSAndy Fleming 
26272cc70bSAndy Fleming #include <config.h>
27272cc70bSAndy Fleming #include <common.h>
28272cc70bSAndy Fleming #include <command.h>
29272cc70bSAndy Fleming #include <mmc.h>
30272cc70bSAndy Fleming #include <part.h>
31272cc70bSAndy Fleming #include <malloc.h>
32272cc70bSAndy Fleming #include <linux/list.h>
33272cc70bSAndy Fleming #include <mmc.h>
34272cc70bSAndy Fleming 
35272cc70bSAndy Fleming static struct list_head mmc_devices;
36272cc70bSAndy Fleming static int cur_dev_num = -1;
37272cc70bSAndy Fleming 
38272cc70bSAndy Fleming int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
39272cc70bSAndy Fleming {
40272cc70bSAndy Fleming 	return mmc->send_cmd(mmc, cmd, data);
41272cc70bSAndy Fleming }
42272cc70bSAndy Fleming 
43272cc70bSAndy Fleming int mmc_set_blocklen(struct mmc *mmc, int len)
44272cc70bSAndy Fleming {
45272cc70bSAndy Fleming 	struct mmc_cmd cmd;
46272cc70bSAndy Fleming 
47272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SET_BLOCKLEN;
48272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
49272cc70bSAndy Fleming 	cmd.cmdarg = len;
50272cc70bSAndy Fleming 	cmd.flags = 0;
51272cc70bSAndy Fleming 
52272cc70bSAndy Fleming 	return mmc_send_cmd(mmc, &cmd, NULL);
53272cc70bSAndy Fleming }
54272cc70bSAndy Fleming 
55272cc70bSAndy Fleming struct mmc *find_mmc_device(int dev_num)
56272cc70bSAndy Fleming {
57272cc70bSAndy Fleming 	struct mmc *m;
58272cc70bSAndy Fleming 	struct list_head *entry;
59272cc70bSAndy Fleming 
60272cc70bSAndy Fleming 	list_for_each(entry, &mmc_devices) {
61272cc70bSAndy Fleming 		m = list_entry(entry, struct mmc, link);
62272cc70bSAndy Fleming 
63272cc70bSAndy Fleming 		if (m->block_dev.dev == dev_num)
64272cc70bSAndy Fleming 			return m;
65272cc70bSAndy Fleming 	}
66272cc70bSAndy Fleming 
67272cc70bSAndy Fleming 	printf("MMC Device %d not found\n", dev_num);
68272cc70bSAndy Fleming 
69272cc70bSAndy Fleming 	return NULL;
70272cc70bSAndy Fleming }
71272cc70bSAndy Fleming 
72272cc70bSAndy Fleming static ulong
73272cc70bSAndy Fleming mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src)
74272cc70bSAndy Fleming {
75272cc70bSAndy Fleming 	struct mmc_cmd cmd;
76272cc70bSAndy Fleming 	struct mmc_data data;
77272cc70bSAndy Fleming 	int err;
78272cc70bSAndy Fleming 	int stoperr = 0;
79272cc70bSAndy Fleming 	struct mmc *mmc = find_mmc_device(dev_num);
80272cc70bSAndy Fleming 	int blklen;
81272cc70bSAndy Fleming 
82272cc70bSAndy Fleming 	if (!mmc)
83272cc70bSAndy Fleming 		return -1;
84272cc70bSAndy Fleming 
85272cc70bSAndy Fleming 	blklen = mmc->write_bl_len;
86272cc70bSAndy Fleming 
87272cc70bSAndy Fleming 	err = mmc_set_blocklen(mmc, mmc->write_bl_len);
88272cc70bSAndy Fleming 
89272cc70bSAndy Fleming 	if (err) {
90272cc70bSAndy Fleming 		printf("set write bl len failed\n\r");
91272cc70bSAndy Fleming 		return err;
92272cc70bSAndy Fleming 	}
93272cc70bSAndy Fleming 
94272cc70bSAndy Fleming 	if (blkcnt > 1)
95272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
96272cc70bSAndy Fleming 	else
97272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
98272cc70bSAndy Fleming 
99272cc70bSAndy Fleming 	if (mmc->high_capacity)
100272cc70bSAndy Fleming 		cmd.cmdarg = start;
101272cc70bSAndy Fleming 	else
102272cc70bSAndy Fleming 		cmd.cmdarg = start * blklen;
103272cc70bSAndy Fleming 
104272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
105272cc70bSAndy Fleming 	cmd.flags = 0;
106272cc70bSAndy Fleming 
107272cc70bSAndy Fleming 	data.src = src;
108272cc70bSAndy Fleming 	data.blocks = blkcnt;
109272cc70bSAndy Fleming 	data.blocksize = blklen;
110272cc70bSAndy Fleming 	data.flags = MMC_DATA_WRITE;
111272cc70bSAndy Fleming 
112272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, &data);
113272cc70bSAndy Fleming 
114272cc70bSAndy Fleming 	if (err) {
115272cc70bSAndy Fleming 		printf("mmc write failed\n\r");
116272cc70bSAndy Fleming 		return err;
117272cc70bSAndy Fleming 	}
118272cc70bSAndy Fleming 
119272cc70bSAndy Fleming 	if (blkcnt > 1) {
120272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
121272cc70bSAndy Fleming 		cmd.cmdarg = 0;
122272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R1b;
123272cc70bSAndy Fleming 		cmd.flags = 0;
124272cc70bSAndy Fleming 		stoperr = mmc_send_cmd(mmc, &cmd, NULL);
125272cc70bSAndy Fleming 	}
126272cc70bSAndy Fleming 
127272cc70bSAndy Fleming 	return blkcnt;
128272cc70bSAndy Fleming }
129272cc70bSAndy Fleming 
130272cc70bSAndy Fleming int mmc_read_block(struct mmc *mmc, void *dst, uint blocknum)
131272cc70bSAndy Fleming {
132272cc70bSAndy Fleming 	struct mmc_cmd cmd;
133272cc70bSAndy Fleming 	struct mmc_data data;
134272cc70bSAndy Fleming 
135272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
136272cc70bSAndy Fleming 
137272cc70bSAndy Fleming 	if (mmc->high_capacity)
138272cc70bSAndy Fleming 		cmd.cmdarg = blocknum;
139272cc70bSAndy Fleming 	else
140272cc70bSAndy Fleming 		cmd.cmdarg = blocknum * mmc->read_bl_len;
141272cc70bSAndy Fleming 
142272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
143272cc70bSAndy Fleming 	cmd.flags = 0;
144272cc70bSAndy Fleming 
145272cc70bSAndy Fleming 	data.dest = dst;
146272cc70bSAndy Fleming 	data.blocks = 1;
147272cc70bSAndy Fleming 	data.blocksize = mmc->read_bl_len;
148272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
149272cc70bSAndy Fleming 
150272cc70bSAndy Fleming 	return mmc_send_cmd(mmc, &cmd, &data);
151272cc70bSAndy Fleming }
152272cc70bSAndy Fleming 
153272cc70bSAndy Fleming int mmc_read(struct mmc *mmc, u64 src, uchar *dst, int size)
154272cc70bSAndy Fleming {
155272cc70bSAndy Fleming 	char *buffer;
156272cc70bSAndy Fleming 	int i;
157272cc70bSAndy Fleming 	int blklen = mmc->read_bl_len;
158272cc70bSAndy Fleming 	int startblock = src / blklen;
159272cc70bSAndy Fleming 	int endblock = (src + size - 1) / blklen;
160272cc70bSAndy Fleming 	int err = 0;
161272cc70bSAndy Fleming 
162272cc70bSAndy Fleming 	/* Make a buffer big enough to hold all the blocks we might read */
163272cc70bSAndy Fleming 	buffer = malloc(blklen);
164272cc70bSAndy Fleming 
165272cc70bSAndy Fleming 	if (!buffer) {
166272cc70bSAndy Fleming 		printf("Could not allocate buffer for MMC read!\n");
167272cc70bSAndy Fleming 		return -1;
168272cc70bSAndy Fleming 	}
169272cc70bSAndy Fleming 
170272cc70bSAndy Fleming 	/* We always do full block reads from the card */
171272cc70bSAndy Fleming 	err = mmc_set_blocklen(mmc, mmc->read_bl_len);
172272cc70bSAndy Fleming 
173272cc70bSAndy Fleming 	if (err)
174272cc70bSAndy Fleming 		return err;
175272cc70bSAndy Fleming 
176272cc70bSAndy Fleming 	for (i = startblock; i <= endblock; i++) {
177272cc70bSAndy Fleming 		int segment_size;
178272cc70bSAndy Fleming 		int offset;
179272cc70bSAndy Fleming 
180272cc70bSAndy Fleming 		err = mmc_read_block(mmc, buffer, i);
181272cc70bSAndy Fleming 
182272cc70bSAndy Fleming 		if (err)
183272cc70bSAndy Fleming 			goto free_buffer;
184272cc70bSAndy Fleming 
185272cc70bSAndy Fleming 		/*
186272cc70bSAndy Fleming 		 * The first block may not be aligned, so we
187272cc70bSAndy Fleming 		 * copy from the desired point in the block
188272cc70bSAndy Fleming 		 */
189272cc70bSAndy Fleming 		offset = (src & (blklen - 1));
190272cc70bSAndy Fleming 		segment_size = MIN(blklen - offset, size);
191272cc70bSAndy Fleming 
192272cc70bSAndy Fleming 		memcpy(dst, buffer + offset, segment_size);
193272cc70bSAndy Fleming 
194272cc70bSAndy Fleming 		dst += segment_size;
195272cc70bSAndy Fleming 		src += segment_size;
196272cc70bSAndy Fleming 		size -= segment_size;
197272cc70bSAndy Fleming 	}
198272cc70bSAndy Fleming 
199272cc70bSAndy Fleming free_buffer:
200272cc70bSAndy Fleming 	free(buffer);
201272cc70bSAndy Fleming 
202272cc70bSAndy Fleming 	return err;
203272cc70bSAndy Fleming }
204272cc70bSAndy Fleming 
205272cc70bSAndy Fleming static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst)
206272cc70bSAndy Fleming {
207272cc70bSAndy Fleming 	int err;
208272cc70bSAndy Fleming 	int i;
209272cc70bSAndy Fleming 	struct mmc *mmc = find_mmc_device(dev_num);
210272cc70bSAndy Fleming 
211272cc70bSAndy Fleming 	if (!mmc)
212272cc70bSAndy Fleming 		return 0;
213272cc70bSAndy Fleming 
214272cc70bSAndy Fleming 	/* We always do full block reads from the card */
215272cc70bSAndy Fleming 	err = mmc_set_blocklen(mmc, mmc->read_bl_len);
216272cc70bSAndy Fleming 
217272cc70bSAndy Fleming 	if (err) {
218272cc70bSAndy Fleming 		return 0;
219272cc70bSAndy Fleming 	}
220272cc70bSAndy Fleming 
221272cc70bSAndy Fleming 	for (i = start; i < start + blkcnt; i++, dst += mmc->read_bl_len) {
222272cc70bSAndy Fleming 		err = mmc_read_block(mmc, dst, i);
223272cc70bSAndy Fleming 
224272cc70bSAndy Fleming 		if (err) {
225272cc70bSAndy Fleming 			printf("block read failed: %d\n", err);
226272cc70bSAndy Fleming 			return i - start;
227272cc70bSAndy Fleming 		}
228272cc70bSAndy Fleming 	}
229272cc70bSAndy Fleming 
230272cc70bSAndy Fleming 	return blkcnt;
231272cc70bSAndy Fleming }
232272cc70bSAndy Fleming 
233272cc70bSAndy Fleming int mmc_go_idle(struct mmc* mmc)
234272cc70bSAndy Fleming {
235272cc70bSAndy Fleming 	struct mmc_cmd cmd;
236272cc70bSAndy Fleming 	int err;
237272cc70bSAndy Fleming 
238272cc70bSAndy Fleming 	udelay(1000);
239272cc70bSAndy Fleming 
240272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_GO_IDLE_STATE;
241272cc70bSAndy Fleming 	cmd.cmdarg = 0;
242272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_NONE;
243272cc70bSAndy Fleming 	cmd.flags = 0;
244272cc70bSAndy Fleming 
245272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
246272cc70bSAndy Fleming 
247272cc70bSAndy Fleming 	if (err)
248272cc70bSAndy Fleming 		return err;
249272cc70bSAndy Fleming 
250272cc70bSAndy Fleming 	udelay(2000);
251272cc70bSAndy Fleming 
252272cc70bSAndy Fleming 	return 0;
253272cc70bSAndy Fleming }
254272cc70bSAndy Fleming 
255272cc70bSAndy Fleming int
256272cc70bSAndy Fleming sd_send_op_cond(struct mmc *mmc)
257272cc70bSAndy Fleming {
258272cc70bSAndy Fleming 	int timeout = 1000;
259272cc70bSAndy Fleming 	int err;
260272cc70bSAndy Fleming 	struct mmc_cmd cmd;
261272cc70bSAndy Fleming 
262272cc70bSAndy Fleming 	do {
263272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_APP_CMD;
264272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R1;
265272cc70bSAndy Fleming 		cmd.cmdarg = 0;
266272cc70bSAndy Fleming 		cmd.flags = 0;
267272cc70bSAndy Fleming 
268272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
269272cc70bSAndy Fleming 
270272cc70bSAndy Fleming 		if (err)
271272cc70bSAndy Fleming 			return err;
272272cc70bSAndy Fleming 
273272cc70bSAndy Fleming 		cmd.cmdidx = SD_CMD_APP_SEND_OP_COND;
274272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R3;
275272cc70bSAndy Fleming 		cmd.cmdarg = mmc->voltages;
276272cc70bSAndy Fleming 
277272cc70bSAndy Fleming 		if (mmc->version == SD_VERSION_2)
278272cc70bSAndy Fleming 			cmd.cmdarg |= OCR_HCS;
279272cc70bSAndy Fleming 
280272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
281272cc70bSAndy Fleming 
282272cc70bSAndy Fleming 		if (err)
283272cc70bSAndy Fleming 			return err;
284272cc70bSAndy Fleming 
285272cc70bSAndy Fleming 		udelay(1000);
286272cc70bSAndy Fleming 	} while ((!(cmd.response[0] & OCR_BUSY)) && timeout--);
287272cc70bSAndy Fleming 
288272cc70bSAndy Fleming 	if (timeout <= 0)
289272cc70bSAndy Fleming 		return UNUSABLE_ERR;
290272cc70bSAndy Fleming 
291272cc70bSAndy Fleming 	if (mmc->version != SD_VERSION_2)
292272cc70bSAndy Fleming 		mmc->version = SD_VERSION_1_0;
293272cc70bSAndy Fleming 
294272cc70bSAndy Fleming 	mmc->ocr = ((uint *)(cmd.response))[0];
295272cc70bSAndy Fleming 
296272cc70bSAndy Fleming 	mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
297272cc70bSAndy Fleming 	mmc->rca = 0;
298272cc70bSAndy Fleming 
299272cc70bSAndy Fleming 	return 0;
300272cc70bSAndy Fleming }
301272cc70bSAndy Fleming 
302272cc70bSAndy Fleming int mmc_send_op_cond(struct mmc *mmc)
303272cc70bSAndy Fleming {
304272cc70bSAndy Fleming 	int timeout = 1000;
305272cc70bSAndy Fleming 	struct mmc_cmd cmd;
306272cc70bSAndy Fleming 	int err;
307272cc70bSAndy Fleming 
308272cc70bSAndy Fleming 	/* Some cards seem to need this */
309272cc70bSAndy Fleming 	mmc_go_idle(mmc);
310272cc70bSAndy Fleming 
311272cc70bSAndy Fleming 	do {
312272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_SEND_OP_COND;
313272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R3;
314272cc70bSAndy Fleming 		cmd.cmdarg = OCR_HCS | mmc->voltages;
315272cc70bSAndy Fleming 		cmd.flags = 0;
316272cc70bSAndy Fleming 
317272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
318272cc70bSAndy Fleming 
319272cc70bSAndy Fleming 		if (err)
320272cc70bSAndy Fleming 			return err;
321272cc70bSAndy Fleming 
322272cc70bSAndy Fleming 		udelay(1000);
323272cc70bSAndy Fleming 	} while (!(cmd.response[0] & OCR_BUSY) && timeout--);
324272cc70bSAndy Fleming 
325272cc70bSAndy Fleming 	if (timeout <= 0)
326272cc70bSAndy Fleming 		return UNUSABLE_ERR;
327272cc70bSAndy Fleming 
328272cc70bSAndy Fleming 	mmc->version = MMC_VERSION_UNKNOWN;
329272cc70bSAndy Fleming 	mmc->ocr = ((uint *)(cmd.response))[0];
330272cc70bSAndy Fleming 
331272cc70bSAndy Fleming 	mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
332272cc70bSAndy Fleming 	mmc->rca = 0;
333272cc70bSAndy Fleming 
334272cc70bSAndy Fleming 	return 0;
335272cc70bSAndy Fleming }
336272cc70bSAndy Fleming 
337272cc70bSAndy Fleming 
338272cc70bSAndy Fleming int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd)
339272cc70bSAndy Fleming {
340272cc70bSAndy Fleming 	struct mmc_cmd cmd;
341272cc70bSAndy Fleming 	struct mmc_data data;
342272cc70bSAndy Fleming 	int err;
343272cc70bSAndy Fleming 
344272cc70bSAndy Fleming 	/* Get the Card Status Register */
345272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SEND_EXT_CSD;
346272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
347272cc70bSAndy Fleming 	cmd.cmdarg = 0;
348272cc70bSAndy Fleming 	cmd.flags = 0;
349272cc70bSAndy Fleming 
350272cc70bSAndy Fleming 	data.dest = ext_csd;
351272cc70bSAndy Fleming 	data.blocks = 1;
352272cc70bSAndy Fleming 	data.blocksize = 512;
353272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
354272cc70bSAndy Fleming 
355272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, &data);
356272cc70bSAndy Fleming 
357272cc70bSAndy Fleming 	return err;
358272cc70bSAndy Fleming }
359272cc70bSAndy Fleming 
360272cc70bSAndy Fleming 
361272cc70bSAndy Fleming int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
362272cc70bSAndy Fleming {
363272cc70bSAndy Fleming 	struct mmc_cmd cmd;
364272cc70bSAndy Fleming 
365272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SWITCH;
366272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1b;
367272cc70bSAndy Fleming 	cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
368272cc70bSAndy Fleming 		(index << 16) |
369272cc70bSAndy Fleming 		(value << 8);
370272cc70bSAndy Fleming 	cmd.flags = 0;
371272cc70bSAndy Fleming 
372272cc70bSAndy Fleming 	return mmc_send_cmd(mmc, &cmd, NULL);
373272cc70bSAndy Fleming }
374272cc70bSAndy Fleming 
375272cc70bSAndy Fleming int mmc_change_freq(struct mmc *mmc)
376272cc70bSAndy Fleming {
377272cc70bSAndy Fleming 	char ext_csd[512];
378272cc70bSAndy Fleming 	char cardtype;
379272cc70bSAndy Fleming 	int err;
380272cc70bSAndy Fleming 
381272cc70bSAndy Fleming 	mmc->card_caps = 0;
382272cc70bSAndy Fleming 
383272cc70bSAndy Fleming 	/* Only version 4 supports high-speed */
384272cc70bSAndy Fleming 	if (mmc->version < MMC_VERSION_4)
385272cc70bSAndy Fleming 		return 0;
386272cc70bSAndy Fleming 
387272cc70bSAndy Fleming 	mmc->card_caps |= MMC_MODE_4BIT;
388272cc70bSAndy Fleming 
389272cc70bSAndy Fleming 	err = mmc_send_ext_csd(mmc, ext_csd);
390272cc70bSAndy Fleming 
391272cc70bSAndy Fleming 	if (err)
392272cc70bSAndy Fleming 		return err;
393272cc70bSAndy Fleming 
394272cc70bSAndy Fleming 	if (ext_csd[212] || ext_csd[213] || ext_csd[214] || ext_csd[215])
395272cc70bSAndy Fleming 		mmc->high_capacity = 1;
396272cc70bSAndy Fleming 
397272cc70bSAndy Fleming 	cardtype = ext_csd[196] & 0xf;
398272cc70bSAndy Fleming 
399272cc70bSAndy Fleming 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
400272cc70bSAndy Fleming 
401272cc70bSAndy Fleming 	if (err)
402272cc70bSAndy Fleming 		return err;
403272cc70bSAndy Fleming 
404272cc70bSAndy Fleming 	/* Now check to see that it worked */
405272cc70bSAndy Fleming 	err = mmc_send_ext_csd(mmc, ext_csd);
406272cc70bSAndy Fleming 
407272cc70bSAndy Fleming 	if (err)
408272cc70bSAndy Fleming 		return err;
409272cc70bSAndy Fleming 
410272cc70bSAndy Fleming 	/* No high-speed support */
411272cc70bSAndy Fleming 	if (!ext_csd[185])
412272cc70bSAndy Fleming 		return 0;
413272cc70bSAndy Fleming 
414272cc70bSAndy Fleming 	/* High Speed is set, there are two types: 52MHz and 26MHz */
415272cc70bSAndy Fleming 	if (cardtype & MMC_HS_52MHZ)
416272cc70bSAndy Fleming 		mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
417272cc70bSAndy Fleming 	else
418272cc70bSAndy Fleming 		mmc->card_caps |= MMC_MODE_HS;
419272cc70bSAndy Fleming 
420272cc70bSAndy Fleming 	return 0;
421272cc70bSAndy Fleming }
422272cc70bSAndy Fleming 
423272cc70bSAndy Fleming int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
424272cc70bSAndy Fleming {
425272cc70bSAndy Fleming 	struct mmc_cmd cmd;
426272cc70bSAndy Fleming 	struct mmc_data data;
427272cc70bSAndy Fleming 
428272cc70bSAndy Fleming 	/* Switch the frequency */
429272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_SWITCH_FUNC;
430272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
431272cc70bSAndy Fleming 	cmd.cmdarg = (mode << 31) | 0xffffff;
432272cc70bSAndy Fleming 	cmd.cmdarg &= ~(0xf << (group * 4));
433272cc70bSAndy Fleming 	cmd.cmdarg |= value << (group * 4);
434272cc70bSAndy Fleming 	cmd.flags = 0;
435272cc70bSAndy Fleming 
436272cc70bSAndy Fleming 	data.dest = (char *)resp;
437272cc70bSAndy Fleming 	data.blocksize = 64;
438272cc70bSAndy Fleming 	data.blocks = 1;
439272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
440272cc70bSAndy Fleming 
441272cc70bSAndy Fleming 	return mmc_send_cmd(mmc, &cmd, &data);
442272cc70bSAndy Fleming }
443272cc70bSAndy Fleming 
444272cc70bSAndy Fleming 
445272cc70bSAndy Fleming int sd_change_freq(struct mmc *mmc)
446272cc70bSAndy Fleming {
447272cc70bSAndy Fleming 	int err;
448272cc70bSAndy Fleming 	struct mmc_cmd cmd;
449272cc70bSAndy Fleming 	uint scr[2];
450272cc70bSAndy Fleming 	uint switch_status[16];
451272cc70bSAndy Fleming 	struct mmc_data data;
452272cc70bSAndy Fleming 	int timeout;
453272cc70bSAndy Fleming 
454272cc70bSAndy Fleming 	mmc->card_caps = 0;
455272cc70bSAndy Fleming 
456272cc70bSAndy Fleming 	/* Read the SCR to find out if this card supports higher speeds */
457272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_APP_CMD;
458272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
459272cc70bSAndy Fleming 	cmd.cmdarg = mmc->rca << 16;
460272cc70bSAndy Fleming 	cmd.flags = 0;
461272cc70bSAndy Fleming 
462272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
463272cc70bSAndy Fleming 
464272cc70bSAndy Fleming 	if (err)
465272cc70bSAndy Fleming 		return err;
466272cc70bSAndy Fleming 
467272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_APP_SEND_SCR;
468272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
469272cc70bSAndy Fleming 	cmd.cmdarg = 0;
470272cc70bSAndy Fleming 	cmd.flags = 0;
471272cc70bSAndy Fleming 
472272cc70bSAndy Fleming 	timeout = 3;
473272cc70bSAndy Fleming 
474272cc70bSAndy Fleming retry_scr:
475272cc70bSAndy Fleming 	data.dest = (char *)&scr;
476272cc70bSAndy Fleming 	data.blocksize = 8;
477272cc70bSAndy Fleming 	data.blocks = 1;
478272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
479272cc70bSAndy Fleming 
480272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, &data);
481272cc70bSAndy Fleming 
482272cc70bSAndy Fleming 	if (err) {
483272cc70bSAndy Fleming 		if (timeout--)
484272cc70bSAndy Fleming 			goto retry_scr;
485272cc70bSAndy Fleming 
486272cc70bSAndy Fleming 		return err;
487272cc70bSAndy Fleming 	}
488272cc70bSAndy Fleming 
489272cc70bSAndy Fleming 	mmc->scr[0] = scr[0];
490272cc70bSAndy Fleming 	mmc->scr[1] = scr[1];
491272cc70bSAndy Fleming 
492272cc70bSAndy Fleming 	switch ((mmc->scr[0] >> 24) & 0xf) {
493272cc70bSAndy Fleming 		case 0:
494272cc70bSAndy Fleming 			mmc->version = SD_VERSION_1_0;
495272cc70bSAndy Fleming 			break;
496272cc70bSAndy Fleming 		case 1:
497272cc70bSAndy Fleming 			mmc->version = SD_VERSION_1_10;
498272cc70bSAndy Fleming 			break;
499272cc70bSAndy Fleming 		case 2:
500272cc70bSAndy Fleming 			mmc->version = SD_VERSION_2;
501272cc70bSAndy Fleming 			break;
502272cc70bSAndy Fleming 		default:
503272cc70bSAndy Fleming 			mmc->version = SD_VERSION_1_0;
504272cc70bSAndy Fleming 			break;
505272cc70bSAndy Fleming 	}
506272cc70bSAndy Fleming 
507272cc70bSAndy Fleming 	/* Version 1.0 doesn't support switching */
508272cc70bSAndy Fleming 	if (mmc->version == SD_VERSION_1_0)
509272cc70bSAndy Fleming 		return 0;
510272cc70bSAndy Fleming 
511272cc70bSAndy Fleming 	timeout = 4;
512272cc70bSAndy Fleming 	while (timeout--) {
513272cc70bSAndy Fleming 		err = sd_switch(mmc, SD_SWITCH_CHECK, 0, 1,
514272cc70bSAndy Fleming 				(u8 *)&switch_status);
515272cc70bSAndy Fleming 
516272cc70bSAndy Fleming 		if (err)
517272cc70bSAndy Fleming 			return err;
518272cc70bSAndy Fleming 
519272cc70bSAndy Fleming 		/* The high-speed function is busy.  Try again */
520272cc70bSAndy Fleming 		if (!switch_status[7] & SD_HIGHSPEED_BUSY)
521272cc70bSAndy Fleming 			break;
522272cc70bSAndy Fleming 	}
523272cc70bSAndy Fleming 
524272cc70bSAndy Fleming 	if (mmc->scr[0] & SD_DATA_4BIT)
525272cc70bSAndy Fleming 		mmc->card_caps |= MMC_MODE_4BIT;
526272cc70bSAndy Fleming 
527272cc70bSAndy Fleming 	/* If high-speed isn't supported, we return */
528272cc70bSAndy Fleming 	if (!(switch_status[3] & SD_HIGHSPEED_SUPPORTED))
529272cc70bSAndy Fleming 		return 0;
530272cc70bSAndy Fleming 
531272cc70bSAndy Fleming 	err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)&switch_status);
532272cc70bSAndy Fleming 
533272cc70bSAndy Fleming 	if (err)
534272cc70bSAndy Fleming 		return err;
535272cc70bSAndy Fleming 
536272cc70bSAndy Fleming 	if ((switch_status[4] & 0x0f000000) == 0x01000000)
537272cc70bSAndy Fleming 		mmc->card_caps |= MMC_MODE_HS;
538272cc70bSAndy Fleming 
539272cc70bSAndy Fleming 	return 0;
540272cc70bSAndy Fleming }
541272cc70bSAndy Fleming 
542272cc70bSAndy Fleming /* frequency bases */
543272cc70bSAndy Fleming /* divided by 10 to be nice to platforms without floating point */
544272cc70bSAndy Fleming int fbase[] = {
545272cc70bSAndy Fleming 	10000,
546272cc70bSAndy Fleming 	100000,
547272cc70bSAndy Fleming 	1000000,
548272cc70bSAndy Fleming 	10000000,
549272cc70bSAndy Fleming };
550272cc70bSAndy Fleming 
551272cc70bSAndy Fleming /* Multiplier values for TRAN_SPEED.  Multiplied by 10 to be nice
552272cc70bSAndy Fleming  * to platforms without floating point.
553272cc70bSAndy Fleming  */
554272cc70bSAndy Fleming int multipliers[] = {
555272cc70bSAndy Fleming 	0,	/* reserved */
556272cc70bSAndy Fleming 	10,
557272cc70bSAndy Fleming 	12,
558272cc70bSAndy Fleming 	13,
559272cc70bSAndy Fleming 	15,
560272cc70bSAndy Fleming 	20,
561272cc70bSAndy Fleming 	25,
562272cc70bSAndy Fleming 	30,
563272cc70bSAndy Fleming 	35,
564272cc70bSAndy Fleming 	40,
565272cc70bSAndy Fleming 	45,
566272cc70bSAndy Fleming 	50,
567272cc70bSAndy Fleming 	55,
568272cc70bSAndy Fleming 	60,
569272cc70bSAndy Fleming 	70,
570272cc70bSAndy Fleming 	80,
571272cc70bSAndy Fleming };
572272cc70bSAndy Fleming 
573272cc70bSAndy Fleming void mmc_set_ios(struct mmc *mmc)
574272cc70bSAndy Fleming {
575272cc70bSAndy Fleming 	mmc->set_ios(mmc);
576272cc70bSAndy Fleming }
577272cc70bSAndy Fleming 
578272cc70bSAndy Fleming void mmc_set_clock(struct mmc *mmc, uint clock)
579272cc70bSAndy Fleming {
580272cc70bSAndy Fleming 	if (clock > mmc->f_max)
581272cc70bSAndy Fleming 		clock = mmc->f_max;
582272cc70bSAndy Fleming 
583272cc70bSAndy Fleming 	if (clock < mmc->f_min)
584272cc70bSAndy Fleming 		clock = mmc->f_min;
585272cc70bSAndy Fleming 
586272cc70bSAndy Fleming 	mmc->clock = clock;
587272cc70bSAndy Fleming 
588272cc70bSAndy Fleming 	mmc_set_ios(mmc);
589272cc70bSAndy Fleming }
590272cc70bSAndy Fleming 
591272cc70bSAndy Fleming void mmc_set_bus_width(struct mmc *mmc, uint width)
592272cc70bSAndy Fleming {
593272cc70bSAndy Fleming 	mmc->bus_width = width;
594272cc70bSAndy Fleming 
595272cc70bSAndy Fleming 	mmc_set_ios(mmc);
596272cc70bSAndy Fleming }
597272cc70bSAndy Fleming 
598272cc70bSAndy Fleming int mmc_startup(struct mmc *mmc)
599272cc70bSAndy Fleming {
600272cc70bSAndy Fleming 	int err;
601272cc70bSAndy Fleming 	uint mult, freq;
602272cc70bSAndy Fleming 	u64 cmult, csize;
603272cc70bSAndy Fleming 	struct mmc_cmd cmd;
604272cc70bSAndy Fleming 
605272cc70bSAndy Fleming 	/* Put the Card in Identify Mode */
606272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_ALL_SEND_CID;
607272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R2;
608272cc70bSAndy Fleming 	cmd.cmdarg = 0;
609272cc70bSAndy Fleming 	cmd.flags = 0;
610272cc70bSAndy Fleming 
611272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
612272cc70bSAndy Fleming 
613272cc70bSAndy Fleming 	if (err)
614272cc70bSAndy Fleming 		return err;
615272cc70bSAndy Fleming 
616272cc70bSAndy Fleming 	memcpy(mmc->cid, cmd.response, 16);
617272cc70bSAndy Fleming 
618272cc70bSAndy Fleming 	/*
619272cc70bSAndy Fleming 	 * For MMC cards, set the Relative Address.
620272cc70bSAndy Fleming 	 * For SD cards, get the Relatvie Address.
621272cc70bSAndy Fleming 	 * This also puts the cards into Standby State
622272cc70bSAndy Fleming 	 */
623272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;
624272cc70bSAndy Fleming 	cmd.cmdarg = mmc->rca << 16;
625272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R6;
626272cc70bSAndy Fleming 	cmd.flags = 0;
627272cc70bSAndy Fleming 
628272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
629272cc70bSAndy Fleming 
630272cc70bSAndy Fleming 	if (err)
631272cc70bSAndy Fleming 		return err;
632272cc70bSAndy Fleming 
633272cc70bSAndy Fleming 	if (IS_SD(mmc))
634272cc70bSAndy Fleming 		mmc->rca = (((uint *)(cmd.response))[0] >> 16) & 0xffff;
635272cc70bSAndy Fleming 
636272cc70bSAndy Fleming 	/* Get the Card-Specific Data */
637272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SEND_CSD;
638272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R2;
639272cc70bSAndy Fleming 	cmd.cmdarg = mmc->rca << 16;
640272cc70bSAndy Fleming 	cmd.flags = 0;
641272cc70bSAndy Fleming 
642272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
643272cc70bSAndy Fleming 
644272cc70bSAndy Fleming 	if (err)
645272cc70bSAndy Fleming 		return err;
646272cc70bSAndy Fleming 
647272cc70bSAndy Fleming 	mmc->csd[0] = ((uint *)(cmd.response))[0];
648272cc70bSAndy Fleming 	mmc->csd[1] = ((uint *)(cmd.response))[1];
649272cc70bSAndy Fleming 	mmc->csd[2] = ((uint *)(cmd.response))[2];
650272cc70bSAndy Fleming 	mmc->csd[3] = ((uint *)(cmd.response))[3];
651272cc70bSAndy Fleming 
652272cc70bSAndy Fleming 	if (mmc->version == MMC_VERSION_UNKNOWN) {
653272cc70bSAndy Fleming 		int version = (cmd.response[0] >> 2) & 0xf;
654272cc70bSAndy Fleming 
655272cc70bSAndy Fleming 		switch (version) {
656272cc70bSAndy Fleming 			case 0:
657272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_1_2;
658272cc70bSAndy Fleming 				break;
659272cc70bSAndy Fleming 			case 1:
660272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_1_4;
661272cc70bSAndy Fleming 				break;
662272cc70bSAndy Fleming 			case 2:
663272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_2_2;
664272cc70bSAndy Fleming 				break;
665272cc70bSAndy Fleming 			case 3:
666272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_3;
667272cc70bSAndy Fleming 				break;
668272cc70bSAndy Fleming 			case 4:
669272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_4;
670272cc70bSAndy Fleming 				break;
671272cc70bSAndy Fleming 			default:
672272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_1_2;
673272cc70bSAndy Fleming 				break;
674272cc70bSAndy Fleming 		}
675272cc70bSAndy Fleming 	}
676272cc70bSAndy Fleming 
677272cc70bSAndy Fleming 	/* divide frequency by 10, since the mults are 10x bigger */
678272cc70bSAndy Fleming 	freq = fbase[(cmd.response[3] & 0x7)];
679272cc70bSAndy Fleming 	mult = multipliers[((cmd.response[3] >> 3) & 0xf)];
680272cc70bSAndy Fleming 
681272cc70bSAndy Fleming 	mmc->tran_speed = freq * mult;
682272cc70bSAndy Fleming 
683272cc70bSAndy Fleming 	mmc->read_bl_len = 1 << ((((uint *)(cmd.response))[1] >> 16) & 0xf);
684272cc70bSAndy Fleming 
685272cc70bSAndy Fleming 	if (IS_SD(mmc))
686272cc70bSAndy Fleming 		mmc->write_bl_len = mmc->read_bl_len;
687272cc70bSAndy Fleming 	else
688272cc70bSAndy Fleming 		mmc->write_bl_len = 1 << ((((uint *)(cmd.response))[3] >> 22) & 0xf);
689272cc70bSAndy Fleming 
690272cc70bSAndy Fleming 	if (mmc->high_capacity) {
691272cc70bSAndy Fleming 		csize = (mmc->csd[1] & 0x3f) << 16
692272cc70bSAndy Fleming 			| (mmc->csd[2] & 0xffff0000) >> 16;
693272cc70bSAndy Fleming 		cmult = 8;
694272cc70bSAndy Fleming 	} else {
695272cc70bSAndy Fleming 		csize = (mmc->csd[1] & 0x3ff) << 2
696272cc70bSAndy Fleming 			| (mmc->csd[2] & 0xc0000000) >> 30;
697272cc70bSAndy Fleming 		cmult = (mmc->csd[2] & 0x00038000) >> 15;
698272cc70bSAndy Fleming 	}
699272cc70bSAndy Fleming 
700272cc70bSAndy Fleming 	mmc->capacity = (csize + 1) << (cmult + 2);
701272cc70bSAndy Fleming 	mmc->capacity *= mmc->read_bl_len;
702272cc70bSAndy Fleming 
703272cc70bSAndy Fleming 	if (mmc->read_bl_len > 512)
704272cc70bSAndy Fleming 		mmc->read_bl_len = 512;
705272cc70bSAndy Fleming 
706272cc70bSAndy Fleming 	if (mmc->write_bl_len > 512)
707272cc70bSAndy Fleming 		mmc->write_bl_len = 512;
708272cc70bSAndy Fleming 
709272cc70bSAndy Fleming 	/* Select the card, and put it into Transfer Mode */
710272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SELECT_CARD;
711272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1b;
712272cc70bSAndy Fleming 	cmd.cmdarg = mmc->rca << 16;
713272cc70bSAndy Fleming 	cmd.flags = 0;
714272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
715272cc70bSAndy Fleming 
716272cc70bSAndy Fleming 	if (err)
717272cc70bSAndy Fleming 		return err;
718272cc70bSAndy Fleming 
719272cc70bSAndy Fleming 	if (IS_SD(mmc))
720272cc70bSAndy Fleming 		err = sd_change_freq(mmc);
721272cc70bSAndy Fleming 	else
722272cc70bSAndy Fleming 		err = mmc_change_freq(mmc);
723272cc70bSAndy Fleming 
724272cc70bSAndy Fleming 	if (err)
725272cc70bSAndy Fleming 		return err;
726272cc70bSAndy Fleming 
727272cc70bSAndy Fleming 	/* Restrict card's capabilities by what the host can do */
728272cc70bSAndy Fleming 	mmc->card_caps &= mmc->host_caps;
729272cc70bSAndy Fleming 
730272cc70bSAndy Fleming 	if (IS_SD(mmc)) {
731272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_4BIT) {
732272cc70bSAndy Fleming 			cmd.cmdidx = MMC_CMD_APP_CMD;
733272cc70bSAndy Fleming 			cmd.resp_type = MMC_RSP_R1;
734272cc70bSAndy Fleming 			cmd.cmdarg = mmc->rca << 16;
735272cc70bSAndy Fleming 			cmd.flags = 0;
736272cc70bSAndy Fleming 
737272cc70bSAndy Fleming 			err = mmc_send_cmd(mmc, &cmd, NULL);
738272cc70bSAndy Fleming 			if (err)
739272cc70bSAndy Fleming 				return err;
740272cc70bSAndy Fleming 
741272cc70bSAndy Fleming 			cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH;
742272cc70bSAndy Fleming 			cmd.resp_type = MMC_RSP_R1;
743272cc70bSAndy Fleming 			cmd.cmdarg = 2;
744272cc70bSAndy Fleming 			cmd.flags = 0;
745272cc70bSAndy Fleming 			err = mmc_send_cmd(mmc, &cmd, NULL);
746272cc70bSAndy Fleming 			if (err)
747272cc70bSAndy Fleming 				return err;
748272cc70bSAndy Fleming 
749272cc70bSAndy Fleming 			mmc_set_bus_width(mmc, 4);
750272cc70bSAndy Fleming 		}
751272cc70bSAndy Fleming 
752272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_HS)
753272cc70bSAndy Fleming 			mmc_set_clock(mmc, 50000000);
754272cc70bSAndy Fleming 		else
755272cc70bSAndy Fleming 			mmc_set_clock(mmc, 25000000);
756272cc70bSAndy Fleming 	} else {
757272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_4BIT) {
758272cc70bSAndy Fleming 			/* Set the card to use 4 bit*/
759272cc70bSAndy Fleming 			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
760272cc70bSAndy Fleming 					EXT_CSD_BUS_WIDTH,
761272cc70bSAndy Fleming 					EXT_CSD_BUS_WIDTH_4);
762272cc70bSAndy Fleming 
763272cc70bSAndy Fleming 			if (err)
764272cc70bSAndy Fleming 				return err;
765272cc70bSAndy Fleming 
766272cc70bSAndy Fleming 			mmc_set_bus_width(mmc, 4);
767272cc70bSAndy Fleming 		} else if (mmc->card_caps & MMC_MODE_8BIT) {
768272cc70bSAndy Fleming 			/* Set the card to use 8 bit*/
769272cc70bSAndy Fleming 			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
770272cc70bSAndy Fleming 					EXT_CSD_BUS_WIDTH,
771272cc70bSAndy Fleming 					EXT_CSD_BUS_WIDTH_8);
772272cc70bSAndy Fleming 
773272cc70bSAndy Fleming 			if (err)
774272cc70bSAndy Fleming 				return err;
775272cc70bSAndy Fleming 
776272cc70bSAndy Fleming 			mmc_set_bus_width(mmc, 8);
777272cc70bSAndy Fleming 		}
778272cc70bSAndy Fleming 
779272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_HS) {
780272cc70bSAndy Fleming 			if (mmc->card_caps & MMC_MODE_HS_52MHz)
781272cc70bSAndy Fleming 				mmc_set_clock(mmc, 52000000);
782272cc70bSAndy Fleming 			else
783272cc70bSAndy Fleming 				mmc_set_clock(mmc, 26000000);
784272cc70bSAndy Fleming 		} else
785272cc70bSAndy Fleming 			mmc_set_clock(mmc, 20000000);
786272cc70bSAndy Fleming 	}
787272cc70bSAndy Fleming 
788272cc70bSAndy Fleming 	/* fill in device description */
789272cc70bSAndy Fleming 	mmc->block_dev.lun = 0;
790272cc70bSAndy Fleming 	mmc->block_dev.type = 0;
791272cc70bSAndy Fleming 	mmc->block_dev.blksz = mmc->read_bl_len;
792272cc70bSAndy Fleming 	mmc->block_dev.lba = mmc->capacity/mmc->read_bl_len;
793272cc70bSAndy Fleming 	sprintf(mmc->block_dev.vendor,"Man %02x%02x%02x Snr %02x%02x%02x%02x",
794272cc70bSAndy Fleming 			mmc->cid[0], mmc->cid[1], mmc->cid[2],
795272cc70bSAndy Fleming 			mmc->cid[9], mmc->cid[10], mmc->cid[11], mmc->cid[12]);
796272cc70bSAndy Fleming 	sprintf(mmc->block_dev.product,"%c%c%c%c%c", mmc->cid[3],
797272cc70bSAndy Fleming 			mmc->cid[4], mmc->cid[5], mmc->cid[6], mmc->cid[7]);
798272cc70bSAndy Fleming 	sprintf(mmc->block_dev.revision,"%d.%d", mmc->cid[8] >> 4,
799272cc70bSAndy Fleming 			mmc->cid[8] & 0xf);
800272cc70bSAndy Fleming 	init_part(&mmc->block_dev);
801272cc70bSAndy Fleming 
802272cc70bSAndy Fleming 	return 0;
803272cc70bSAndy Fleming }
804272cc70bSAndy Fleming 
805272cc70bSAndy Fleming int mmc_send_if_cond(struct mmc *mmc)
806272cc70bSAndy Fleming {
807272cc70bSAndy Fleming 	struct mmc_cmd cmd;
808272cc70bSAndy Fleming 	int err;
809272cc70bSAndy Fleming 
810272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_SEND_IF_COND;
811272cc70bSAndy Fleming 	/* We set the bit if the host supports voltages between 2.7 and 3.6 V */
812272cc70bSAndy Fleming 	cmd.cmdarg = ((mmc->voltages & 0xff8000) != 0) << 8 | 0xaa;
813272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R7;
814272cc70bSAndy Fleming 	cmd.flags = 0;
815272cc70bSAndy Fleming 
816272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
817272cc70bSAndy Fleming 
818272cc70bSAndy Fleming 	if (err)
819272cc70bSAndy Fleming 		return err;
820272cc70bSAndy Fleming 
821272cc70bSAndy Fleming 	if ((((uint *)(cmd.response))[0] & 0xff) != 0xaa)
822272cc70bSAndy Fleming 		return UNUSABLE_ERR;
823272cc70bSAndy Fleming 	else
824272cc70bSAndy Fleming 		mmc->version = SD_VERSION_2;
825272cc70bSAndy Fleming 
826272cc70bSAndy Fleming 	return 0;
827272cc70bSAndy Fleming }
828272cc70bSAndy Fleming 
829272cc70bSAndy Fleming int mmc_register(struct mmc *mmc)
830272cc70bSAndy Fleming {
831272cc70bSAndy Fleming 	/* Setup the universal parts of the block interface just once */
832272cc70bSAndy Fleming 	mmc->block_dev.if_type = IF_TYPE_MMC;
833272cc70bSAndy Fleming 	mmc->block_dev.dev = cur_dev_num++;
834272cc70bSAndy Fleming 	mmc->block_dev.removable = 1;
835272cc70bSAndy Fleming 	mmc->block_dev.block_read = mmc_bread;
836272cc70bSAndy Fleming 	mmc->block_dev.block_write = mmc_bwrite;
837272cc70bSAndy Fleming 
838272cc70bSAndy Fleming 	INIT_LIST_HEAD (&mmc->link);
839272cc70bSAndy Fleming 
840272cc70bSAndy Fleming 	list_add_tail (&mmc->link, &mmc_devices);
841272cc70bSAndy Fleming 
842272cc70bSAndy Fleming 	return 0;
843272cc70bSAndy Fleming }
844272cc70bSAndy Fleming 
845272cc70bSAndy Fleming block_dev_desc_t *mmc_get_dev(int dev)
846272cc70bSAndy Fleming {
847272cc70bSAndy Fleming 	struct mmc *mmc = find_mmc_device(dev);
848272cc70bSAndy Fleming 
849*e85649c7SRabin Vincent 	return mmc ? &mmc->block_dev : NULL;
850272cc70bSAndy Fleming }
851272cc70bSAndy Fleming 
852272cc70bSAndy Fleming int mmc_init(struct mmc *mmc)
853272cc70bSAndy Fleming {
854272cc70bSAndy Fleming 	int err;
855272cc70bSAndy Fleming 
856272cc70bSAndy Fleming 	err = mmc->init(mmc);
857272cc70bSAndy Fleming 
858272cc70bSAndy Fleming 	if (err)
859272cc70bSAndy Fleming 		return err;
860272cc70bSAndy Fleming 
861272cc70bSAndy Fleming 	/* Reset the Card */
862272cc70bSAndy Fleming 	err = mmc_go_idle(mmc);
863272cc70bSAndy Fleming 
864272cc70bSAndy Fleming 	if (err)
865272cc70bSAndy Fleming 		return err;
866272cc70bSAndy Fleming 
867272cc70bSAndy Fleming 	/* Test for SD version 2 */
868272cc70bSAndy Fleming 	err = mmc_send_if_cond(mmc);
869272cc70bSAndy Fleming 
870272cc70bSAndy Fleming 	/* If we got an error other than timeout, we bail */
871272cc70bSAndy Fleming 	if (err && err != TIMEOUT)
872272cc70bSAndy Fleming 		return err;
873272cc70bSAndy Fleming 
874272cc70bSAndy Fleming 	/* Now try to get the SD card's operating condition */
875272cc70bSAndy Fleming 	err = sd_send_op_cond(mmc);
876272cc70bSAndy Fleming 
877272cc70bSAndy Fleming 	/* If the command timed out, we check for an MMC card */
878272cc70bSAndy Fleming 	if (err == TIMEOUT) {
879272cc70bSAndy Fleming 		err = mmc_send_op_cond(mmc);
880272cc70bSAndy Fleming 
881272cc70bSAndy Fleming 		if (err) {
882272cc70bSAndy Fleming 			printf("Card did not respond to voltage select!\n");
883272cc70bSAndy Fleming 			return UNUSABLE_ERR;
884272cc70bSAndy Fleming 		}
885272cc70bSAndy Fleming 	}
886272cc70bSAndy Fleming 
887272cc70bSAndy Fleming 	return mmc_startup(mmc);
888272cc70bSAndy Fleming }
889272cc70bSAndy Fleming 
890272cc70bSAndy Fleming /*
891272cc70bSAndy Fleming  * CPU and board-specific MMC initializations.  Aliased function
892272cc70bSAndy Fleming  * signals caller to move on
893272cc70bSAndy Fleming  */
894272cc70bSAndy Fleming static int __def_mmc_init(bd_t *bis)
895272cc70bSAndy Fleming {
896272cc70bSAndy Fleming 	return -1;
897272cc70bSAndy Fleming }
898272cc70bSAndy Fleming 
899f9a109b3SPeter Tyser int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
900f9a109b3SPeter Tyser int board_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
901272cc70bSAndy Fleming 
902272cc70bSAndy Fleming void print_mmc_devices(char separator)
903272cc70bSAndy Fleming {
904272cc70bSAndy Fleming 	struct mmc *m;
905272cc70bSAndy Fleming 	struct list_head *entry;
906272cc70bSAndy Fleming 
907272cc70bSAndy Fleming 	list_for_each(entry, &mmc_devices) {
908272cc70bSAndy Fleming 		m = list_entry(entry, struct mmc, link);
909272cc70bSAndy Fleming 
910272cc70bSAndy Fleming 		printf("%s: %d", m->name, m->block_dev.dev);
911272cc70bSAndy Fleming 
912272cc70bSAndy Fleming 		if (entry->next != &mmc_devices)
913272cc70bSAndy Fleming 			printf("%c ", separator);
914272cc70bSAndy Fleming 	}
915272cc70bSAndy Fleming 
916272cc70bSAndy Fleming 	printf("\n");
917272cc70bSAndy Fleming }
918272cc70bSAndy Fleming 
919272cc70bSAndy Fleming int mmc_initialize(bd_t *bis)
920272cc70bSAndy Fleming {
921272cc70bSAndy Fleming 	INIT_LIST_HEAD (&mmc_devices);
922272cc70bSAndy Fleming 	cur_dev_num = 0;
923272cc70bSAndy Fleming 
924272cc70bSAndy Fleming 	if (board_mmc_init(bis) < 0)
925272cc70bSAndy Fleming 		cpu_mmc_init(bis);
926272cc70bSAndy Fleming 
927272cc70bSAndy Fleming 	print_mmc_devices(',');
928272cc70bSAndy Fleming 
929272cc70bSAndy Fleming 	return 0;
930272cc70bSAndy Fleming }
931