AnsweredAssumed Answered

Does I.MX6 have some bugs when it is used with 32-bits DDR3?

Question asked by 芸菲 尤 on Apr 18, 2018
Latest reply on Apr 19, 2018 by 芸菲 尤
Hi,all:
我们产品A使用I.MX6DL(MCIMX6U7CVM08AC)和 2片MT41K256M16TW-107来组成32位的DDR3系统。在调试之初,我按照 https://community.nxp.com/docs/DOC-94917 文档,做了DDR3基本配置、校准和压力测试。
所有项都通过了,系统在正常运行时也没有异常。
硬件设计原理图如下:
当我将U盘插入板子时,我发现在"/dev/"目录下比没有产生类似 "sd*" 的设备节点,内核输出信息如下:
usb 2-1.2: new high-speed USB device number 6 using ci_hdrc
usb 2-1.2: New USB device found, idVendor=0781, idProduct=556b
usb 2-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 2-1.2: Product: Cruzer Edge
usb 2-1.2: Manufacturer: SanDisk
usb 2-1.2: SerialNumber: 4C530399950108110182
usb-storage 2-1.2:1.0: USB Mass Storage device detected
scsi host3: usb-storage 2-1.2:1.0
scsi 3:0:0:0: scsi scan: INQUIRY result too short (5), using 36
scsi 3:0:0:0: Direct-Access PQ: 0 ANSI: 0
usb 2-1.2: USB disconnect, device number 6
sd 3:0:0:0: [sda] Read Capacity(10) failed: Result: hostbyte=DID_NO_CONNECT driverbyte=DRIVER_OK
sd 3:0:0:0: [sda] Sense not available.
sd 3:0:0:0: [sda] Write Protect is off
sd 3:0:0:0: [sda] Mode Sense: 55 53 42 53
sd 3:0:0:0: [sda] Incomplete mode parameter data
sd 3:0:0:0: [sda] Assuming drive cache: write through
sd 3:0:0:0: [sda] Read Capacity(10) failed: Result: hostbyte=DID_NO_CONNECT driverbyte=DRIVER_OK
sd 3:0:0:0: [sda] Sense not available.
sd 3:0:0:0: [sda] Attached SCSI disk
我们硬件工程师测试了DDR3相关的电源,发现没有异常(整个DDR3相关电源由MMPF0100F0AEP PMIC芯片提供)。
我在u-boot下使用usb命令是可以正常读取U盘中文件的内容的;并且在Linux下,我也用USB接口的WIFI网卡试过,可以进行网络通信,USB通信看起来也没有问题。
然后,我使用tcpdump和wireshark抓取USB发送的URB包,发现在 int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) 函数中发出的SCSI命令是错误的,而这发生在内存拷贝上。
为了修改这个问题我添加了以下修改内容
/**
* CONFIG_USB_MEMORY_DATA_INCOHERENT_FIXED
*
* memset(bcb->CDB, 0, sizeof(bcb->CDB));
* memcpy(bcb->CDB, srb->cmnd, bcb->Length);
* Does not work, data can not copy from srb->cmnd to bcb->CDB
* I just use one temperary memory to avoid memory copy form srb->cmnd to bcb->CDB dirctly.
* Wow!, it does work!!
* Fix this problem, if find real reason
*/
int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
{
struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf;
unsigned int transfer_length = scsi_bufflen(srb);
unsigned int residue;
int result;
int fake_sense = 0;
unsigned int cswlen;
unsigned int cbwlen = US_BULK_CB_WRAP_LEN;
#ifdef CONFIG_USB_MEMORY_DATA_INCOHERENT_FIXED
__u8 cdb[64], *tmp;
#endif
/* Take care of BULK32 devices; set extra byte to 0 */
if (unlikely(us->fflags & US_FL_BULK32)) {
cbwlen = 32;
us->iobuf[31] = 0;
}
/* set up the command wrapper */
bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
bcb->DataTransferLength = cpu_to_le32(transfer_length);
bcb->Flags = srb->sc_data_direction == DMA_FROM_DEVICE ?
US_BULK_FLAG_IN : 0;
bcb->Tag = ++us->tag;
bcb->Lun = srb->device->lun;
if (us->fflags & US_FL_SCM_MULT_TARG)
bcb->Lun |= srb->device->id << 4;
bcb->Length = srb->cmd_len;
/* copy the command payload */
#ifdef CONFIG_USB_MEMORY_DATA_INCOHERENT_FIXED
memset(cdb, 0, sizeof(cdb));
memcpy(&cdb[1], srb->cmnd, srb->cmd_len);
cdb[0] = srb->cmd_len; /* bcb->Length = srb->cmd_len */
tmp = &(bcb->Length);
memcpy(tmp, cdb, srb->cmd_len + 1); /* bcb->Length + CDB */
#else
memset(bcb->CDB, 0, sizeof(bcb->CDB));
memcpy(bcb->CDB, srb->cmnd, bcb->Length);
#endif
/* send it to out endpoint */
usb_stor_dbg(us, "Bulk Command S 0x%x T 0x%x L %d F %d Trg %d LUN %d CL %d\n",
le32_to_cpu(bcb->Signature), bcb->Tag,
le32_to_cpu(bcb->DataTransferLength), bcb->Flags,
(bcb->Lun >> 4), (bcb->Lun & 0x0F),
bcb->Length);
result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
bcb, cbwlen, NULL);
usb_stor_dbg(us, "Bulk command transfer result=%d\n", result);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
/* DATA STAGE */
/* send/receive data payload, if there is any */
/* Some USB-IDE converter chips need a 100us delay between the
* command phase and the data phase. Some devices need a little
* more than that, probably because of clock rate inaccuracies. */
if (unlikely(us->fflags & US_FL_GO_SLOW))
udelay(125);
if (transfer_length) {
unsigned int pipe = srb->sc_data_direction == DMA_FROM_DEVICE ?
us->recv_bulk_pipe : us->send_bulk_pipe;
result = usb_stor_bulk_srb(us, pipe, srb);
usb_stor_dbg(us, "Bulk data transfer result 0x%x\n", result);
if (result == USB_STOR_XFER_ERROR)
return USB_STOR_TRANSPORT_ERROR;
/* If the device tried to send back more data than the
* amount requested, the spec requires us to transfer
* the CSW anyway. Since there's no point retrying the
* the command, we'll return fake sense data indicating
* Illegal Request, Invalid Field in CDB.
*/
if (result == USB_STOR_XFER_LONG)
fake_sense = 1;
/*
* Sometimes a device will mistakenly skip the data phase
* and go directly to the status phase without sending a
* zero-length packet. If we get a 13-byte response here,
* check whether it really is a CSW.
*/
if (result == USB_STOR_XFER_SHORT &&
srb->sc_data_direction == DMA_FROM_DEVICE &&
transfer_length - scsi_get_resid(srb) ==
US_BULK_CS_WRAP_LEN) {
struct scatterlist *sg = NULL;
unsigned int offset = 0;
if (usb_stor_access_xfer_buf((unsigned char *) bcs,
US_BULK_CS_WRAP_LEN, srb, &sg,
&offset, FROM_XFER_BUF) ==
US_BULK_CS_WRAP_LEN &&
bcs->Signature ==
cpu_to_le32(US_BULK_CS_SIGN)) {
usb_stor_dbg(us, "Device skipped data phase\n");
scsi_set_resid(srb, transfer_length);
goto skipped_data_phase;
}
}
}
/* See flow chart on pg 15 of the Bulk Only Transport spec for
* an explanation of how this code works.
*/
/* get CSW for device status */
usb_stor_dbg(us, "Attempting to get CSW...\n");
result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
bcs, US_BULK_CS_WRAP_LEN, &cswlen);
/* Some broken devices add unnecessary zero-length packets to the
* end of their data transfers. Such packets show up as 0-length
* CSWs. If we encounter such a thing, try to read the CSW again.
*/
if (result == USB_STOR_XFER_SHORT && cswlen == 0) {
usb_stor_dbg(us, "Received 0-length CSW; retrying...\n");
result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
bcs, US_BULK_CS_WRAP_LEN, &cswlen);
}
/* did the attempt to read the CSW fail? */
if (result == USB_STOR_XFER_STALLED) {
/* get the status again */
usb_stor_dbg(us, "Attempting to get CSW (2nd try)...\n");
result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
bcs, US_BULK_CS_WRAP_LEN, NULL);
}
/* if we still have a failure at this point, we're in trouble */
usb_stor_dbg(us, "Bulk status result = %d\n", result);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
skipped_data_phase:
/* check bulk status */
residue = le32_to_cpu(bcs->Residue);
usb_stor_dbg(us, "Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n",
le32_to_cpu(bcs->Signature), bcs->Tag,
residue, bcs->Status);
if (!(bcs->Tag == us->tag || (us->fflags & US_FL_BULK_IGNORE_TAG)) ||
bcs->Status > US_BULK_STAT_PHASE) {
usb_stor_dbg(us, "Bulk logical error\n");
return USB_STOR_TRANSPORT_ERROR;
}
/* Some broken devices report odd signatures, so we do not check them
* for validity against the spec. We store the first one we see,
* and check subsequent transfers for validity against this signature.
*/
if (!us->bcs_signature) {
us->bcs_signature = bcs->Signature;
if (us->bcs_signature != cpu_to_le32(US_BULK_CS_SIGN))
usb_stor_dbg(us, "Learnt BCS signature 0x%08X\n",
le32_to_cpu(us->bcs_signature));
} else if (bcs->Signature != us->bcs_signature) {
usb_stor_dbg(us, "Signature mismatch: got %08X, expecting %08X\n",
le32_to_cpu(bcs->Signature),
le32_to_cpu(us->bcs_signature));
return USB_STOR_TRANSPORT_ERROR;
}
/* try to compute the actual residue, based on how much data
* was really transferred and what the device tells us */
if (residue && !(us->fflags & US_FL_IGNORE_RESIDUE)) {
/* Heuristically detect devices that generate bogus residues
* by seeing what happens with INQUIRY and READ CAPACITY
* commands.
*/
if (bcs->Status == US_BULK_STAT_OK &&
scsi_get_resid(srb) == 0 &&
((srb->cmnd[0] == INQUIRY &&
transfer_length == 36) ||
(srb->cmnd[0] == READ_CAPACITY &&
transfer_length == 8))) {
us->fflags |= US_FL_IGNORE_RESIDUE;
} else {
residue = min(residue, transfer_length);
scsi_set_resid(srb, max(scsi_get_resid(srb),
(int) residue));
}
}
/* based on the status code, we report good or bad */
switch (bcs->Status) {
case US_BULK_STAT_OK:
/* device babbled -- return fake sense data */
if (fake_sense) {
memcpy(srb->sense_buffer,
usb_stor_sense_invalidCDB,
sizeof(usb_stor_sense_invalidCDB));
return USB_STOR_TRANSPORT_NO_SENSE;
}
/* command good -- note that data could be short */
return USB_STOR_TRANSPORT_GOOD;
case US_BULK_STAT_FAIL:
/* command failed */
return USB_STOR_TRANSPORT_FAILED;
case US_BULK_STAT_PHASE:
/* phase error -- note that a transport reset will be
* invoked by the invoke_transport() function
*/
return USB_STOR_TRANSPORT_ERROR;
}
/* we should never get here, but if we do, we're in trouble */
return USB_STOR_TRANSPORT_ERROR;
}
作此修改后,可以暂时解决U盘不能识别问题。经过测试文件拷贝也正常。
现在我们开发了另一个项目B,项目B使用I.MX6Q,但由于硬件工程师设计失误,我们使用了1片MT41K256M16TW-107组成16位的DDR3,我们发现即使在内核不添加CONFIG_USB_MEMORY_DATA_INCOHERENT_FIXED相关代码时,插入U盘也能正常通信。因此我又修改了A项目的DDR3配置,将DDR3的位宽修改为16bits,同样在不添加CONFIG_USB_MEMORY_DATA_INCOHERENT_FIXED相关代码的情况下和U盘能正常通信。
因此我想问一下,I.MX6在使用32位的DDR3时是否有什么bugs?或者我在DDR3的配置上存在问题?
附件包括我们在u-boot中使用的DCD文件mx6dl_2x_mt41k256m16.cfg,mt41k256m16 数据手册

Outcomes