OpenCores
URL https://opencores.org/ocsvn/usb_fpga_2_14/usb_fpga_2_14/trunk

Subversion Repositories usb_fpga_2_14

[/] [usb_fpga_2_14/] [trunk/] [fx3/] [ztex-flash.c] - Rev 2

Compare with Previous | Blame | View Log

/*%
   ZTEX Firmware Kit for EZ-USB FX3 Microcontrollers
   Copyright (C) 2009-2017 ZTEX GmbH.
   http://www.ztex.de
 
   This Source Code Form is subject to the terms of the Mozilla Public
   License, v. 2.0. If a copy of the MPL was not distributed with this file,
   You can obtain one at http://mozilla.org/MPL/2.0/.
 
   Alternatively, the contents of this file may be used under the terms
   of the GNU General Public License Version 3, as described below:
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 3 as
   published by the Free Software Foundation.
 
   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program; if not, see http://www.gnu.org/licenses/.
%*/
/*
    SPI flash support.
*/
 
#include "cyu3system.h"
#include "cyu3os.h"
#include "cyu3usb.h"
#include "cyu3spi.h"
#include "cyu3dma.h"
#include "cyu3error.h"
 
#define ZTEX_SPI_DMA
#define ZTEX_SPI_PAGE_SIZE 256
 
#ifdef ZTEX_SPI_DMA
CyU3PDmaChannel ztex_spi_wr_handle, ztex_spi_rd_handle;
#endif
 
#define FLASH_EC_TIMEOUT 2
#define FLASH_EC_BUSY 3
#define FLASH_EC_PENDING 4
#define FLASH_EC_NOTSUPPORTED 7
#define FLASH_EC_RUNTIME 8
 
struct __attribute__((__packed__)) ztex_flash_t {
    uint8_t enabled;			// 0	1: enabled, 0:disabled
    uint16_t sector_size; 		// 1    sector size <sector size> = MSB==0 : flash_sector_size and 0x7fff ? 1<<(flash_sector_size and 0x7fff)
    uint32_t sectors;			// 3	number of sectors
    uint8_t ec; 	       		// 7	error code
 
    uint8_t vendor;			// 0
    uint8_t device;			// 1
    uint8_t memtype;			// 2
    uint8_t erase_cmd;			// 3
    uint8_t last_cmd;			// 4
    uint8_t buf[4];			// 5
    uint8_t cs;
 
    uint32_t ep0_read_addr;
    uint8_t need_pp;
    uint32_t write_addr;
};
 
struct ztex_flash_t ztex_flash;
 
void ztex_flash_select () {
    CyU3PSpiSetSsnLine(CyFalse);
    ztex_flash.cs = 1;
}
 
void ztex_flash_deselect () {
    CyU3PSpiSetSsnLine(CyTrue);
    ztex_flash.cs = 0;
}
 
uint8_t ztex_flash_wait() {
    uint16_t i;
    CyU3PReturnStatus_t status = CY_U3P_SUCCESS;
 
    ztex_flash.ec = ztex_flash.cs ? FLASH_EC_PENDING : ztex_flash.enabled ? 0 : FLASH_EC_NOTSUPPORTED;
    if ( ztex_flash.ec ) return ztex_flash.ec;
 
    ztex_flash.buf[0]=ztex_flash.last_cmd=5;
    ztex_flash_select();
    ZTEX_REC(status = CyU3PSpiTransmitWords (ztex_flash.buf, 1));
    if (status != CY_U3P_SUCCESS) goto errreturn;
 
    for ( i=0; i<11000; i++) {  // wait up to 11s
	ZTEX_REC(status = CyU3PSpiReceiveWords (ztex_flash.buf, 4));
	if (status != CY_U3P_SUCCESS) goto errreturn;
	if ( !(ztex_flash.buf[0] & 1) ) break;
	CyU3PThreadSleep(1);	// 1ms
    }
    ztex_flash_deselect();
    ztex_flash.ec = ztex_flash.buf[0] & 1 ? FLASH_EC_TIMEOUT : 0;
    return ztex_flash.ec;
 
errreturn:
    ztex_flash.ec = FLASH_EC_RUNTIME;
    ztex_flash_deselect();
    return ztex_flash.ec;
}
 
