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