基于VxWorks 7.0的S25FL256S与ZYNQ驱动及文件系统挂接

好久没有更新博客,只因最近工作比较忙,今天抽点时间写一下在zedboard平台上挂接文件系统,块设备为S25FL256s,共计32M的flash,flash划分为两部分,0x0---0x1000000,前16M用于存储系统uboot和系统的相关参数,0x1000000---0x2000000为truetffs文件系统,其配置如下:

第一步:在VIP里面增加组件DRV_QSPI_FDT_ZYNQ7K,DRV_SPI_FLASH,INCLUDE_TFFS,INCLUDE_TL_FTL,INCLUDE_TFFS_MOUNT,INCLUDE_TFFS_STUB_VXBFLASH,INCLUDE_MTD_VXBFLASH

第二步:flash预留配置,VXBFLASH_CFG_STR "$spflspilash0#0:0x0,0x1000000,0x100000,rfa0"

第三步:调试驱动,由于zynq系列只能支持3字节地址操作,最大支持16M大小flash操作,而本次系统设计的flash为32M flash,如果要四字节操作,必须使用bank寄存器,首先在设备树配置如下:


  qspi0: qspi@e000d000 
            {
	            #address-cells = <1>;
	            #size-cells = <0>;
	            compatible = "xlnx,zynq7k-qspi";
	            reg = <0xe000d000 0x100="">,
	                  <0xf8000000 0x800="">;
	            interrupts = <51>; 
	            interrupt-parent = <&intc>;
	           spiflash@0
		        {
			        compatible = "spflspilash";
			        reg = <0>;
			        data-lines = <4>;
			        chip-number = <1>;
			        addr-mode = <1>;
			        spi-max-frequency = <100000000>;
		        };
            };

addr-mode=1,为三字节地址操作模式,必须修改bank寄存器,所以相关的驱动必须做修改,我在前期调试时,也是由于没有注意到这点,导致flash写0地址时0x1000000地址也被修改,如下是代码:



STATUS spiFlashDataWrite
    (
    SPI_FLASH_DEV * pDrvCtrl,
    UINT32          startAddr,
    UINT32          dataLen,
    UINT8 *         dataBuf
    )
    {
    UINT32  bytesToWrite;
    UINT32  writeCmdLen;
    UINT8   writeCmdOpcode;
    UINT32  windowSize;
    UINT8 * pBuf;
    UINT32  optAddr;
    UINT32  addrDiv;
    STATUS  rc      = OK;
    UINT8   status  = 0;
    UINT8   value = 0;
    
    if ((pDrvCtrl->hd.mode & SPI_PARALLEL) != 0)
        {
        addrDiv = pDrvCtrl->hd.chipNum;
        }
    else
        {
        addrDiv = 1;
        }
 
    writeCmdLen     = pDrvCtrl->addrWidth + 1;
    writeCmdOpcode  = SPI_PP_CMD;
    pBuf            = pDrvCtrl->pWrBuf;
 
    if ((pDrvCtrl->specialInfo != NULL) &&
        (pDrvCtrl->specialInfo->flag & SPI_MASTER_TRANS_LIMIT))
        {
        windowSize = pDrvCtrl->specialInfo->windowSize;
        }
    else
        {
        windowSize = 0;
        }
    //驱动中增加对大于32Mflash的支持,因为zynq不支持大于16Mfalsh的命令,如果大于16M,需要使用bank寄存器,朱红博于2020年10月19日
      if(pDrvCtrl->addrMode == 1)
      {
      	value |= startAddr >>24;
      	spiFlashWriteReg(pDrvCtrl,SPI_WBNK_CMD,value);
      }
    if (spiFlashWaitReady (pDrvCtrl) != OK)
        {
        return ERROR;
        }
 
    /* clear the Write Enable Latch (WEL) bit to 0 */
 
    if (spiFlashReadSR(pDrvCtrl, &status) == OK)
        {
        if ((status & SR_WEL) != 0)
            {
            status &= (UINT8)(~(SR_WEL));
            if (spiFlashWriteReg (pDrvCtrl, SPI_WRSR_CMD, status) != OK)
                {
                return ERROR;
                }
            }
        }
    else
        {
        return ERROR;
        }
 
    /* data must be written in "page Size" chunks */
 
    do
        {
        optAddr = startAddr / addrDiv;
 
        /* set page program command */
 
        pBuf[0] = writeCmdOpcode;
        pBuf[1] = (UINT8)(optAddr >> (pDrvCtrl->addrWidth * 8 - 8));
        pBuf[2] = (UINT8)(optAddr >> (pDrvCtrl->addrWidth * 8 - 16));
        pBuf[3] = (UINT8)(optAddr >> (pDrvCtrl->addrWidth * 8 - 24));
 
        if (pDrvCtrl->addrWidth == 4)
            {
            pBuf[4] = (UINT8)(optAddr);
            }
 
        /* compute the number of data ready to write for one write cycle */
 
        bytesToWrite = min (dataLen, PageSize (pDrvCtrl) -
                            (startAddr % PageSize (pDrvCtrl)));
 
        /* controller might impose a limit on the amount of data */
 
        if (windowSize != 0)
            {
            bytesToWrite = min (bytesToWrite, windowSize - writeCmdLen);
            }
 
        bcopy ((char *)dataBuf, (char *)(pBuf + writeCmdLen), bytesToWrite);
 
        if (spiFlashWriteEn (pDrvCtrl, SPI_WREN_CMD) != OK)
            {
            rc = ERROR;
            break;
            }
 
        DEBUG_MSG (SPI_FLASH_DBG_INFO, "spiFlashDataWrite: WRITE len[0x%x] "
                   "from buf 0x%x to 0x%x, WRITE cmd: 0x%x cmdLen %d, addrShift"
                   " %d\n", bytesToWrite, pBuf, startAddr, *(UINT32 *)pBuf,
                   writeCmdLen, addrDiv);
 
        /* program one page */
 
        if (spiFlashWrite (pDrvCtrl, pBuf, (bytesToWrite + writeCmdLen),
                           pDrvCtrl->ppTime, pDrvCtrl->addrWidth) != OK)
            {
            rc = ERROR;
            break;
            }
 
        if (spiFlashWaitReady (pDrvCtrl) != OK)
            {
            rc = ERROR;
            break;
            }
 
        if (spiFlashStatusCheck (pDrvCtrl) != OK)
            {
            rc = ERROR;
            break;
            }
 
        startAddr  += bytesToWrite;
        dataBuf    += bytesToWrite;
        dataLen    -= bytesToWrite;
        }while (dataLen);
 
    return rc;
    }

代码中增加相关的bank寄存器的操作


if(pDrvCtrl->addrMode == 1)      
{          
    value |= startAddr >>24;          
    spiFlashWriteReg(pDrvCtrl,SPI_WBNK_CMD,value);     
} 

将操作地址的A24写入bank寄存器,bank寄存器的写操作命令时0X17,寄存器定义如下

9.3.5 Bank Register Write (BRWR 17h)

The Bank Register Write (BRWR) command is used to write address bits above A23, into the Bank Address Register (BAR). The command is also used to write the Extended address control bit (EXTADD) that is also in BAR[7]. BAR provides the high order addresses needed by devices having more than 128 Mbits (16 Mbytes), when using 3-byte address commands without extended addressing enabled (BAR[7] EXTADD = 0). Because this command is part of the addressing method and is not changing data in the flash memory, this command does not require the WREN command to precede it. The BRWR instruction is entered, followed by the data byte on SI. The Bank Register is one data byte in length. The BRWR command has no effect on the P_ERR, E_ERR or WIP bits of the Status and Configuration Registers. Any bank address bit reserved for the future should always be written as a 0.


同时,对读操作和扇区擦操作做相同的处理:


STATUS spiFlashDataRead
    (
    SPI_FLASH_DEV * pDrvCtrl,
    UINT32          startAddr,
    UINT32          dataLen,
    UINT8 *         dataBuf
    )
    {
    UINT8 * pCmd;
    UINT32  bytesToRead;
    UINT32  optAddr;
    UINT32  addrDiv;
    UINT32  readCmdLen;
    UINT32  windowSize;
    STATUS  rc = OK;
    UINT8   value = 0;
 
    if ((pDrvCtrl->hd.mode & SPI_PARALLEL) != 0)
        {
        addrDiv = pDrvCtrl->hd.chipNum;
        }
    else
        {
        addrDiv = 1;
        }
 
    readCmdLen  = pDrvCtrl->addrWidth + 1;
 
    if (pDrvCtrl->readCmd != SPI_READ_CMD)
        {
        readCmdLen += pDrvCtrl->readDummy / (8 / pDrvCtrl->hd.dataLines);
        }
 
    if ((pDrvCtrl->specialInfo != NULL) &&
        (pDrvCtrl->specialInfo->flag & SPI_MASTER_TRANS_LIMIT))
        {
        windowSize = pDrvCtrl->specialInfo->windowSize;
        }
    else
        {
        windowSize = 0;
        }
 
    DEBUG_MSG (SPI_FLASH_DBG_INFO, "spiFlashDataRead: Read command is %x, Dummy"
               " is %d clock cycles, command length is %d\n", pDrvCtrl->readCmd,
               pDrvCtrl->readDummy, readCmdLen);
    //驱动中增加对大于32Mflash的支持,因为zynq不支持大于16Mfalsh的命令,如果大于16M,需要使用bank寄存器,朱红博于2020年10月19日
    if(pDrvCtrl->addrMode == 1)
    {
    	value |= startAddr >>24;
    	spiFlashWriteReg(pDrvCtrl,SPI_WBNK_CMD,value);
    }
    	
    pCmd = pDrvCtrl->pWrBuf;
    bzero ((char *)pCmd, readCmdLen);
 
    do
        {
        optAddr = startAddr / addrDiv;
 
        /* set read command */
 
        pCmd[0] = pDrvCtrl->readCmd;
        pCmd[1] = (UINT8)(optAddr >> (pDrvCtrl->addrWidth * 8 - 8));
        pCmd[2] = (UINT8)(optAddr >> (pDrvCtrl->addrWidth * 8 - 16));
        pCmd[3] = (UINT8)(optAddr >> (pDrvCtrl->addrWidth * 8 - 24));
 
        if (pDrvCtrl->addrWidth == 4)
            {
            pCmd[4] = (UINT8)optAddr;
            }
 
        /* controller might impose a limit on the amount of data */
 
        if (windowSize != 0)
            {
            bytesToRead = min (dataLen, windowSize - readCmdLen);
            }
        else
            {
            bytesToRead = dataLen;
            }
 
        if (spiFlashRead (pDrvCtrl, pCmd, readCmdLen, dataBuf,
                bytesToRead, pDrvCtrl->readNbits, pDrvCtrl->readDummy * pDrvCtrl->hd.dataLines,
                pDrvCtrl->addrWidth) != OK)
            {
            rc = ERROR;
            break;
            }
 
        startAddr  += bytesToRead;
        dataBuf    += bytesToRead;
        dataLen    -= bytesToRead;
        } while (dataLen);
 
    return rc;
    }

扇区擦操作