uint8_t ztex_flash_read (uint8_t* buf, uint32_t addr, uint32_t size) {
    CyU3PReturnStatus_t status = CY_U3P_SUCCESS;
 
    if ( ztex_flash_wait() ) return ztex_flash.ec;
 
    ztex_flash.buf[0]=ztex_flash.last_cmd=0xb;
    ztex_flash.buf[1]=addr>>16;
    ztex_flash.buf[2]=addr>>8;
    ztex_flash.buf[3]=addr;
    ztex_flash_select();
    ZTEX_REC(status = CyU3PSpiTransmitWords (ztex_flash.buf, 5));
    if (status != CY_U3P_SUCCESS) goto errreturn;
 
#ifdef ZTEX_SPI_DMA
    CyU3PDmaBuffer_t buf_p;
    uint32_t s2;
 
    buf_p.buffer = buf;
    buf_p.status = 0;
 
    while ( size>0 ) {
	s2 = size;
	if ( s2 > ZTEX_SPI_PAGE_SIZE ) s2 = ZTEX_SPI_PAGE_SIZE;
 
        buf_p.size  = s2;
	buf_p.count = s2;
 
	CyU3PSpiSetBlockXfer (0, s2);
	ZTEX_REC(status = CyU3PDmaChannelSetupRecvBuffer (&ztex_spi_rd_handle, &buf_p));
	if (status != CY_U3P_SUCCESS) goto errreturn;
 
	ZTEX_REC(status = CyU3PDmaChannelWaitForCompletion (&ztex_spi_rd_handle, 500));
	if (status != CY_U3P_SUCCESS) {
    	    CyU3PDmaChannelReset (&ztex_spi_rd_handle);
    	    CyU3PSpiDisableBlockXfer (CyFalse, CyTrue);
	    goto errreturn;
	}
        CyU3PSpiDisableBlockXfer (CyFalse, CyTrue);
 
        size-=s2;
        buf_p.buffer+=s2;
//        CyU3PThreadSleep(1);
    }
#else
    ZTEX_REC(status = CyU3PSpiReceiveWords(buf, size) );
    if (status != CY_U3P_SUCCESS) goto errreturn;
#endif
    ztex_flash_deselect();
    return 0;
 
errreturn:
    ztex_flash.ec = FLASH_EC_RUNTIME;
    ztex_flash_deselect();
    return ztex_flash.ec;
}
 
 
// sends pp command
uint8_t ztex_flash_pp () {
    CyU3PReturnStatus_t status = CY_U3P_SUCCESS;
 
    ztex_flash_deselect();			// finish previous write cmd
    ztex_flash.need_pp = 0;
    if ( ztex_flash_wait() ) return ztex_flash.ec;
 
//    ZTEX_LOG("flash_pp: %d",ztex_flash.write_addr);
 
    ztex_flash.buf[0]=ztex_flash.last_cmd=0x06;
    ztex_flash_select();
    ZTEX_REC(status = CyU3PSpiTransmitWords (ztex_flash.buf, 1));
    if (status != CY_U3P_SUCCESS) goto errreturn;
    ztex_flash_deselect();
 
    ztex_flash.buf[0]=ztex_flash.last_cmd=0x02;
    ztex_flash.buf[1]=ztex_flash.write_addr >> 16;
    ztex_flash.buf[2]=ztex_flash.write_addr >> 8;
    ztex_flash.buf[3]=ztex_flash.write_addr;
    ztex_flash_select();
    ZTEX_REC(status = CyU3PSpiTransmitWords (ztex_flash.buf, 4));
    if (status != CY_U3P_SUCCESS) goto errreturn;
 
    return 0;
 
errreturn:
    ztex_flash.ec = FLASH_EC_RUNTIME;
    ztex_flash_deselect();
    return ztex_flash.ec;
}
 
