Commit d52ebf102209cc1ad460c79b9498b2c8936ba413
Committed by
Andy Fleming
1 parent
5f837c2c0e
Exists in
master
and in
54 other branches
mmc: add generic mmc spi driver
This patch supports mmc/sd card with spi interface. It is based on the generic mmc framework. It works with SDHC and supports multi blocks read/write. The crc checksum on data packet is enabled with the def, There is a subcomamnd "mmc_spi" to setup spi bus and cs at run time. Signed-off-by: Thomas Chou <thomas@wytron.com.tw> Signed-off-by: Andy Fleming <afleming@freescale.com>
Showing 6 changed files with 448 additions and 20 deletions Side-by-side Diff
common/Makefile
... | ... | @@ -118,6 +118,7 @@ |
118 | 118 | COBJS-$(CONFIG_CMD_MII) += cmd_mii.o |
119 | 119 | COBJS-$(CONFIG_CMD_MISC) += cmd_misc.o |
120 | 120 | COBJS-$(CONFIG_CMD_MMC) += cmd_mmc.o |
121 | +COBJS-$(CONFIG_CMD_MMC_SPI) += cmd_mmc_spi.o | |
121 | 122 | COBJS-$(CONFIG_MP) += cmd_mp.o |
122 | 123 | COBJS-$(CONFIG_CMD_MTDPARTS) += cmd_mtdparts.o |
123 | 124 | COBJS-$(CONFIG_CMD_NAND) += cmd_nand.o |
common/cmd_mmc_spi.c
1 | +/* | |
2 | + * Command for mmc_spi setup. | |
3 | + * | |
4 | + * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> | |
5 | + * Licensed under the GPL-2 or later. | |
6 | + */ | |
7 | + | |
8 | +#include <common.h> | |
9 | +#include <mmc.h> | |
10 | +#include <spi.h> | |
11 | + | |
12 | +#ifndef CONFIG_MMC_SPI_BUS | |
13 | +# define CONFIG_MMC_SPI_BUS 0 | |
14 | +#endif | |
15 | +#ifndef CONFIG_MMC_SPI_CS | |
16 | +# define CONFIG_MMC_SPI_CS 1 | |
17 | +#endif | |
18 | +/* in SPI mode, MMC speed limit is 20MHz, while SD speed limit is 25MHz */ | |
19 | +#ifndef CONFIG_MMC_SPI_SPEED | |
20 | +# define CONFIG_MMC_SPI_SPEED 25000000 | |
21 | +#endif | |
22 | +/* MMC and SD specs only seem to care that sampling is on the | |
23 | + * rising edge ... meaning SPI modes 0 or 3. So either SPI mode | |
24 | + * should be legit. We'll use mode 0 since the steady state is 0, | |
25 | + * which is appropriate for hotplugging, unless the platform data | |
26 | + * specify mode 3 (if hardware is not compatible to mode 0). | |
27 | + */ | |
28 | +#ifndef CONFIG_MMC_SPI_MODE | |
29 | +# define CONFIG_MMC_SPI_MODE SPI_MODE_0 | |
30 | +#endif | |
31 | + | |
32 | +static int do_mmc_spi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | |
33 | +{ | |
34 | + uint bus = CONFIG_MMC_SPI_BUS; | |
35 | + uint cs = CONFIG_MMC_SPI_CS; | |
36 | + uint speed = CONFIG_MMC_SPI_SPEED; | |
37 | + uint mode = CONFIG_MMC_SPI_MODE; | |
38 | + char *endp; | |
39 | + struct mmc *mmc; | |
40 | + | |
41 | + if (argc < 2) | |
42 | + goto usage; | |
43 | + | |
44 | + cs = simple_strtoul(argv[1], &endp, 0); | |
45 | + if (*argv[1] == 0 || (*endp != 0 && *endp != ':')) | |
46 | + goto usage; | |
47 | + if (*endp == ':') { | |
48 | + if (endp[1] == 0) | |
49 | + goto usage; | |
50 | + bus = cs; | |
51 | + cs = simple_strtoul(endp + 1, &endp, 0); | |
52 | + if (*endp != 0) | |
53 | + goto usage; | |
54 | + } | |
55 | + if (argc >= 3) { | |
56 | + speed = simple_strtoul(argv[2], &endp, 0); | |
57 | + if (*argv[2] == 0 || *endp != 0) | |
58 | + goto usage; | |
59 | + } | |
60 | + if (argc >= 4) { | |
61 | + mode = simple_strtoul(argv[3], &endp, 16); | |
62 | + if (*argv[3] == 0 || *endp != 0) | |
63 | + goto usage; | |
64 | + } | |
65 | + if (!spi_cs_is_valid(bus, cs)) { | |
66 | + printf("Invalid SPI bus %u cs %u\n", bus, cs); | |
67 | + return 1; | |
68 | + } | |
69 | + | |
70 | + mmc = mmc_spi_init(bus, cs, speed, mode); | |
71 | + if (!mmc) { | |
72 | + printf("Failed to create MMC Device\n"); | |
73 | + return 1; | |
74 | + } | |
75 | + printf("%s: %d at %u:%u hz %u mode %u\n", mmc->name, mmc->block_dev.dev, | |
76 | + bus, cs, speed, mode); | |
77 | + return 0; | |
78 | + | |
79 | +usage: | |
80 | + cmd_usage(cmdtp); | |
81 | + return 1; | |
82 | +} | |
83 | + | |
84 | +U_BOOT_CMD( | |
85 | + mmc_spi, 4, 0, do_mmc_spi, | |
86 | + "mmc_spi setup", | |
87 | + "[bus:]cs [hz] [mode] - setup mmc_spi device" | |
88 | +); |
drivers/mmc/Makefile
... | ... | @@ -31,6 +31,7 @@ |
31 | 31 | COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o |
32 | 32 | COBJS-$(CONFIG_GENERIC_MMC) += mmc.o |
33 | 33 | COBJS-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o |
34 | +COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o | |
34 | 35 | COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o |
35 | 36 | COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o |
36 | 37 | COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o |
drivers/mmc/mmc.c
... | ... | @@ -117,7 +117,10 @@ |
117 | 117 | return 0; |
118 | 118 | } |
119 | 119 | |
120 | - if (blkcnt > 1) { | |
120 | + /* SPI multiblock writes terminate using a special | |
121 | + * token, not a STOP_TRANSMISSION request. | |
122 | + */ | |
123 | + if (!mmc_host_is_spi(mmc) && blkcnt > 1) { | |
121 | 124 | cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; |
122 | 125 | cmd.cmdarg = 0; |
123 | 126 | cmd.resp_type = MMC_RSP_R1b; |
... | ... | @@ -279,7 +282,8 @@ |
279 | 282 | * how to manage low voltages SD card is not yet |
280 | 283 | * specified. |
281 | 284 | */ |
282 | - cmd.cmdarg = mmc->voltages & 0xff8000; | |
285 | + cmd.cmdarg = mmc_host_is_spi(mmc) ? 0 : | |
286 | + (mmc->voltages & 0xff8000); | |
283 | 287 | |
284 | 288 | if (mmc->version == SD_VERSION_2) |
285 | 289 | cmd.cmdarg |= OCR_HCS; |
... | ... | @@ -298,6 +302,18 @@ |
298 | 302 | if (mmc->version != SD_VERSION_2) |
299 | 303 | mmc->version = SD_VERSION_1_0; |
300 | 304 | |
305 | + if (mmc_host_is_spi(mmc)) { /* read OCR for spi */ | |
306 | + cmd.cmdidx = MMC_CMD_SPI_READ_OCR; | |
307 | + cmd.resp_type = MMC_RSP_R3; | |
308 | + cmd.cmdarg = 0; | |
309 | + cmd.flags = 0; | |
310 | + | |
311 | + err = mmc_send_cmd(mmc, &cmd, NULL); | |
312 | + | |
313 | + if (err) | |
314 | + return err; | |
315 | + } | |
316 | + | |
301 | 317 | mmc->ocr = cmd.response[0]; |
302 | 318 | |
303 | 319 | mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); |
... | ... | @@ -318,7 +334,8 @@ |
318 | 334 | do { |
319 | 335 | cmd.cmdidx = MMC_CMD_SEND_OP_COND; |
320 | 336 | cmd.resp_type = MMC_RSP_R3; |
321 | - cmd.cmdarg = OCR_HCS | mmc->voltages; | |
337 | + cmd.cmdarg = OCR_HCS | (mmc_host_is_spi(mmc) ? 0 : | |
338 | + mmc->voltages); | |
322 | 339 | cmd.flags = 0; |
323 | 340 | |
324 | 341 | err = mmc_send_cmd(mmc, &cmd, NULL); |
... | ... | @@ -332,6 +349,18 @@ |
332 | 349 | if (timeout <= 0) |
333 | 350 | return UNUSABLE_ERR; |
334 | 351 | |
352 | + if (mmc_host_is_spi(mmc)) { /* read OCR for spi */ | |
353 | + cmd.cmdidx = MMC_CMD_SPI_READ_OCR; | |
354 | + cmd.resp_type = MMC_RSP_R3; | |
355 | + cmd.cmdarg = 0; | |
356 | + cmd.flags = 0; | |
357 | + | |
358 | + err = mmc_send_cmd(mmc, &cmd, NULL); | |
359 | + | |
360 | + if (err) | |
361 | + return err; | |
362 | + } | |
363 | + | |
335 | 364 | mmc->version = MMC_VERSION_UNKNOWN; |
336 | 365 | mmc->ocr = cmd.response[0]; |
337 | 366 | |
... | ... | @@ -387,6 +416,9 @@ |
387 | 416 | |
388 | 417 | mmc->card_caps = 0; |
389 | 418 | |
419 | + if (mmc_host_is_spi(mmc)) | |
420 | + return 0; | |
421 | + | |
390 | 422 | /* Only version 4 supports high-speed */ |
391 | 423 | if (mmc->version < MMC_VERSION_4) |
392 | 424 | return 0; |
... | ... | @@ -460,6 +492,9 @@ |
460 | 492 | |
461 | 493 | mmc->card_caps = 0; |
462 | 494 | |
495 | + if (mmc_host_is_spi(mmc)) | |
496 | + return 0; | |
497 | + | |
463 | 498 | /* Read the SCR to find out if this card supports higher speeds */ |
464 | 499 | cmd.cmdidx = MMC_CMD_APP_CMD; |
465 | 500 | cmd.resp_type = MMC_RSP_R1; |
466 | 501 | |
... | ... | @@ -610,8 +645,22 @@ |
610 | 645 | struct mmc_cmd cmd; |
611 | 646 | char ext_csd[512]; |
612 | 647 | |
648 | +#ifdef CONFIG_MMC_SPI_CRC_ON | |
649 | + if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */ | |
650 | + cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF; | |
651 | + cmd.resp_type = MMC_RSP_R1; | |
652 | + cmd.cmdarg = 1; | |
653 | + cmd.flags = 0; | |
654 | + err = mmc_send_cmd(mmc, &cmd, NULL); | |
655 | + | |
656 | + if (err) | |
657 | + return err; | |
658 | + } | |
659 | +#endif | |
660 | + | |
613 | 661 | /* Put the Card in Identify Mode */ |
614 | - cmd.cmdidx = MMC_CMD_ALL_SEND_CID; | |
662 | + cmd.cmdidx = mmc_host_is_spi(mmc) ? MMC_CMD_SEND_CID : | |
663 | + MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */ | |
615 | 664 | cmd.resp_type = MMC_RSP_R2; |
616 | 665 | cmd.cmdarg = 0; |
617 | 666 | cmd.flags = 0; |
618 | 667 | |
619 | 668 | |
620 | 669 | |
... | ... | @@ -628,18 +677,20 @@ |
628 | 677 | * For SD cards, get the Relatvie Address. |
629 | 678 | * This also puts the cards into Standby State |
630 | 679 | */ |
631 | - cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR; | |
632 | - cmd.cmdarg = mmc->rca << 16; | |
633 | - cmd.resp_type = MMC_RSP_R6; | |
634 | - cmd.flags = 0; | |
680 | + if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */ | |
681 | + cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR; | |
682 | + cmd.cmdarg = mmc->rca << 16; | |
683 | + cmd.resp_type = MMC_RSP_R6; | |
684 | + cmd.flags = 0; | |
635 | 685 | |
636 | - err = mmc_send_cmd(mmc, &cmd, NULL); | |
686 | + err = mmc_send_cmd(mmc, &cmd, NULL); | |
637 | 687 | |
638 | - if (err) | |
639 | - return err; | |
688 | + if (err) | |
689 | + return err; | |
640 | 690 | |
641 | - if (IS_SD(mmc)) | |
642 | - mmc->rca = (cmd.response[0] >> 16) & 0xffff; | |
691 | + if (IS_SD(mmc)) | |
692 | + mmc->rca = (cmd.response[0] >> 16) & 0xffff; | |
693 | + } | |
643 | 694 | |
644 | 695 | /* Get the Card-Specific Data */ |
645 | 696 | cmd.cmdidx = MMC_CMD_SEND_CSD; |
646 | 697 | |
... | ... | @@ -715,14 +766,16 @@ |
715 | 766 | mmc->write_bl_len = 512; |
716 | 767 | |
717 | 768 | /* Select the card, and put it into Transfer Mode */ |
718 | - cmd.cmdidx = MMC_CMD_SELECT_CARD; | |
719 | - cmd.resp_type = MMC_RSP_R1b; | |
720 | - cmd.cmdarg = mmc->rca << 16; | |
721 | - cmd.flags = 0; | |
722 | - err = mmc_send_cmd(mmc, &cmd, NULL); | |
769 | + if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */ | |
770 | + cmd.cmdidx = MMC_CMD_SELECT_CARD; | |
771 | + cmd.resp_type = MMC_RSP_R1b; | |
772 | + cmd.cmdarg = mmc->rca << 16; | |
773 | + cmd.flags = 0; | |
774 | + err = mmc_send_cmd(mmc, &cmd, NULL); | |
723 | 775 | |
724 | - if (err) | |
725 | - return err; | |
776 | + if (err) | |
777 | + return err; | |
778 | + } | |
726 | 779 | |
727 | 780 | if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) { |
728 | 781 | /* check ext_csd version and capacity */ |
drivers/mmc/mmc_spi.c
1 | +/* | |
2 | + * generic mmc spi driver | |
3 | + * | |
4 | + * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> | |
5 | + * Licensed under the GPL-2 or later. | |
6 | + */ | |
7 | +#include <common.h> | |
8 | +#include <malloc.h> | |
9 | +#include <part.h> | |
10 | +#include <mmc.h> | |
11 | +#include <spi.h> | |
12 | +#include <crc.h> | |
13 | +#include <linux/crc7.h> | |
14 | +#include <linux/byteorder/swab.h> | |
15 | + | |
16 | +/* MMC/SD in SPI mode reports R1 status always */ | |
17 | +#define R1_SPI_IDLE (1 << 0) | |
18 | +#define R1_SPI_ERASE_RESET (1 << 1) | |
19 | +#define R1_SPI_ILLEGAL_COMMAND (1 << 2) | |
20 | +#define R1_SPI_COM_CRC (1 << 3) | |
21 | +#define R1_SPI_ERASE_SEQ (1 << 4) | |
22 | +#define R1_SPI_ADDRESS (1 << 5) | |
23 | +#define R1_SPI_PARAMETER (1 << 6) | |
24 | +/* R1 bit 7 is always zero, reuse this bit for error */ | |
25 | +#define R1_SPI_ERROR (1 << 7) | |
26 | + | |
27 | +/* Response tokens used to ack each block written: */ | |
28 | +#define SPI_MMC_RESPONSE_CODE(x) ((x) & 0x1f) | |
29 | +#define SPI_RESPONSE_ACCEPTED ((2 << 1)|1) | |
30 | +#define SPI_RESPONSE_CRC_ERR ((5 << 1)|1) | |
31 | +#define SPI_RESPONSE_WRITE_ERR ((6 << 1)|1) | |
32 | + | |
33 | +/* Read and write blocks start with these tokens and end with crc; | |
34 | + * on error, read tokens act like a subset of R2_SPI_* values. | |
35 | + */ | |
36 | +#define SPI_TOKEN_SINGLE 0xfe /* single block r/w, multiblock read */ | |
37 | +#define SPI_TOKEN_MULTI_WRITE 0xfc /* multiblock write */ | |
38 | +#define SPI_TOKEN_STOP_TRAN 0xfd /* terminate multiblock write */ | |
39 | + | |
40 | +/* MMC SPI commands start with a start bit "0" and a transmit bit "1" */ | |
41 | +#define MMC_SPI_CMD(x) (0x40 | (x & 0x3f)) | |
42 | + | |
43 | +/* bus capability */ | |
44 | +#define MMC_SPI_VOLTAGE (MMC_VDD_32_33 | MMC_VDD_33_34) | |
45 | +#define MMC_SPI_MIN_CLOCK 400000 /* 400KHz to meet MMC spec */ | |
46 | + | |
47 | +/* timeout value */ | |
48 | +#define CTOUT 8 | |
49 | +#define RTOUT 3000000 /* 1 sec */ | |
50 | +#define WTOUT 3000000 /* 1 sec */ | |
51 | + | |
52 | +static uint mmc_spi_sendcmd(struct mmc *mmc, ushort cmdidx, u32 cmdarg) | |
53 | +{ | |
54 | + struct spi_slave *spi = mmc->priv; | |
55 | + u8 cmdo[7]; | |
56 | + u8 r1; | |
57 | + int i; | |
58 | + cmdo[0] = 0xff; | |
59 | + cmdo[1] = MMC_SPI_CMD(cmdidx); | |
60 | + cmdo[2] = cmdarg >> 24; | |
61 | + cmdo[3] = cmdarg >> 16; | |
62 | + cmdo[4] = cmdarg >> 8; | |
63 | + cmdo[5] = cmdarg; | |
64 | + cmdo[6] = (crc7(0, &cmdo[1], 5) << 1) | 0x01; | |
65 | + spi_xfer(spi, sizeof(cmdo) * 8, cmdo, NULL, 0); | |
66 | + for (i = 0; i < CTOUT; i++) { | |
67 | + spi_xfer(spi, 1 * 8, NULL, &r1, 0); | |
68 | + if (i && (r1 & 0x80) == 0) /* r1 response */ | |
69 | + break; | |
70 | + } | |
71 | + debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1); | |
72 | + return r1; | |
73 | +} | |
74 | + | |
75 | +static uint mmc_spi_readdata(struct mmc *mmc, void *xbuf, | |
76 | + u32 bcnt, u32 bsize) | |
77 | +{ | |
78 | + struct spi_slave *spi = mmc->priv; | |
79 | + u8 *buf = xbuf; | |
80 | + u8 r1; | |
81 | + u16 crc; | |
82 | + int i; | |
83 | + while (bcnt--) { | |
84 | + for (i = 0; i < RTOUT; i++) { | |
85 | + spi_xfer(spi, 1 * 8, NULL, &r1, 0); | |
86 | + if (r1 != 0xff) /* data token */ | |
87 | + break; | |
88 | + } | |
89 | + debug("%s:tok%d %x\n", __func__, i, r1); | |
90 | + if (r1 == SPI_TOKEN_SINGLE) { | |
91 | + spi_xfer(spi, bsize * 8, NULL, buf, 0); | |
92 | + spi_xfer(spi, 2 * 8, NULL, &crc, 0); | |
93 | +#ifdef CONFIG_MMC_SPI_CRC_ON | |
94 | + if (swab16(cyg_crc16(buf, bsize)) != crc) { | |
95 | + debug("%s: CRC error\n", mmc->name); | |
96 | + r1 = R1_SPI_COM_CRC; | |
97 | + break; | |
98 | + } | |
99 | +#endif | |
100 | + r1 = 0; | |
101 | + } else { | |
102 | + r1 = R1_SPI_ERROR; | |
103 | + break; | |
104 | + } | |
105 | + buf += bsize; | |
106 | + } | |
107 | + return r1; | |
108 | +} | |
109 | + | |
110 | +static uint mmc_spi_writedata(struct mmc *mmc, const void *xbuf, | |
111 | + u32 bcnt, u32 bsize, int multi) | |
112 | +{ | |
113 | + struct spi_slave *spi = mmc->priv; | |
114 | + const u8 *buf = xbuf; | |
115 | + u8 r1; | |
116 | + u16 crc; | |
117 | + u8 tok[2]; | |
118 | + int i; | |
119 | + tok[0] = 0xff; | |
120 | + tok[1] = multi ? SPI_TOKEN_MULTI_WRITE : SPI_TOKEN_SINGLE; | |
121 | + while (bcnt--) { | |
122 | +#ifdef CONFIG_MMC_SPI_CRC_ON | |
123 | + crc = swab16(cyg_crc16((u8 *)buf, bsize)); | |
124 | +#endif | |
125 | + spi_xfer(spi, 2 * 8, tok, NULL, 0); | |
126 | + spi_xfer(spi, bsize * 8, buf, NULL, 0); | |
127 | + spi_xfer(spi, 2 * 8, &crc, NULL, 0); | |
128 | + for (i = 0; i < CTOUT; i++) { | |
129 | + spi_xfer(spi, 1 * 8, NULL, &r1, 0); | |
130 | + if ((r1 & 0x10) == 0) /* response token */ | |
131 | + break; | |
132 | + } | |
133 | + debug("%s:tok%d %x\n", __func__, i, r1); | |
134 | + if (SPI_MMC_RESPONSE_CODE(r1) == SPI_RESPONSE_ACCEPTED) { | |
135 | + for (i = 0; i < WTOUT; i++) { /* wait busy */ | |
136 | + spi_xfer(spi, 1 * 8, NULL, &r1, 0); | |
137 | + if (i && r1 == 0xff) { | |
138 | + r1 = 0; | |
139 | + break; | |
140 | + } | |
141 | + } | |
142 | + if (i == WTOUT) { | |
143 | + debug("%s:wtout %x\n", __func__, r1); | |
144 | + r1 = R1_SPI_ERROR; | |
145 | + break; | |
146 | + } | |
147 | + } else { | |
148 | + debug("%s: err %x\n", __func__, r1); | |
149 | + r1 = R1_SPI_COM_CRC; | |
150 | + break; | |
151 | + } | |
152 | + buf += bsize; | |
153 | + } | |
154 | + if (multi && bcnt == -1) { /* stop multi write */ | |
155 | + tok[1] = SPI_TOKEN_STOP_TRAN; | |
156 | + spi_xfer(spi, 2 * 8, tok, NULL, 0); | |
157 | + for (i = 0; i < WTOUT; i++) { /* wait busy */ | |
158 | + spi_xfer(spi, 1 * 8, NULL, &r1, 0); | |
159 | + if (i && r1 == 0xff) { | |
160 | + r1 = 0; | |
161 | + break; | |
162 | + } | |
163 | + } | |
164 | + if (i == WTOUT) { | |
165 | + debug("%s:wstop %x\n", __func__, r1); | |
166 | + r1 = R1_SPI_ERROR; | |
167 | + } | |
168 | + } | |
169 | + return r1; | |
170 | +} | |
171 | + | |
172 | +static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd, | |
173 | + struct mmc_data *data) | |
174 | +{ | |
175 | + struct spi_slave *spi = mmc->priv; | |
176 | + u8 r1; | |
177 | + int i; | |
178 | + int ret = 0; | |
179 | + debug("%s:cmd%d %x %x %x\n", __func__, | |
180 | + cmd->cmdidx, cmd->resp_type, cmd->cmdarg, cmd->flags); | |
181 | + spi_claim_bus(spi); | |
182 | + spi_cs_activate(spi); | |
183 | + r1 = mmc_spi_sendcmd(mmc, cmd->cmdidx, cmd->cmdarg); | |
184 | + if (r1 == 0xff) { /* no response */ | |
185 | + ret = NO_CARD_ERR; | |
186 | + goto done; | |
187 | + } else if (r1 & R1_SPI_COM_CRC) { | |
188 | + ret = COMM_ERR; | |
189 | + goto done; | |
190 | + } else if (r1 & ~R1_SPI_IDLE) { /* other errors */ | |
191 | + ret = TIMEOUT; | |
192 | + goto done; | |
193 | + } else if (cmd->resp_type == MMC_RSP_R2) { | |
194 | + r1 = mmc_spi_readdata(mmc, cmd->response, 1, 16); | |
195 | + for (i = 0; i < 4; i++) | |
196 | + cmd->response[i] = swab32(cmd->response[i]); | |
197 | + debug("r128 %x %x %x %x\n", cmd->response[0], cmd->response[1], | |
198 | + cmd->response[2], cmd->response[3]); | |
199 | + } else if (!data) { | |
200 | + switch (cmd->cmdidx) { | |
201 | + case SD_CMD_APP_SEND_OP_COND: | |
202 | + case MMC_CMD_SEND_OP_COND: | |
203 | + cmd->response[0] = (r1 & R1_SPI_IDLE) ? 0 : OCR_BUSY; | |
204 | + break; | |
205 | + case SD_CMD_SEND_IF_COND: | |
206 | + case MMC_CMD_SPI_READ_OCR: | |
207 | + spi_xfer(spi, 4 * 8, NULL, cmd->response, 0); | |
208 | + cmd->response[0] = swab32(cmd->response[0]); | |
209 | + debug("r32 %x\n", cmd->response[0]); | |
210 | + break; | |
211 | + } | |
212 | + } else { | |
213 | + debug("%s:data %x %x %x\n", __func__, | |
214 | + data->flags, data->blocks, data->blocksize); | |
215 | + if (data->flags == MMC_DATA_READ) | |
216 | + r1 = mmc_spi_readdata(mmc, data->dest, | |
217 | + data->blocks, data->blocksize); | |
218 | + else if (data->flags == MMC_DATA_WRITE) | |
219 | + r1 = mmc_spi_writedata(mmc, data->src, | |
220 | + data->blocks, data->blocksize, | |
221 | + (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)); | |
222 | + if (r1 & R1_SPI_COM_CRC) | |
223 | + ret = COMM_ERR; | |
224 | + else if (r1) /* other errors */ | |
225 | + ret = TIMEOUT; | |
226 | + } | |
227 | +done: | |
228 | + spi_cs_deactivate(spi); | |
229 | + spi_release_bus(spi); | |
230 | + return ret; | |
231 | +} | |
232 | + | |
233 | +static void mmc_spi_set_ios(struct mmc *mmc) | |
234 | +{ | |
235 | + struct spi_slave *spi = mmc->priv; | |
236 | + debug("%s: clock %u\n", __func__, mmc->clock); | |
237 | + if (mmc->clock) | |
238 | + spi_set_speed(spi, mmc->clock); | |
239 | +} | |
240 | + | |
241 | +static int mmc_spi_init_p(struct mmc *mmc) | |
242 | +{ | |
243 | + struct spi_slave *spi = mmc->priv; | |
244 | + mmc->clock = 0; | |
245 | + spi_set_speed(spi, MMC_SPI_MIN_CLOCK); | |
246 | + spi_claim_bus(spi); | |
247 | + /* cs deactivated for 100+ clock */ | |
248 | + spi_xfer(spi, 18 * 8, NULL, NULL, 0); | |
249 | + spi_release_bus(spi); | |
250 | + return 0; | |
251 | +} | |
252 | + | |
253 | +struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode) | |
254 | +{ | |
255 | + struct mmc *mmc; | |
256 | + | |
257 | + mmc = malloc(sizeof(*mmc)); | |
258 | + if (!mmc) | |
259 | + return NULL; | |
260 | + memset(mmc, 0, sizeof(*mmc)); | |
261 | + mmc->priv = spi_setup_slave(bus, cs, speed, mode); | |
262 | + if (!mmc->priv) { | |
263 | + free(mmc); | |
264 | + return NULL; | |
265 | + } | |
266 | + sprintf(mmc->name, "MMC_SPI"); | |
267 | + mmc->send_cmd = mmc_spi_request; | |
268 | + mmc->set_ios = mmc_spi_set_ios; | |
269 | + mmc->init = mmc_spi_init_p; | |
270 | + mmc->host_caps = MMC_MODE_SPI; | |
271 | + | |
272 | + mmc->voltages = MMC_SPI_VOLTAGE; | |
273 | + mmc->f_max = speed; | |
274 | + mmc->f_min = MMC_SPI_MIN_CLOCK; | |
275 | + mmc->block_dev.part_type = PART_TYPE_DOS; | |
276 | + | |
277 | + mmc_register(mmc); | |
278 | + | |
279 | + return mmc; | |
280 | +} |
include/mmc.h
... | ... | @@ -44,6 +44,7 @@ |
44 | 44 | #define MMC_MODE_HS_52MHz 0x010 |
45 | 45 | #define MMC_MODE_4BIT 0x100 |
46 | 46 | #define MMC_MODE_8BIT 0x200 |
47 | +#define MMC_MODE_SPI 0x400 | |
47 | 48 | |
48 | 49 | #define SD_DATA_4BIT 0x00040000 |
49 | 50 | |
... | ... | @@ -75,6 +76,8 @@ |
75 | 76 | #define MMC_CMD_WRITE_SINGLE_BLOCK 24 |
76 | 77 | #define MMC_CMD_WRITE_MULTIPLE_BLOCK 25 |
77 | 78 | #define MMC_CMD_APP_CMD 55 |
79 | +#define MMC_CMD_SPI_READ_OCR 58 | |
80 | +#define MMC_CMD_SPI_CRC_ON_OFF 59 | |
78 | 81 | |
79 | 82 | #define SD_CMD_SEND_RELATIVE_ADDR 3 |
80 | 83 | #define SD_CMD_SWITCH_FUNC 6 |
... | ... | @@ -291,6 +294,8 @@ |
291 | 294 | |
292 | 295 | #ifdef CONFIG_GENERIC_MMC |
293 | 296 | int atmel_mci_init(void *regs); |
297 | +#define mmc_host_is_spi(mmc) ((mmc)->host_caps & MMC_MODE_SPI) | |
298 | +struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode); | |
294 | 299 | #else |
295 | 300 | int mmc_legacy_init(int verbose); |
296 | 301 | #endif |