我的内核是linux 2.6.28,已经带有了sd
mmc卡驱动了只要在menuconfig里面把相应选项选上即可,
编译出来了之后,我插上了我的一个512mb的卡,在启动信息了打印如下:
s3c2440-sdi s3c2440-sdi: powered down. mmc0: error -110 whilst initialising SD card s3c2440-sdi s3c2440-sdi: powered down. 注意红色部分,进入了之后/dev/devices
里面看不到没有相应的驱动。驱动工作不正常!
但是插上了一个32mb的卡后,就能够正常识别。
在网上查到如下信息:
mmc0: error -110
whilst initialising SD card
應該是卡在 linux/driver/mmc/core/sd.c 的 mmc_sd_init_card() --> mmc_send_app_op_cond(host, ocr, NULL) ocr 是指 card 內部的 Operation Condition Register (OCR) 讀出來的值 發送 CMD41 CMD55 讀取 OCR 的值 問題是出在 OCR[31] 一直是 0 <--- 初始化沒有完成?? 結果問題是出在電壓不足,要把 sd power enable 上面这位说的解决方法我也不太清楚,不过他指出的错误地方是正确的,就是在那里出错的。
现在说下这个错误的解决方案:
driver/mmc/core/sd.c
的mmc_sd_init_card()函數中mmc_read_switch前延時10ms
mdelay(10); err = mmc_read_switch(card); 看到红色语句了么,就这个加上去就可以了啊,哈哈......
加上红色延时语句之后启动信息如下
s3c2440-sdi s3c2440-sdi: running at 196kHz
(requested: 195kHz).
s3c2440-sdi s3c2440-sdi: running at 25000kHz (requested: 25000kHz). s3c2440-sdi s3c2440-sdi: running at 25000kHz (requested: 25000kHz). mmc0: new SD card at address 95b9 mmcblk0: mmc0:95b9 SD02G 1.83 GiB mmcblk0: p1 驱动工作完全正常。 进入系统后 mount /dev/mmcblk0p1 /mnt 初始化:
SD卡调试关键点: 1. 上电时要延时足够长的时间给SD卡一个准备过程,在我的程序里是5秒,根据不同的卡设置不同的延时时间。SD卡初始化第一步在发送CMD命令之前,在片选有效的情况下首先要发送至少74个时钟,否则将有可能出现SD卡不能初始化的问题。 2. SD卡发送复位命令CMD0后,要发送版本查询命令CMD8,返回状态一般分两种,若返回0x01表示此SD卡接受CMD8,也就是说此SD卡支持版本2;若返回0x05则表示此SD卡支持版本1。因为不同版本的SD卡操作要求有不一样的地方,所以务必查询SD卡的版本号,否则也会出现SD卡无法正常工作的问题。 3. 理论上要求发送CMD58获得SD卡电压参数,但实际过程中由于事先都知道了SD卡的工作电压,因此可省略这一步简化程序。协议书上也建议尽量不要用这个命令。 4. SD卡读写超时时间要按照协议说明书书上的给定值(读超时:100ms;写超时:250ms),这个值要在程序中准确计算出来,否则将会出现不能正常读写数据的问题。我自己定义了一个计算公式:超时时间=(8/clk)*arg。 5. 2GB以内的SD卡(标准卡)和2GB以上的SD卡(大容量卡)在地址访问形式上不同,这一点尤其要注意,否则将会出现无法读写数据的问题。如标准卡在读写操作时,对读或写命令令牌当中的地址域符初值0x10,表示对第16个字节以后的地址单元进行操作(前提是此SD卡支持偏移读写操作),而对大容量卡读或写命令令牌当中的地址域符初值0x10时,则表示对第16块进行读写操作,而且大容量卡只支持块读写操作,块大小固定为512字节,对其进行字节操作将会出错。 6. 对某一块要进行写操作时最好先执行擦出命令,这样写入的速度就能大大提高。进行擦除操作时不管是标准卡还是大容量卡都按块操作执行,也就是一次擦除至少512字节。 7. 对标准卡进行字节操作时,起始和终止必须在一个物理扇区内,否则将不能进行读写操作。实际操作过程中建议用块操作以提高效率。不管是标准卡还是大容量卡一个读写命令只能对一个块进行操作,不允许跨物理层地址操作。 8. 在写数据块前要先写入若干个dummy data字节,写完一个块数据时,主机要监测MISO数据线,如果从机处于忙状态这根数据线会保持低电平,这样主机就可以根据这根数据线的状态以决定是否发送下一个命令,在从机没有释放MISO数据线之前,主机绝对不能执行其他命令,否则将会导致写入的数据出错,而且从机也不会响应主机的命令。 9. 在SPI模式下,CRC校验是被忽略的,但依然要求主从机发送CRC码,只是数值可以是任意值,一般主机的CRC码通常设为0x00或0xFF。 读多块操作和写多块操作的传输停止形式不一样,读多块操作时用用命令CMD12终止传输,而写多块操作时用Stop Tran Token(停止传输令牌,值为0xFD)终止传输。 ---------------------------------------------------------------------------------------- 1、
(1)图中的Y轴坐标表示SD卡支持的电压,一般都是2.7-3.6v之间。X轴为时间坐标。 这段时间为插上SD卡,到sd卡的的供电电压达到最低要求电压的这段时间。这个时候sd clock还没正常提供,处于禁止状态,Linux在这段时间内delay 10ms。 (3)supply ramp up time阶段: (2)(3)阶段时间段,对应Linux mmc子系统的codes为:
* Apply power to the MMC stack. This is a two-stage process. * First, we enable power to the card without the clock running. * We then wait a bit for the power to stabilise. Finally, * enable the bus drivers and clock to the card. * * We must _NOT_ enable the clock prior to power stablising. * * If a host does all the power sequencing itself, ignore the * initial MMC_POWER_UP stage. */ static void mmc_power_up(struct mmc_host *host) { int bit; /* If ocr is set, we use it */ if (host->ocr) bit = ffs(host->ocr) - 1; else bit = fls(host->ocr_avail) - 1; host->ios.vdd = bit; if (mmc_host_is_spi(host)) { host->ios.chip_select = MMC_CS_HIGH; host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; } else { host->ios.chip_select = MMC_CS_DONTCARE; host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; } host->ios.power_mode = MMC_POWER_UP; host->ios.bus_width = MMC_BUS_WIDTH_1; host->ios.timing = MMC_TIMING_LEGACY; mmc_set_ios(host);//这段code被调用之前,为状态(2)power up time时间段,这段时间内host->ios.clock被设置为0. /* * This delay should be sufficient to allow the power supply * to reach the minimum voltage. */ mmc_delay(10); host->ios.clock = host->f_init; host->ios.power_mode = MMC_POWER_ON; mmc_set_ios(host);//这段code之前属于supply ramp up time阶段,这段时间内host->ios.clock被设置为最小值,我们在驱动中会设置这个值,这个值应该小于等于400KHZ. /* * This delay must be at least 74 clock sizes, or 1 ms, or the * time required to reach a stable voltage. */ mmc_delay(10);//这个延时是等sd host给出74个clocks。74个clocks期间CMD线拉高,这个时候不能发送任何命令给SD卡。这个延时过后,系统就可以发送命令给sd card了。 } (4)CMD0阶段 对应linux驱动中的code为:
{ int err; struct mmc_command cmd; /* * Non-SPI hosts need to prevent chipselect going active during * GO_IDLE; that would put chips into SPI mode. Remind them of * that in case of hardware that won't pull up DAT3/nCS otherwise. * * SPI hosts ignore ios.chip_select; it's managed according to * rules that must accomodate non-MMC slaves which this layer * won't even know about. */ #if 0 if (!mmc_host_is_spi(host)) { mmc_set_chip_select(host, MMC_CS_HIGH); mmc_delay(1); } #endif memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_GO_IDLE_STATE; cmd.arg = 0; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC; err = mmc_wait_for_cmd(host, &cmd, 0); mmc_delay(1); if (!mmc_host_is_spi(host)) { mmc_set_chip_select(host, MMC_CS_DONTCARE); mmc_delay(1); } host->use_spi_crc = 0; return err; } 这段时间sd进入idle状态。 (5)CMD8阶段 sd host发送cmd8来check sd host端支持的电压 host->ocr_avail是否被sd卡支持,如果sd支持的话那么在cmd8的response中会返回这些支持的值,并且返回写入cmd8命令中的test_pattern值。否则就表示不支持sd host的电压范围。对应的linux mmc驱动代码为:
{ struct mmc_command cmd; int err; static const u8 test_pattern = 0xAA; u8 result_pattern; /* * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND * before SD_APP_OP_COND. This command will harmlessly fail for * SD 1.0 cards. */ cmd.opcode = SD_SEND_IF_COND; cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR; err = mmc_wait_for_cmd(host, &cmd, 0); if (err) return err; if (mmc_host_is_spi(host)) result_pattern = cmd.resp[1] & 0xFF; else result_pattern = cmd.resp[0] & 0xFF; if (result_pattern != test_pattern) return -EIO; return 0; }
(6) ACMD41阶段 (1)这个阶段设置ACMD41的参数OCR=0,为了去获得SD卡自身支持的电压范围值。然后与 host->ocr_avail,SD host所能支持的电压范围取交集,如果交集为空,则返回匹配失败。
{ struct mmc_command cmd; int i, err = 0; BUG_ON(!host); memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = SD_APP_OP_COND; if (mmc_host_is_spi(host)) cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */ else cmd.arg = ocr; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR; for (i = 100; i; i--) { err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES); if (err) break; /* if we're just probing, do a single pass */ if (ocr == 0) break; /* otherwise wait until reset completes */ if (mmc_host_is_spi(host)) { if (!(cmd.resp[0] & R1_SPI_IDLE)) break; } else { if (cmd.resp[0] & MMC_CARD_BUSY) break; } err = -ETIMEDOUT; mmc_delay(10); } if (rocr && !mmc_host_is_spi(host)) *rocr = cmd.resp[0]; return err; } (7) CMD2阶段 通过这个命令或者所有SD卡的 Menufacory id ,Product id等卡的信息。
{ int err; struct mmc_command cmd; BUG_ON(!host); BUG_ON(!cid); memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_ALL_SEND_CID; cmd.arg = 0; cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); if (err) return err; memcpy(cid, cmd.resp, sizeof(u32) * 4); return 0; } (8)CMD3取得卡的相对地址,进入数据传输模式,点对点的传输。
{ int err; struct mmc_command cmd; BUG_ON(!host); BUG_ON(!rca); memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = SD_SEND_RELATIVE_ADDR; cmd.arg = 0; cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); if (err) return err; *rca = cmd.resp[0] >> 16; return 0; } |
|
来自: 笔录收藏 > 《usb枚举SD卡》