// has to be called at begin of a 64K write sequence
uint8_t ztex_flash_write_start (uint16_t sector) {
    CyU3PReturnStatus_t status = CY_U3P_SUCCESS;
 
    if ( ztex_flash_wait() ) return ztex_flash.ec;
 
    // write enable command
    ztex_flash.buf[0]=ztex_flash.last_cmd=0x06;
    ztex_flash_select();
    ZTEX_REC(status = CyU3PSpiTransmitWords (ztex_flash.buf, 1));
    if (status != CY_U3P_SUCCESS) goto errreturn;
    ztex_flash_deselect();
 
    // erase  command
//    ztex_log("flash_erase");
    ztex_flash.buf[0]=ztex_flash.last_cmd=ztex_flash.erase_cmd;
    ztex_flash.buf[1]=sector;
    ztex_flash.buf[2]=0;
    ztex_flash.buf[3]=0;
    ztex_flash_select();
    ZTEX_REC(status = CyU3PSpiTransmitWords (ztex_flash.buf, 4));
    if (status != CY_U3P_SUCCESS) goto errreturn;
    ztex_flash_deselect();
 
    ztex_flash.need_pp = 1;
    ztex_flash.write_addr = sector << 16;
    return 0;
 
errreturn:
    ztex_flash.ec = FLASH_EC_RUNTIME;
    ztex_flash_deselect();
    return ztex_flash.ec;
}
 
// is called between ztex_write_start and ztex_write_finish
uint8_t ztex_flash_write (uint8_t* buf, uint32_t size) {
    uint32_t s2;
    CyU3PReturnStatus_t status = CY_U3P_SUCCESS;
 
    if ( ztex_flash.need_pp && ztex_flash_pp() ) return ztex_flash.ec;
 
    while (size>0) {
	s2 = 256-(ztex_flash.write_addr & 255);
	if (s2>size) s2=size;
#ifdef ZTEX_SPI_DMA
	CyU3PDmaBuffer_t buf_p;
 
        buf_p.size  = s2;
	buf_p.count = s2;
	buf_p.buffer = buf;
	buf_p.status = 0;
 
	CyU3PSpiSetBlockXfer (s2, 0);
	ZTEX_REC( status = CyU3PDmaChannelSetupSendBuffer (&ztex_spi_wr_handle, &buf_p) );
 
	ZTEX_REC(status = CyU3PDmaChannelWaitForCompletion (&ztex_spi_wr_handle, 500));
	if (status != CY_U3P_SUCCESS) {
    	    CyU3PSpiDisableBlockXfer (CyTrue, CyFalse);
	    goto errreturn;
	}
        CyU3PSpiDisableBlockXfer (CyTrue, CyFalse);
#else	
	ZTEX_REC(status = CyU3PSpiTransmitWords (buf, s2));
	if (status != CY_U3P_SUCCESS) goto errreturn;
#endif	
	size-=s2;
	buf+=s2;
	ztex_flash.write_addr+=s2;
	if ( size==0 ) {
	    ztex_flash_deselect();	// finish pp
	    ztex_flash.need_pp = 1;	// do not wait
	}
	else {
	    if ( ztex_flash_pp() ) return ztex_flash.ec; // finish pp + wait
	}
    }
 
    return 0;
 
errreturn:
    ztex_flash.ec = FLASH_EC_RUNTIME;
    ztex_flash_deselect();
    return ztex_flash.ec;
}
 
// is called after ztex_flash_write
void ztex_flash_write_finish () {
    ztex_flash_deselect();
}
 
 
// VR 0x40
uint8_t vr_flash_info(uint16_t value, uint16_t index, uint16_t length ) {
    if ( ztex_flash.ec==0 && ztex_flash.cs ) ztex_flash.ec = FLASH_EC_PENDING;
    CyU3PMemCopy( ztex_ep0buf, (uint8_t*)&ztex_flash, 8);
    ZTEX_REC_RET( CyU3PUsbSendEP0Data( 8, ztex_ep0buf ) );
    return 0;
}
 