STATUS spiFlashSectorErase
    (
    SPI_FLASH_DEV * pDrvCtrl,
    UINT32          startAddr,
    UINT32          blks
    )
    {
    UINT8           status  = 0;
    STATUS          rc      = OK;
    UINT32          optAddr;
    UINT32          addrDiv;
    UINT8 *         pCmd;
    UINT32          eraseCmdLen;
    UINT8           eraseCmdOpcode;
    SPI_TRANSFER    transInfo;
    UINT32          i;
    UINT8           value = 0;
    
    DEBUG_MSG (SPI_FLASH_DBG_INFO, "spiFlashSectorErase: Erase %d unit from %d "
               "unit\n", blks, startAddr / SectorSize (pDrvCtrl));
 
    /* whole chip erase */
 
    if ((startAddr == 0) && (blks == SectorNum (pDrvCtrl)))
        {
        return spiFlashChipErase (pDrvCtrl);
        }
 
    if ((pDrvCtrl->hd.mode & SPI_PARALLEL) != 0)
        {
        addrDiv = pDrvCtrl->hd.chipNum;
        }
    else
        {
        addrDiv = 1;
        }
 
 
      //驱动中增加对大于32Mflash的支持,因为zynq不支持大于16Mfalsh的命令,如果大于16M,需要使用bank寄存器,朱红博于2020年10月19日
  	if(pDrvCtrl->addrMode == 1)
  	{
  		value |= startAddr >>24;
  		spiFlashWriteReg(pDrvCtrl,SPI_WBNK_CMD,value);
  	}
    eraseCmdLen     = pDrvCtrl->addrWidth + 1;
    eraseCmdOpcode  = SPI_SE_CMD;
 
    pCmd = pDrvCtrl->pWrBuf;
    bzero ((char *)pCmd, eraseCmdLen);
    bzero ((char *)&transInfo, sizeof (SPI_TRANSFER));
 
    /* wait until the device is not busy */
 
    if (spiFlashWaitReady (pDrvCtrl) != OK)
        {
        return ERROR;
        }
 
    /* clear the Write Enable Latch (WEL) bit to 0 */
 
    if (spiFlashReadSR(pDrvCtrl, &status) == OK)
        {
        if ((status & SR_WEL) != 0)
            {
            status &= (UINT8)(~(SR_WEL));
            if (spiFlashWriteReg (pDrvCtrl, SPI_WRSR_CMD, status) != OK)
                {
                return ERROR;
                }
            }
        }
    else
        {
        return ERROR;
        }
 
    for (i = 0; i < blks; i++)
        {
        optAddr = startAddr / addrDiv;
 
        /* set erase command */
        pCmd[0] = eraseCmdOpcode;
        pCmd[1] = (UINT8) (optAddr >> (pDrvCtrl->addrWidth * 8 - 8));
        pCmd[2] = (UINT8) (optAddr >> (pDrvCtrl->addrWidth * 8 - 16));
        pCmd[3] = (UINT8) (optAddr >> (pDrvCtrl->addrWidth * 8 - 24));
        
        if (pDrvCtrl->addrWidth == 4)
            {
            pCmd[4] = (UINT8)(optAddr);
            }
 
        /* write enable */
 
        if (spiFlashWriteEn (pDrvCtrl, SPI_WREN_CMD) != OK)
            {
            rc = ERROR;
            break;
            }
        transInfo.txBuf = pCmd;
        transInfo.txLen = eraseCmdLen;
        transInfo.base  = pDrvCtrl->base;
        transInfo.addrLen  = pDrvCtrl->addrWidth;
 
        if (vxbSpiDevXfer (pDrvCtrl->pDev, &(pDrvCtrl->hd), &transInfo) != OK)
            {
            rc = ERROR;
            break;
            }
 
        /* wait until the device is not busy */
        if (spiFlashWaitReady (pDrvCtrl) != OK)
            {
            rc = ERROR;
            break;
            }
 
        if (spiFlashStatusCheck (pDrvCtrl) != OK)
            {
            rc = ERROR;
            break;
            }
 
        startAddr += SectorSize (pDrvCtrl);
        }
 
    return rc;
    }
    

经测试,S25FL256S芯片读写正常,测试代码如下:



/****************************************************************************************
name:	       s25Fl256sWriteDataTest
Author:
Version:       v1.0
Description:   spiflash 写状态寄存器1测试
*****************************************************************************************
Modification log:
Date         Who     Revision      Comments
----------  ------  --------  ---------------------------------------
2020/05/20   ZHB       1.00        Create
*****************************************************************************************/
 