// VR 0x41
uint8_t vr_flash_read(uint16_t value, uint16_t index, uint16_t length ) {
    index = index >> 8;
    if ( index==0 ) {
//        ZTEX_LOG("FR: %d, %d", index, value);
	ztex_flash.ep0_read_addr = value << 16;
    }
    if ( ztex_flash_read(ztex_ep0buf,ztex_flash.ep0_read_addr,length) ) return 255;
    ztex_flash.ep0_read_addr += length;
    ZTEX_REC_RET ( CyU3PUsbSendEP0Data( length, ztex_ep0buf ) );
    return 0;
}
 
// VC 0x42
uint8_t vc_flash_write(uint16_t value, uint16_t index, uint16_t length ) {
    index = index >> 8;
//    ZTEX_LOG("FW: %d, %d", index, value);
    if ( index==0 && ztex_flash_write_start(value) ) return 255;
 
    ZTEX_REC_RET ( CyU3PUsbGetEP0Data (length, ztex_ep0buf, NULL) );
    if ( ztex_flash_write(ztex_ep0buf,length) ) return 255;
 
    if ( index==3 ) ztex_flash_write_finish();
    return 0;
}
 
// VR 0x43
uint8_t vr_flash_info2(uint16_t value, uint16_t index, uint16_t length ) {
    CyU3PMemCopy( ztex_ep0buf, (uint8_t*)&ztex_flash.ec, 10);
    return CyU3PUsbSendEP0Data( 10, ztex_ep0buf );
}
 
void ztex_flash_init () {
    CyU3PReturnStatus_t status = CY_U3P_SUCCESS;
    CyU3PSpiConfig_t spiConfig;
 
    ztex_flash.enabled = 0;
    ztex_flash.ec = FLASH_EC_NOTSUPPORTED;
    ztex_flash.cs = 0;
 
    ztex_register_vendor_req(0x40, vr_flash_info);
    ztex_register_vendor_req(0x41, vr_flash_read);
    ztex_register_vendor_cmd(0x42, vc_flash_write);
    ztex_register_vendor_req(0x43, vr_flash_info2);
 
    // Start the SPI module and configure the master 
    ZTEX_REC(status = CyU3PSpiInit());
    if (status != CY_U3P_SUCCESS) goto errreturn;
 
    /* Start the SPI master block. Run the SPI clock at 8MHz
       and configure the word length to 8 bits. Also configure
       the slave select using FW */
    CyU3PMemSet ((uint8_t *)&spiConfig, 0, sizeof(spiConfig));
    spiConfig.isLsbFirst = CyFalse;
    spiConfig.cpol       = CyTrue;
    spiConfig.ssnPol     = CyFalse;
    spiConfig.cpha       = CyTrue;
    spiConfig.leadTime   = CY_U3P_SPI_SSN_LAG_LEAD_HALF_CLK;
    spiConfig.lagTime    = CY_U3P_SPI_SSN_LAG_LEAD_HALF_CLK;
    spiConfig.ssnCtrl    = CY_U3P_SPI_SSN_CTRL_FW;
    spiConfig.clock      = 33000000;
//    spiConfig.clock      = 10000000;
    spiConfig.wordLen    = 8;
    ZTEX_REC(status =  CyU3PSpiSetConfig (&spiConfig, NULL) );
    if (status != CY_U3P_SUCCESS) goto errreturn;
 
#ifdef ZTEX_SPI_DMA
    CyU3PDmaChannelConfig_t dmaConfig;
 
    // DMA channels for SPI write 
    CyU3PMemSet ((uint8_t *)&dmaConfig, 0, sizeof(dmaConfig));
    dmaConfig.size           = ZTEX_SPI_PAGE_SIZE;
    // No buffers need to be allocated as this channel will be used only in override mode. */
    dmaConfig.count          = 0;
    dmaConfig.prodAvailCount = 0;
    dmaConfig.dmaMode        = CY_U3P_DMA_MODE_BYTE;
    dmaConfig.prodHeader     = 0;
    dmaConfig.prodFooter     = 0;
    dmaConfig.consHeader     = 0;
    dmaConfig.notification   = 0;
    dmaConfig.cb             = NULL;
 
    /* Channel to write to SPI flash. */
    dmaConfig.prodSckId = CY_U3P_CPU_SOCKET_PROD;
    dmaConfig.consSckId = CY_U3P_LPP_SOCKET_SPI_CONS;
    ZTEX_REC(status =  CyU3PDmaChannelCreate (&ztex_spi_wr_handle, CY_U3P_DMA_TYPE_MANUAL_OUT, &dmaConfig) );
    if (status != CY_U3P_SUCCESS) goto errreturn;
 
    /* Channel to read from SPI flash. */
    dmaConfig.prodSckId = CY_U3P_LPP_SOCKET_SPI_PROD;
    dmaConfig.consSckId = CY_U3P_CPU_SOCKET_CONS;
    ZTEX_REC(status =  CyU3PDmaChannelCreate (&ztex_spi_rd_handle, CY_U3P_DMA_TYPE_MANUAL_IN, &dmaConfig) );
    if (status != CY_U3P_SUCCESS) goto errreturn;
#endif
 
    // CMD 0x90 may not be supported by all devices
    ztex_flash.buf[0]=0x90;
    ztex_flash.buf[1]=0;
    ztex_flash.buf[2]=0;
    ztex_flash.buf[3]=0;
    ztex_flash_select();
    ZTEX_REC(status = CyU3PSpiTransmitWords (ztex_flash.buf, 4));
    if (status != CY_U3P_SUCCESS) goto errreturn;
    ztex_flash.device = CyU3PSpiReceiveWords (ztex_flash.buf, 2) == CY_U3P_SUCCESS ? ztex_flash.buf[1] : 127;
    ztex_flash_deselect();
 
    // CMD 0x9F: JEDEC ID
    ztex_flash.buf[0]=ztex_flash.last_cmd=0x9F;
    ztex_flash_select();
    ZTEX_REC(status = CyU3PSpiTransmitWords (ztex_flash.buf, 1));
    if (status != CY_U3P_SUCCESS) goto errreturn;
    ZTEX_REC(status = CyU3PSpiReceiveWords (ztex_flash.buf, 3));
    if (status != CY_U3P_SUCCESS) goto errreturn;
    ztex_flash_deselect();
 
    if ( ztex_flash.buf[2]<16 || ztex_flash.buf[2]>24 ) {
	ztex_log("Error: Invalid Flash size");
	return;
    }
    ztex_flash.vendor = ztex_flash.buf[0];
    ztex_flash.memtype = ztex_flash.buf[1];
    ztex_flash.sector_size = 0x8010;  		// only 64 KByte sectors are supported because erasing 4 KByte is extremely slow
    ztex_flash.sectors = 1 << (ztex_flash.buf[2]-16);
    ztex_flash.erase_cmd = 0xd8;
    ztex_flash.ec = 0;
    ztex_flash.enabled = 1;
 
    ZTEX_LOG("Info: Found %d MBit SPI Flash",ztex_flash.sectors>>1);
    return;
 
errreturn:
    ztex_flash.ec = FLASH_EC_RUNTIME;
    ztex_flash_deselect();
    ztex_log("Error: No SPI Flash found");
}
 
void ztex_usb_stop_flash() {
#ifdef ZTEX_SPI_DMA
    CyU3PDmaChannelReset (&ztex_spi_wr_handle);
    CyU3PDmaChannelReset (&ztex_spi_rd_handle);
#endif    
}
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.