void s25WriteDataTest(int secAddr)
{
	UINT8   cmd = SPI_RDSR_CMD;
	UINT8   jedecId[2];
	VXB_DEV_ID  pFlash;
	SPI_FLASH_DEV *pDrvCtrl;
	UINT8   tmpBuf[256];
	/*页大小为256个字节,按页大小写测试*/
	int i = 0;
	
	for(i = 0; i < sizeof(tmpBuf);i++)
		tmpBuf[i] = i;
	memset(jedecId, 0,sizeof(jedecId));
	pFlash = findVxbDevNodetId(&vxbRoot,SPI_FLASH_NAME);
	pDrvCtrl = (SPI_FLASH_DEV *)vxbDevSoftcGet(pFlash);
	if(pDrvCtrl)
	{
		spiFlashDataWrite(pDrvCtrl,secAddr,sizeof(tmpBuf),tmpBuf);	
	}
}
/****************************************************************************************
name:	       s25Fl256sSectorEraseTest
Author:
Version:       v1.0
Description:   spiflash 扇区擦除测试
*****************************************************************************************
Modification log:
Date         Who     Revision      Comments
----------  ------  --------  ---------------------------------------
2020/05/20   ZHB       1.00        Create
*****************************************************************************************/
void s25SectorEraseTest(int startAddr,int numOfErasableBlocks)
{
	VXB_DEV_ID	pFlash;
	SPI_FLASH_DEV *pDrvCtrl;
	
	pFlash = findVxbDevNodetId(&vxbRoot,SPI_FLASH_NAME);
	pDrvCtrl = (SPI_FLASH_DEV *)vxbDevSoftcGet(pFlash);
	if(pDrvCtrl)
	{
		if(spiFlashSectorErase(pDrvCtrl,startAddr,numOfErasableBlocks)==ERROR)
			printf("sector  erase failed.......\n");
	}
}
/****************************************************************************************
name:	       s25Fl256sReadDataTest
Author:
Version:       v1.0
Description:   spiflash 读数据测试
*****************************************************************************************
Modification log:
Date         Who     Revision      Comments
----------  ------  --------  ---------------------------------------
2020/05/20   ZHB       1.00        Create
*****************************************************************************************/
 
void s25ReadDataTest(int secAddr)
{
	UINT8   cmd = SPI_RDSR_CMD;
	VXB_DEV_ID  pFlash;
	SPI_FLASH_DEV *pDrvCtrl;
	UINT8   tmpBuf[256];
	int i = 0;
	
	memset(tmpBuf, 0,sizeof(tmpBuf));
	pFlash = findVxbDevNodetId(&vxbRoot,SPI_FLASH_NAME);
	pDrvCtrl = (SPI_FLASH_DEV *)vxbDevSoftcGet(pFlash);
	if(pDrvCtrl)
	{
		spiFlashDataRead(pDrvCtrl,secAddr,sizeof(tmpBuf),tmpBuf);
		for(i = 0; i < sizeof(tmpBuf);i++)
		{
			if(i%16 == 0)
				printf("\n");
			printf("%02x ",tmpBuf[i]);
		}
	}
}

最后,进行文件系统挂接



STATUS  usrResetTffsDisk(int driverNo)
{
	STATUS retVal;
	char diskName[32];
	
	memset(diskName,0,sizeof(diskName));
	sprintf(diskName,"/tffs%d",driverNo);
	
	retVal = sysTffsFormat(driverNo);
	if(retVal != OK)
		return retVal;
	retVal = usrTffsConfig(driverNo,0,diskName);
	if(retVal != OK)
		return retVal;
	dosFsVolFormat(diskName,DOS_OPT_BLANK,0);
	return OK;
	
}

至此,文件系统挂接完成,shell输入devs,查看设备,显示存在设备/tffs0,挂接成功。

下一篇
« Prev Post
上一篇
Next Post »